From 1d1a4564d8e0a05d56a95df08c6c411bea704bec Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 20 Sep 2012 20:29:13 -0700 Subject: ALSA: hda - Add new DSP loader callback routines Pass DMA buffer pointers in calls to setup_bdle(). Add DSP loader callback routines to controller. Add new DSP loader switch to Kconfig to turn off DSP firmware. Signed-off-by: Ian Minett Signed-off-by: Takashi Iwai --- sound/pci/hda/Kconfig | 8 ++++ sound/pci/hda/hda_codec.h | 45 ++++++++++++++++++ sound/pci/hda/hda_intel.c | 116 +++++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 163 insertions(+), 6 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig index 7105c3de1bca..ba1dbd8e00a0 100644 --- a/sound/pci/hda/Kconfig +++ b/sound/pci/hda/Kconfig @@ -236,4 +236,12 @@ config SND_HDA_POWER_SAVE_DEFAULT The default time-out value in seconds for HD-audio automatic power-save mode. 0 means to disable the power-save mode. +config SND_HDA_DSP_LOADER + bool "Enable DSP firmware loader" + depends on FW_LOADER + default y + help + Say Y here to enable the DSP firmware loader, used by certain + codecs (e.g. CA0132) to transfer their DSP binaries to the hardware. + endif diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index 507fe8a917b6..c218bf41261a 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -618,6 +618,17 @@ struct hda_bus_ops { /* notify power-up/down from codec to controller */ void (*pm_notify)(struct hda_bus *bus, bool power_up); #endif +#ifdef CONFIG_SND_HDA_DSP_LOADER + /* prepare DSP transfer */ + int (*load_dsp_prepare)(struct hda_bus *bus, unsigned int format, + unsigned int byte_size, + struct snd_dma_buffer *bufp); + /* start/stop DSP transfer */ + void (*load_dsp_trigger)(struct hda_bus *bus, bool start); + /* clean up DSP transfer */ + void (*load_dsp_cleanup)(struct hda_bus *bus, + struct snd_dma_buffer *dmab); +#endif }; /* template to pass to the bus constructor */ @@ -1129,6 +1140,40 @@ static inline void snd_hda_power_sync(struct hda_codec *codec) int snd_hda_load_patch(struct hda_bus *bus, size_t size, const void *buf); #endif +#ifdef CONFIG_SND_HDA_DSP_LOADER +static inline int +snd_hda_codec_load_dsp_prepare(struct hda_codec *codec, unsigned int format, + unsigned int size, + struct snd_dma_buffer *bufp) +{ + return codec->bus->ops.load_dsp_prepare(codec->bus, format, size, bufp); +} +static inline void +snd_hda_codec_load_dsp_trigger(struct hda_codec *codec, bool start) +{ + return codec->bus->ops.load_dsp_trigger(codec->bus, start); +} +static inline void +snd_hda_codec_load_dsp_cleanup(struct hda_codec *codec, + struct snd_dma_buffer *dmab) +{ + return codec->bus->ops.load_dsp_cleanup(codec->bus, dmab); +} +#else +static inline int +snd_hda_codec_load_dsp_prepare(struct hda_codec *codec, unsigned int format, + unsigned int size, + struct snd_dma_buffer *bufp) +{ + return 0; +} +static inline void +snd_hda_codec_load_dsp_trigger(struct hda_codec *codec, bool start) {} +static inline void +snd_hda_codec_load_dsp_cleanup(struct hda_codec *codec, + struct snd_dma_buffer *dmab) {} +#endif + /* * Codec modularization */ diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index cd2dbaf1be78..99e61b985d29 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -1038,6 +1038,15 @@ static unsigned int azx_get_response(struct hda_bus *bus, static void azx_power_notify(struct hda_bus *bus, bool power_up); #endif +#ifdef CONFIG_SND_HDA_DSP_LOADER +static int azx_load_dsp_prepare(struct hda_bus *bus, unsigned int format, + unsigned int byte_size, + struct snd_dma_buffer *bufp); +static void azx_load_dsp_trigger(struct hda_bus *bus, bool start); +static void azx_load_dsp_cleanup(struct hda_bus *bus, + struct snd_dma_buffer *dmab); +#endif + /* reset codec link */ static int azx_reset(struct azx *chip, int full_reset) { @@ -1359,7 +1368,7 @@ static irqreturn_t azx_interrupt(int irq, void *dev_id) * set up a BDL entry */ static int setup_bdle(struct azx *chip, - struct snd_pcm_substream *substream, + struct snd_dma_buffer *dmab, struct azx_dev *azx_dev, u32 **bdlp, int ofs, int size, int with_ioc) { @@ -1372,12 +1381,12 @@ static int setup_bdle(struct azx *chip, if (azx_dev->frags >= AZX_MAX_BDL_ENTRIES) return -EINVAL; - addr = snd_pcm_sgbuf_get_addr(substream, ofs); + addr = snd_sgbuf_get_addr(dmab, ofs); /* program the address field of the BDL entry */ bdl[0] = cpu_to_le32((u32)addr); bdl[1] = cpu_to_le32(upper_32_bits(addr)); /* program the size field of the BDL entry */ - chunk = snd_pcm_sgbuf_get_chunk_size(substream, ofs, size); + chunk = snd_sgbuf_get_chunk_size(dmab, ofs, size); /* one BDLE cannot cross 4K boundary on CTHDA chips */ if (chip->driver_caps & AZX_DCAPS_4K_BDLE_BOUNDARY) { u32 remain = 0x1000 - (ofs & 0xfff); @@ -1436,7 +1445,8 @@ static int azx_setup_periods(struct azx *chip, bdl_pos_adj[chip->dev_index]); pos_adj = 0; } else { - ofs = setup_bdle(chip, substream, azx_dev, + ofs = setup_bdle(chip, snd_pcm_get_dma_buf(substream), + azx_dev, &bdl, ofs, pos_adj, true); if (ofs < 0) goto error; @@ -1445,10 +1455,12 @@ static int azx_setup_periods(struct azx *chip, pos_adj = 0; for (i = 0; i < periods; i++) { if (i == periods - 1 && pos_adj) - ofs = setup_bdle(chip, substream, azx_dev, &bdl, ofs, + ofs = setup_bdle(chip, snd_pcm_get_dma_buf(substream), + azx_dev, &bdl, ofs, period_bytes - pos_adj, 0); else - ofs = setup_bdle(chip, substream, azx_dev, &bdl, ofs, + ofs = setup_bdle(chip, snd_pcm_get_dma_buf(substream), + azx_dev, &bdl, ofs, period_bytes, !azx_dev->no_period_wakeup); if (ofs < 0) @@ -1610,6 +1622,11 @@ static int DELAYED_INIT_MARK azx_codec_create(struct azx *chip, const char *mode bus_temp.power_save = &power_save; bus_temp.ops.pm_notify = azx_power_notify; #endif +#ifdef CONFIG_SND_HDA_DSP_LOADER + bus_temp.ops.load_dsp_prepare = azx_load_dsp_prepare; + bus_temp.ops.load_dsp_trigger = azx_load_dsp_trigger; + bus_temp.ops.load_dsp_cleanup = azx_load_dsp_cleanup; +#endif err = snd_hda_bus_new(chip->card, &bus_temp, &chip->bus); if (err < 0) @@ -2427,6 +2444,93 @@ static void azx_stop_chip(struct azx *chip) chip->initialized = 0; } +#ifdef CONFIG_SND_HDA_DSP_LOADER +/* + * DSP loading code (e.g. for CA0132) + */ + +/* use the first stream for loading DSP */ +static struct azx_dev * +azx_get_dsp_loader_dev(struct azx *chip) +{ + return &chip->azx_dev[chip->playback_index_offset]; +} + +static int azx_load_dsp_prepare(struct hda_bus *bus, unsigned int format, + unsigned int byte_size, + struct snd_dma_buffer *bufp) +{ + u32 *bdl; + struct azx *chip = bus->private_data; + struct azx_dev *azx_dev; + int err; + + if (snd_hda_lock_devices(bus)) + return -EBUSY; + + err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV_SG, + snd_dma_pci_data(chip->pci), + byte_size, bufp); + if (err < 0) + goto error; + + azx_dev = azx_get_dsp_loader_dev(chip); + azx_dev->bufsize = byte_size; + azx_dev->period_bytes = byte_size; + azx_dev->format_val = format; + + azx_stream_reset(chip, azx_dev); + + /* reset BDL address */ + azx_sd_writel(azx_dev, SD_BDLPL, 0); + azx_sd_writel(azx_dev, SD_BDLPU, 0); + + azx_dev->frags = 0; + bdl = (u32 *)azx_dev->bdl.area; + err = setup_bdle(chip, bufp, azx_dev, &bdl, 0, byte_size, 0); + if (err < 0) + goto error; + + azx_setup_controller(chip, azx_dev); + return azx_dev->stream_tag; + + error: + snd_hda_unlock_devices(bus); + return err; +} + +static void azx_load_dsp_trigger(struct hda_bus *bus, bool start) +{ + struct azx *chip = bus->private_data; + struct azx_dev *azx_dev = azx_get_dsp_loader_dev(chip); + + if (start) + azx_stream_start(chip, azx_dev); + else + azx_stream_stop(chip, azx_dev); + azx_dev->running = start; +} + +static void azx_load_dsp_cleanup(struct hda_bus *bus, + struct snd_dma_buffer *dmab) +{ + struct azx *chip = bus->private_data; + struct azx_dev *azx_dev = azx_get_dsp_loader_dev(chip); + + /* reset BDL address */ + azx_sd_writel(azx_dev, SD_BDLPL, 0); + azx_sd_writel(azx_dev, SD_BDLPU, 0); + azx_sd_writel(azx_dev, SD_CTL, 0); + azx_dev->bufsize = 0; + azx_dev->period_bytes = 0; + azx_dev->format_val = 0; + + snd_dma_free_pages(dmab); + + snd_hda_unlock_devices(bus); +} +#endif /* CONFIG_SND_HDA_DSP_LOADER */ + #ifdef CONFIG_PM /* power-up/down the controller */ static void azx_power_notify(struct hda_bus *bus, bool power_up) -- cgit v1.2.3 From bcd109c08654975371dfa347d803e34859cc2691 Mon Sep 17 00:00:00 2001 From: Ian Minett Date: Thu, 20 Sep 2012 20:29:14 -0700 Subject: ALSA: hda - Add CA0132 register definitions file Signed-off-by: Ian Minett Signed-off-by: Takashi Iwai --- sound/pci/hda/ca0132_regs.h | 409 +++++++++++++++++++++++++++++++++++++++++++ sound/pci/hda/patch_ca0132.c | 2 + 2 files changed, 411 insertions(+) create mode 100644 sound/pci/hda/ca0132_regs.h (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/ca0132_regs.h b/sound/pci/hda/ca0132_regs.h new file mode 100644 index 000000000000..831ca9c47992 --- /dev/null +++ b/sound/pci/hda/ca0132_regs.h @@ -0,0 +1,409 @@ +/* + * HD audio interface patch for Creative CA0132 chip. + * CA0132 registers defines. + * + * Copyright (c) 2011, Creative Technology Ltd. + * + * This driver is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This driver 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 __CA0132_REGS_H +#define __CA0312_REGS_H + +#define DSP_CHIP_OFFSET 0x100000 +#define DSP_DBGCNTL_MODULE_OFFSET 0xE30 +#define DSP_DBGCNTL_INST_OFFSET \ + (DSP_CHIP_OFFSET + DSP_DBGCNTL_MODULE_OFFSET) + +#define DSP_DBGCNTL_EXEC_LOBIT 0x0 +#define DSP_DBGCNTL_EXEC_HIBIT 0x3 +#define DSP_DBGCNTL_EXEC_MASK 0xF + +#define DSP_DBGCNTL_SS_LOBIT 0x4 +#define DSP_DBGCNTL_SS_HIBIT 0x7 +#define DSP_DBGCNTL_SS_MASK 0xF0 + +#define DSP_DBGCNTL_STATE_LOBIT 0xA +#define DSP_DBGCNTL_STATE_HIBIT 0xD +#define DSP_DBGCNTL_STATE_MASK 0x3C00 + +#define XRAM_CHIP_OFFSET 0x0 +#define XRAM_XRAM_CHANNEL_COUNT 0xE000 +#define XRAM_XRAM_MODULE_OFFSET 0x0 +#define XRAM_XRAM_CHAN_INCR 4 +#define XRAM_XRAM_INST_OFFSET(_chan) \ + (XRAM_CHIP_OFFSET + XRAM_XRAM_MODULE_OFFSET + \ + (_chan * XRAM_XRAM_CHAN_INCR)) + +#define YRAM_CHIP_OFFSET 0x40000 +#define YRAM_YRAM_CHANNEL_COUNT 0x8000 +#define YRAM_YRAM_MODULE_OFFSET 0x0 +#define YRAM_YRAM_CHAN_INCR 4 +#define YRAM_YRAM_INST_OFFSET(_chan) \ + (YRAM_CHIP_OFFSET + YRAM_YRAM_MODULE_OFFSET + \ + (_chan * YRAM_YRAM_CHAN_INCR)) + +#define UC_CHIP_OFFSET 0x80000 +#define UC_UC_CHANNEL_COUNT 0x10000 +#define UC_UC_MODULE_OFFSET 0x0 +#define UC_UC_CHAN_INCR 4 +#define UC_UC_INST_OFFSET(_chan) \ + (UC_CHIP_OFFSET + UC_UC_MODULE_OFFSET + \ + (_chan * UC_UC_CHAN_INCR)) + +#define AXRAM_CHIP_OFFSET 0x3C000 +#define AXRAM_AXRAM_CHANNEL_COUNT 0x1000 +#define AXRAM_AXRAM_MODULE_OFFSET 0x0 +#define AXRAM_AXRAM_CHAN_INCR 4 +#define AXRAM_AXRAM_INST_OFFSET(_chan) \ + (AXRAM_CHIP_OFFSET + AXRAM_AXRAM_MODULE_OFFSET + \ + (_chan * AXRAM_AXRAM_CHAN_INCR)) + +#define AYRAM_CHIP_OFFSET 0x78000 +#define AYRAM_AYRAM_CHANNEL_COUNT 0x1000 +#define AYRAM_AYRAM_MODULE_OFFSET 0x0 +#define AYRAM_AYRAM_CHAN_INCR 4 +#define AYRAM_AYRAM_INST_OFFSET(_chan) \ + (AYRAM_CHIP_OFFSET + AYRAM_AYRAM_MODULE_OFFSET + \ + (_chan * AYRAM_AYRAM_CHAN_INCR)) + +#define DSPDMAC_CHIP_OFFSET 0x110000 +#define DSPDMAC_DMA_CFG_CHANNEL_COUNT 12 +#define DSPDMAC_DMACFG_MODULE_OFFSET 0xF00 +#define DSPDMAC_DMACFG_CHAN_INCR 0x10 +#define DSPDMAC_DMACFG_INST_OFFSET(_chan) \ + (DSPDMAC_CHIP_OFFSET + DSPDMAC_DMACFG_MODULE_OFFSET + \ + (_chan * DSPDMAC_DMACFG_CHAN_INCR)) + +#define DSPDMAC_DMACFG_DBADR_LOBIT 0x0 +#define DSPDMAC_DMACFG_DBADR_HIBIT 0x10 +#define DSPDMAC_DMACFG_DBADR_MASK 0x1FFFF +#define DSPDMAC_DMACFG_LP_LOBIT 0x11 +#define DSPDMAC_DMACFG_LP_HIBIT 0x11 +#define DSPDMAC_DMACFG_LP_MASK 0x20000 + +#define DSPDMAC_DMACFG_AINCR_LOBIT 0x12 +#define DSPDMAC_DMACFG_AINCR_HIBIT 0x12 +#define DSPDMAC_DMACFG_AINCR_MASK 0x40000 + +#define DSPDMAC_DMACFG_DWR_LOBIT 0x13 +#define DSPDMAC_DMACFG_DWR_HIBIT 0x13 +#define DSPDMAC_DMACFG_DWR_MASK 0x80000 + +#define DSPDMAC_DMACFG_AJUMP_LOBIT 0x14 +#define DSPDMAC_DMACFG_AJUMP_HIBIT 0x17 +#define DSPDMAC_DMACFG_AJUMP_MASK 0xF00000 + +#define DSPDMAC_DMACFG_AMODE_LOBIT 0x18 +#define DSPDMAC_DMACFG_AMODE_HIBIT 0x19 +#define DSPDMAC_DMACFG_AMODE_MASK 0x3000000 + +#define DSPDMAC_DMACFG_LK_LOBIT 0x1A +#define DSPDMAC_DMACFG_LK_HIBIT 0x1A +#define DSPDMAC_DMACFG_LK_MASK 0x4000000 + +#define DSPDMAC_DMACFG_AICS_LOBIT 0x1B +#define DSPDMAC_DMACFG_AICS_HIBIT 0x1F +#define DSPDMAC_DMACFG_AICS_MASK 0xF8000000 + +#define DSPDMAC_DMACFG_LP_SINGLE 0 +#define DSPDMAC_DMACFG_LP_LOOPING 1 + +#define DSPDMAC_DMACFG_AINCR_XANDY 0 +#define DSPDMAC_DMACFG_AINCR_XORY 1 + +#define DSPDMAC_DMACFG_DWR_DMA_RD 0 +#define DSPDMAC_DMACFG_DWR_DMA_WR 1 + +#define DSPDMAC_DMACFG_AMODE_LINEAR 0 +#define DSPDMAC_DMACFG_AMODE_RSV1 1 +#define DSPDMAC_DMACFG_AMODE_WINTLV 2 +#define DSPDMAC_DMACFG_AMODE_GINTLV 3 + +#define DSPDMAC_DSP_ADR_OFS_CHANNEL_COUNT 12 +#define DSPDMAC_DSPADROFS_MODULE_OFFSET 0xF04 +#define DSPDMAC_DSPADROFS_CHAN_INCR 0x10 +#define DSPDMAC_DSPADROFS_INST_OFFSET(_chan) \ + (DSPDMAC_CHIP_OFFSET + DSPDMAC_DSPADROFS_MODULE_OFFSET + \ + (_chan * DSPDMAC_DSPADROFS_CHAN_INCR)) + +#define DSPDMAC_DSPADROFS_COFS_LOBIT 0x0 +#define DSPDMAC_DSPADROFS_COFS_HIBIT 0xF +#define DSPDMAC_DSPADROFS_COFS_MASK 0xFFFF + +#define DSPDMAC_DSPADROFS_BOFS_LOBIT 0x10 +#define DSPDMAC_DSPADROFS_BOFS_HIBIT 0x1F +#define DSPDMAC_DSPADROFS_BOFS_MASK 0xFFFF0000 + +#define DSPDMAC_DSP_ADR_WOFS_CHANNEL_COUNT 12 +#define DSPDMAC_DSPADRWOFS_MODULE_OFFSET 0xF04 +#define DSPDMAC_DSPADRWOFS_CHAN_INCR 0x10 + +#define DSPDMAC_DSPADRWOFS_INST_OFFSET(_chan) \ + (DSPDMAC_CHIP_OFFSET + DSPDMAC_DSPADRWOFS_MODULE_OFFSET + \ + (_chan * DSPDMAC_DSPADRWOFS_CHAN_INCR)) + +#define DSPDMAC_DSPADRWOFS_WCOFS_LOBIT 0x0 +#define DSPDMAC_DSPADRWOFS_WCOFS_HIBIT 0xA +#define DSPDMAC_DSPADRWOFS_WCOFS_MASK 0x7FF + +#define DSPDMAC_DSPADRWOFS_WCBFR_LOBIT 0xB +#define DSPDMAC_DSPADRWOFS_WCBFR_HIBIT 0xF +#define DSPDMAC_DSPADRWOFS_WCBFR_MASK 0xF800 + +#define DSPDMAC_DSPADRWOFS_WBOFS_LOBIT 0x10 +#define DSPDMAC_DSPADRWOFS_WBOFS_HIBIT 0x1A +#define DSPDMAC_DSPADRWOFS_WBOFS_MASK 0x7FF0000 + +#define DSPDMAC_DSPADRWOFS_WBBFR_LOBIT 0x1B +#define DSPDMAC_DSPADRWOFS_WBBFR_HIBIT 0x1F +#define DSPDMAC_DSPADRWOFS_WBBFR_MASK 0xF8000000 + +#define DSPDMAC_DSP_ADR_GOFS_CHANNEL_COUNT 12 +#define DSPDMAC_DSPADRGOFS_MODULE_OFFSET 0xF04 +#define DSPDMAC_DSPADRGOFS_CHAN_INCR 0x10 +#define DSPDMAC_DSPADRGOFS_INST_OFFSET(_chan) \ + (DSPDMAC_CHIP_OFFSET + DSPDMAC_DSPADRGOFS_MODULE_OFFSET + \ + (_chan * DSPDMAC_DSPADRGOFS_CHAN_INCR)) + +#define DSPDMAC_DSPADRGOFS_GCOFS_LOBIT 0x0 +#define DSPDMAC_DSPADRGOFS_GCOFS_HIBIT 0x9 +#define DSPDMAC_DSPADRGOFS_GCOFS_MASK 0x3FF + +#define DSPDMAC_DSPADRGOFS_GCS_LOBIT 0xA +#define DSPDMAC_DSPADRGOFS_GCS_HIBIT 0xC +#define DSPDMAC_DSPADRGOFS_GCS_MASK 0x1C00 + +#define DSPDMAC_DSPADRGOFS_GCBFR_LOBIT 0xD +#define DSPDMAC_DSPADRGOFS_GCBFR_HIBIT 0xF +#define DSPDMAC_DSPADRGOFS_GCBFR_MASK 0xE000 + +#define DSPDMAC_DSPADRGOFS_GBOFS_LOBIT 0x10 +#define DSPDMAC_DSPADRGOFS_GBOFS_HIBIT 0x19 +#define DSPDMAC_DSPADRGOFS_GBOFS_MASK 0x3FF0000 + +#define DSPDMAC_DSPADRGOFS_GBS_LOBIT 0x1A +#define DSPDMAC_DSPADRGOFS_GBS_HIBIT 0x1C +#define DSPDMAC_DSPADRGOFS_GBS_MASK 0x1C000000 + +#define DSPDMAC_DSPADRGOFS_GBBFR_LOBIT 0x1D +#define DSPDMAC_DSPADRGOFS_GBBFR_HIBIT 0x1F +#define DSPDMAC_DSPADRGOFS_GBBFR_MASK 0xE0000000 + +#define DSPDMAC_XFR_CNT_CHANNEL_COUNT 12 +#define DSPDMAC_XFRCNT_MODULE_OFFSET 0xF08 +#define DSPDMAC_XFRCNT_CHAN_INCR 0x10 + +#define DSPDMAC_XFRCNT_INST_OFFSET(_chan) \ + (DSPDMAC_CHIP_OFFSET + DSPDMAC_XFRCNT_MODULE_OFFSET + \ + (_chan * DSPDMAC_XFRCNT_CHAN_INCR)) + +#define DSPDMAC_XFRCNT_CCNT_LOBIT 0x0 +#define DSPDMAC_XFRCNT_CCNT_HIBIT 0xF +#define DSPDMAC_XFRCNT_CCNT_MASK 0xFFFF + +#define DSPDMAC_XFRCNT_BCNT_LOBIT 0x10 +#define DSPDMAC_XFRCNT_BCNT_HIBIT 0x1F +#define DSPDMAC_XFRCNT_BCNT_MASK 0xFFFF0000 + +#define DSPDMAC_IRQ_CNT_CHANNEL_COUNT 12 +#define DSPDMAC_IRQCNT_MODULE_OFFSET 0xF0C +#define DSPDMAC_IRQCNT_CHAN_INCR 0x10 +#define DSPDMAC_IRQCNT_INST_OFFSET(_chan) \ + (DSPDMAC_CHIP_OFFSET + DSPDMAC_IRQCNT_MODULE_OFFSET + \ + (_chan * DSPDMAC_IRQCNT_CHAN_INCR)) + +#define DSPDMAC_IRQCNT_CICNT_LOBIT 0x0 +#define DSPDMAC_IRQCNT_CICNT_HIBIT 0xF +#define DSPDMAC_IRQCNT_CICNT_MASK 0xFFFF + +#define DSPDMAC_IRQCNT_BICNT_LOBIT 0x10 +#define DSPDMAC_IRQCNT_BICNT_HIBIT 0x1F +#define DSPDMAC_IRQCNT_BICNT_MASK 0xFFFF0000 + +#define DSPDMAC_AUD_CHSEL_CHANNEL_COUNT 12 +#define DSPDMAC_AUDCHSEL_MODULE_OFFSET 0xFC0 +#define DSPDMAC_AUDCHSEL_CHAN_INCR 0x4 +#define DSPDMAC_AUDCHSEL_INST_OFFSET(_chan) \ + (DSPDMAC_CHIP_OFFSET + DSPDMAC_AUDCHSEL_MODULE_OFFSET + \ + (_chan * DSPDMAC_AUDCHSEL_CHAN_INCR)) + +#define DSPDMAC_AUDCHSEL_ACS_LOBIT 0x0 +#define DSPDMAC_AUDCHSEL_ACS_HIBIT 0x1F +#define DSPDMAC_AUDCHSEL_ACS_MASK 0xFFFFFFFF + +#define DSPDMAC_CHNLSTART_MODULE_OFFSET 0xFF0 +#define DSPDMAC_CHNLSTART_INST_OFFSET \ + (DSPDMAC_CHIP_OFFSET + DSPDMAC_CHNLSTART_MODULE_OFFSET) + +#define DSPDMAC_CHNLSTART_EN_LOBIT 0x0 +#define DSPDMAC_CHNLSTART_EN_HIBIT 0xB +#define DSPDMAC_CHNLSTART_EN_MASK 0xFFF + +#define DSPDMAC_CHNLSTART_VAI1_LOBIT 0xC +#define DSPDMAC_CHNLSTART_VAI1_HIBIT 0xF +#define DSPDMAC_CHNLSTART_VAI1_MASK 0xF000 + +#define DSPDMAC_CHNLSTART_DIS_LOBIT 0x10 +#define DSPDMAC_CHNLSTART_DIS_HIBIT 0x1B +#define DSPDMAC_CHNLSTART_DIS_MASK 0xFFF0000 + +#define DSPDMAC_CHNLSTART_VAI2_LOBIT 0x1C +#define DSPDMAC_CHNLSTART_VAI2_HIBIT 0x1F +#define DSPDMAC_CHNLSTART_VAI2_MASK 0xF0000000 + +#define DSPDMAC_CHNLSTATUS_MODULE_OFFSET 0xFF4 +#define DSPDMAC_CHNLSTATUS_INST_OFFSET \ + (DSPDMAC_CHIP_OFFSET + DSPDMAC_CHNLSTATUS_MODULE_OFFSET) + +#define DSPDMAC_CHNLSTATUS_ISC_LOBIT 0x0 +#define DSPDMAC_CHNLSTATUS_ISC_HIBIT 0xB +#define DSPDMAC_CHNLSTATUS_ISC_MASK 0xFFF + +#define DSPDMAC_CHNLSTATUS_AOO_LOBIT 0xC +#define DSPDMAC_CHNLSTATUS_AOO_HIBIT 0xC +#define DSPDMAC_CHNLSTATUS_AOO_MASK 0x1000 + +#define DSPDMAC_CHNLSTATUS_AOU_LOBIT 0xD +#define DSPDMAC_CHNLSTATUS_AOU_HIBIT 0xD +#define DSPDMAC_CHNLSTATUS_AOU_MASK 0x2000 + +#define DSPDMAC_CHNLSTATUS_AIO_LOBIT 0xE +#define DSPDMAC_CHNLSTATUS_AIO_HIBIT 0xE +#define DSPDMAC_CHNLSTATUS_AIO_MASK 0x4000 + +#define DSPDMAC_CHNLSTATUS_AIU_LOBIT 0xF +#define DSPDMAC_CHNLSTATUS_AIU_HIBIT 0xF +#define DSPDMAC_CHNLSTATUS_AIU_MASK 0x8000 + +#define DSPDMAC_CHNLSTATUS_IEN_LOBIT 0x10 +#define DSPDMAC_CHNLSTATUS_IEN_HIBIT 0x1B +#define DSPDMAC_CHNLSTATUS_IEN_MASK 0xFFF0000 + +#define DSPDMAC_CHNLSTATUS_VAI0_LOBIT 0x1C +#define DSPDMAC_CHNLSTATUS_VAI0_HIBIT 0x1F +#define DSPDMAC_CHNLSTATUS_VAI0_MASK 0xF0000000 + +#define DSPDMAC_CHNLPROP_MODULE_OFFSET 0xFF8 +#define DSPDMAC_CHNLPROP_INST_OFFSET \ + (DSPDMAC_CHIP_OFFSET + DSPDMAC_CHNLPROP_MODULE_OFFSET) + +#define DSPDMAC_CHNLPROP_DCON_LOBIT 0x0 +#define DSPDMAC_CHNLPROP_DCON_HIBIT 0xB +#define DSPDMAC_CHNLPROP_DCON_MASK 0xFFF + +#define DSPDMAC_CHNLPROP_FFS_LOBIT 0xC +#define DSPDMAC_CHNLPROP_FFS_HIBIT 0xC +#define DSPDMAC_CHNLPROP_FFS_MASK 0x1000 + +#define DSPDMAC_CHNLPROP_NAJ_LOBIT 0xD +#define DSPDMAC_CHNLPROP_NAJ_HIBIT 0xD +#define DSPDMAC_CHNLPROP_NAJ_MASK 0x2000 + +#define DSPDMAC_CHNLPROP_ENH_LOBIT 0xE +#define DSPDMAC_CHNLPROP_ENH_HIBIT 0xE +#define DSPDMAC_CHNLPROP_ENH_MASK 0x4000 + +#define DSPDMAC_CHNLPROP_MSPCE_LOBIT 0x10 +#define DSPDMAC_CHNLPROP_MSPCE_HIBIT 0x1B +#define DSPDMAC_CHNLPROP_MSPCE_MASK 0xFFF0000 + +#define DSPDMAC_CHNLPROP_AC_LOBIT 0x1C +#define DSPDMAC_CHNLPROP_AC_HIBIT 0x1F +#define DSPDMAC_CHNLPROP_AC_MASK 0xF0000000 + +#define DSPDMAC_ACTIVE_MODULE_OFFSET 0xFFC +#define DSPDMAC_ACTIVE_INST_OFFSET \ + (DSPDMAC_CHIP_OFFSET + DSPDMAC_ACTIVE_MODULE_OFFSET) + +#define DSPDMAC_ACTIVE_AAR_LOBIT 0x0 +#define DSPDMAC_ACTIVE_AAR_HIBIT 0xB +#define DSPDMAC_ACTIVE_AAR_MASK 0xFFF + +#define DSPDMAC_ACTIVE_WFR_LOBIT 0xC +#define DSPDMAC_ACTIVE_WFR_HIBIT 0x17 +#define DSPDMAC_ACTIVE_WFR_MASK 0xFFF000 + +#define DSP_AUX_MEM_BASE 0xE000 +#define INVALID_CHIP_ADDRESS (~0UL) + +#define X_SIZE (XRAM_XRAM_CHANNEL_COUNT * XRAM_XRAM_CHAN_INCR) +#define Y_SIZE (YRAM_YRAM_CHANNEL_COUNT * YRAM_YRAM_CHAN_INCR) +#define AX_SIZE (AXRAM_AXRAM_CHANNEL_COUNT * AXRAM_AXRAM_CHAN_INCR) +#define AY_SIZE (AYRAM_AYRAM_CHANNEL_COUNT * AYRAM_AYRAM_CHAN_INCR) +#define UC_SIZE (UC_UC_CHANNEL_COUNT * UC_UC_CHAN_INCR) + +#define XEXT_SIZE (X_SIZE + AX_SIZE) +#define YEXT_SIZE (Y_SIZE + AY_SIZE) + +#define U64K 0x10000UL + +#define X_END (XRAM_CHIP_OFFSET + X_SIZE) +#define X_EXT (XRAM_CHIP_OFFSET + XEXT_SIZE) +#define AX_END (XRAM_CHIP_OFFSET + U64K*4) + +#define Y_END (YRAM_CHIP_OFFSET + Y_SIZE) +#define Y_EXT (YRAM_CHIP_OFFSET + YEXT_SIZE) +#define AY_END (YRAM_CHIP_OFFSET + U64K*4) + +#define UC_END (UC_CHIP_OFFSET + UC_SIZE) + +#define X_RANGE_MAIN(a, s) \ + (((a)+((s)-1)*XRAM_XRAM_CHAN_INCR < X_END)) +#define X_RANGE_AUX(a, s) \ + (((a) >= X_END) && ((a)+((s)-1)*XRAM_XRAM_CHAN_INCR < AX_END)) +#define X_RANGE_EXT(a, s) \ + (((a)+((s)-1)*XRAM_XRAM_CHAN_INCR < X_EXT)) +#define X_RANGE_ALL(a, s) \ + (((a)+((s)-1)*XRAM_XRAM_CHAN_INCR < AX_END)) + +#define Y_RANGE_MAIN(a, s) \ + (((a) >= YRAM_CHIP_OFFSET) && \ + ((a)+((s)-1)*YRAM_YRAM_CHAN_INCR < Y_END)) +#define Y_RANGE_AUX(a, s) \ + (((a) >= Y_END) && \ + ((a)+((s)-1)*YRAM_YRAM_CHAN_INCR < AY_END)) +#define Y_RANGE_EXT(a, s) \ + (((a) >= YRAM_CHIP_OFFSET) && \ + ((a)+((s)-1)*YRAM_YRAM_CHAN_INCR < Y_EXT)) +#define Y_RANGE_ALL(a, s) \ + (((a) >= YRAM_CHIP_OFFSET) && \ + ((a)+((s)-1)*YRAM_YRAM_CHAN_INCR < AY_END)) + +#define UC_RANGE(a, s) \ + (((a) >= UC_CHIP_OFFSET) && \ + ((a)+((s)-1)*UC_UC_CHAN_INCR < UC_END)) + +#define X_OFF(a) \ + (((a) - XRAM_CHIP_OFFSET) / XRAM_XRAM_CHAN_INCR) +#define AX_OFF(a) \ + (((a) % (AXRAM_AXRAM_CHANNEL_COUNT * \ + AXRAM_AXRAM_CHAN_INCR)) / AXRAM_AXRAM_CHAN_INCR) + +#define Y_OFF(a) \ + (((a) - YRAM_CHIP_OFFSET) / YRAM_YRAM_CHAN_INCR) +#define AY_OFF(a) \ + (((a) % (AYRAM_AYRAM_CHANNEL_COUNT * \ + AYRAM_AYRAM_CHAN_INCR)) / AYRAM_AYRAM_CHAN_INCR) + +#define UC_OFF(a) (((a) - UC_CHIP_OFFSET) / UC_UC_CHAN_INCR) + +#define X_EXT_MAIN_SIZE(a) (XRAM_XRAM_CHANNEL_COUNT - X_OFF(a)) +#define X_EXT_AUX_SIZE(a, s) ((s) - X_EXT_MAIN_SIZE(a)) + +#define Y_EXT_MAIN_SIZE(a) (YRAM_YRAM_CHANNEL_COUNT - Y_OFF(a)) +#define Y_EXT_AUX_SIZE(a, s) ((s) - Y_EXT_MAIN_SIZE(a)) + +#endif diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index 49750a96d649..da655359da02 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -32,6 +32,8 @@ #include "hda_local.h" #include "hda_auto_parser.h" +#include "ca0132_regs.h" + #define WIDGET_CHIP_CTRL 0x15 #define WIDGET_DSP_CTRL 0x16 -- cgit v1.2.3 From 4aa3bb0c52ac1d973eeced63b40ce22130e2eae4 Mon Sep 17 00:00:00 2001 From: Ian Minett Date: Thu, 20 Sep 2012 20:29:15 -0700 Subject: ALSA: hda - Add DSP firmware enums and defs to CA0132 codec Signed-off-by: Ian Minett Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_ca0132.c | 51 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 43 insertions(+), 8 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index da655359da02..846826d2b738 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include "hda_codec.h" #include "hda_local.h" @@ -34,12 +35,33 @@ #include "ca0132_regs.h" +#define DSP_DMA_WRITE_BUFLEN_INIT (1UL<<18) +#define DSP_DMA_WRITE_BUFLEN_OVLY (1UL<<15) + +#define DMA_TRANSFER_FRAME_SIZE_NWORDS 8 +#define DMA_TRANSFER_MAX_FRAME_SIZE_NWORDS 32 +#define DMA_OVERLAY_FRAME_SIZE_NWORDS 2 + +#define MASTERCONTROL 0x80 +#define MASTERCONTROL_ALLOC_DMA_CHAN 9 + #define WIDGET_CHIP_CTRL 0x15 #define WIDGET_DSP_CTRL 0x16 #define WUH_MEM_CONNID 10 #define DSP_MEM_CONNID 16 +#define MEM_CONNID_MICIN1 3 +#define MEM_CONNID_MICIN2 5 +#define MEM_CONNID_MICOUT1 12 +#define MEM_CONNID_MICOUT2 14 +#define MEM_CONNID_WUH 10 +#define MEM_CONNID_DSP 16 +#define MEM_CONNID_DMIC 100 + +#define SCP_SET 0 +#define SCP_GET 1 + enum hda_cmd_vendor_io { /* for DspIO node */ VENDOR_DSPIO_SCP_WRITE_DATA_LOW = 0x000, @@ -64,7 +86,11 @@ enum hda_cmd_vendor_io { VENDOR_CHIPIO_HIC_POST_READ = 0x702, VENDOR_CHIPIO_HIC_READ_DATA = 0xF03, + VENDOR_CHIPIO_8051_DATA_WRITE = 0x707, + VENDOR_CHIPIO_8051_DATA_READ = 0xF07, + VENDOR_CHIPIO_CT_EXTENSIONS_ENABLE = 0x70A, + VENDOR_CHIPIO_CT_EXTENSIONS_GET = 0xF0A, VENDOR_CHIPIO_PLL_PMU_WRITE = 0x70C, VENDOR_CHIPIO_PLL_PMU_READ = 0xF0C, @@ -72,18 +98,27 @@ enum hda_cmd_vendor_io { VENDOR_CHIPIO_8051_ADDRESS_HIGH = 0x70E, VENDOR_CHIPIO_FLAG_SET = 0x70F, VENDOR_CHIPIO_FLAGS_GET = 0xF0F, - VENDOR_CHIPIO_PARAMETER_SET = 0x710, - VENDOR_CHIPIO_PARAMETER_GET = 0xF10, + VENDOR_CHIPIO_PARAM_SET = 0x710, + VENDOR_CHIPIO_PARAM_GET = 0xF10, VENDOR_CHIPIO_PORT_ALLOC_CONFIG_SET = 0x711, VENDOR_CHIPIO_PORT_ALLOC_SET = 0x712, VENDOR_CHIPIO_PORT_ALLOC_GET = 0xF12, VENDOR_CHIPIO_PORT_FREE_SET = 0x713, - VENDOR_CHIPIO_PARAMETER_EX_ID_GET = 0xF17, - VENDOR_CHIPIO_PARAMETER_EX_ID_SET = 0x717, - VENDOR_CHIPIO_PARAMETER_EX_VALUE_GET = 0xF18, - VENDOR_CHIPIO_PARAMETER_EX_VALUE_SET = 0x718 + VENDOR_CHIPIO_PARAM_EX_ID_GET = 0xF17, + VENDOR_CHIPIO_PARAM_EX_ID_SET = 0x717, + VENDOR_CHIPIO_PARAM_EX_VALUE_GET = 0xF18, + VENDOR_CHIPIO_PARAM_EX_VALUE_SET = 0x718, + + VENDOR_CHIPIO_DMIC_CTL_SET = 0x788, + VENDOR_CHIPIO_DMIC_CTL_GET = 0xF88, + VENDOR_CHIPIO_DMIC_PIN_SET = 0x789, + VENDOR_CHIPIO_DMIC_PIN_GET = 0xF89, + VENDOR_CHIPIO_DMIC_MCLK_SET = 0x78A, + VENDOR_CHIPIO_DMIC_MCLK_GET = 0xF8A, + + VENDOR_CHIPIO_EAPD_SEL_SET = 0x78D }; /* @@ -133,7 +168,7 @@ enum control_flag_id { /* Impedance for ramp generator on Port_A 16 Ohm/10K Ohm */ CONTROL_FLAG_PORT_A_10KOHM_LOAD = 20, /* Impedance for ramp generator on Port_D, 16 Ohm/10K Ohm */ - CONTROL_FLAG_PORT_D_10K0HM_LOAD = 21, + CONTROL_FLAG_PORT_D_10KOHM_LOAD = 21, /* ASI rate is 48kHz/96kHz */ CONTROL_FLAG_ASI_96KHZ = 22, /* DAC power settings able to control attached ports no/yes */ @@ -147,7 +182,7 @@ enum control_flag_id { /* * Control parameter IDs */ -enum control_parameter_id { +enum control_param_id { /* 0: force HDA, 1: allow DSP if HDA Spdif1Out stream is idle */ CONTROL_PARAM_SPDIF1_SOURCE = 2, -- cgit v1.2.3 From 01ef7dbffb411d9d78d1150b268d9c757f9f2f93 Mon Sep 17 00:00:00 2001 From: Ian Minett Date: Thu, 20 Sep 2012 20:29:16 -0700 Subject: ALSA: hda - Update CA0132 codec to load DSP firmware binary This patch adds the code needed to fetch the DSP binary image from the local firmware install location and transfer it over to the chip using the new DSP loader bus ops. Actual DSP effect controls, parameters and mixers are to be included later. - Add calls to new DSP loader system to transfer firmware to the hardware. - Add chip read/write routines, DSP I/O, SCP packet format helper functions and transfer DMA management. - Add guard around DSP download to ensure loader config switch is enabled. The general scheme for downloading the DSP is as follows: 1) If DSP firmware loader is enabled, ca0132_download_dsp() is called to start the process. 2) Driver requests DSP image using request_firmware(). 3) Driver sets up the streaming DMA for DSP image download with dspload_image() and dspxfr_image(), which in turn calls the DSP loader op snd_hda_codec_load_dsp_prepare() to ready the system. 4) DSP image will consist of 1 or more segments, each transferred in sequence by a call to dspxfr_one_seg() and snd_hda_codec_load_dsp_trigger(). 5) Once complete, the loader state is cleaned up with snd_hda_codec_load_dsp_cleanup(). Signed-off-by: Ian Minett Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_ca0132.c | 1869 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 1735 insertions(+), 134 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index 846826d2b738..f5aea7865dbf 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -48,9 +48,6 @@ #define WIDGET_CHIP_CTRL 0x15 #define WIDGET_DSP_CTRL 0x16 -#define WUH_MEM_CONNID 10 -#define DSP_MEM_CONNID 16 - #define MEM_CONNID_MICIN1 3 #define MEM_CONNID_MICIN2 5 #define MEM_CONNID_MICOUT1 12 @@ -62,6 +59,10 @@ #define SCP_SET 0 #define SCP_GET 1 +#define EFX_FILE "ctefx.bin" + +MODULE_FIRMWARE(EFX_FILE); + enum hda_cmd_vendor_io { /* for DspIO node */ VENDOR_DSPIO_SCP_WRITE_DATA_LOW = 0x000, @@ -320,192 +321,1736 @@ static int _add_switch(struct hda_codec *codec, hda_nid_t nid, const char *pfx, return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec)); } -static int _add_volume(struct hda_codec *codec, hda_nid_t nid, const char *pfx, - int chan, int dir) +static int _add_volume(struct hda_codec *codec, hda_nid_t nid, const char *pfx, + int chan, int dir) +{ + char namestr[44]; + int type = dir ? HDA_INPUT : HDA_OUTPUT; + struct snd_kcontrol_new knew = + HDA_CODEC_VOLUME_MONO(namestr, nid, chan, 0, type); + if ((query_amp_caps(codec, nid, type) & AC_AMPCAP_NUM_STEPS) == 0) { + snd_printdd("Skipping '%s %s Volume' (no amp on node 0x%x)\n", pfx, dirstr[dir], nid); + return 0; + } + sprintf(namestr, "%s %s Volume", pfx, dirstr[dir]); + return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec)); +} + +#define add_out_switch(codec, nid, pfx) _add_switch(codec, nid, pfx, 3, 0) +#define add_out_volume(codec, nid, pfx) _add_volume(codec, nid, pfx, 3, 0) +#define add_in_switch(codec, nid, pfx) _add_switch(codec, nid, pfx, 3, 1) +#define add_in_volume(codec, nid, pfx) _add_volume(codec, nid, pfx, 3, 1) +#define add_mono_switch(codec, nid, pfx, chan) \ + _add_switch(codec, nid, pfx, chan, 0) +#define add_mono_volume(codec, nid, pfx, chan) \ + _add_volume(codec, nid, pfx, chan, 0) +#define add_in_mono_switch(codec, nid, pfx, chan) \ + _add_switch(codec, nid, pfx, chan, 1) +#define add_in_mono_volume(codec, nid, pfx, chan) \ + _add_volume(codec, nid, pfx, chan, 1) + +enum dsp_download_state { + DSP_DOWNLOAD_FAILED = -1, + DSP_DOWNLOAD_INIT = 0, + DSP_DOWNLOADING = 1, + DSP_DOWNLOADED = 2 +}; + +struct hda_stream_format { + unsigned int sample_rate; + unsigned short valid_bits_per_sample; + unsigned short container_size; + unsigned short number_channels; +}; + +/* retrieve parameters from hda format */ +#define get_hdafmt_chs(fmt) (fmt & 0xf) +#define get_hdafmt_bits(fmt) ((fmt >> 4) & 0x7) +#define get_hdafmt_rate(fmt) ((fmt >> 8) & 0x7f) +#define get_hdafmt_type(fmt) ((fmt >> 15) & 0x1) + +/* + * CA0132 specific + */ + +struct ca0132_spec { + struct auto_pin_cfg autocfg; + struct hda_multi_out multiout; + hda_nid_t out_pins[AUTO_CFG_MAX_OUTS]; + hda_nid_t dacs[AUTO_CFG_MAX_OUTS]; + hda_nid_t hp_dac; + hda_nid_t input_pins[AUTO_PIN_LAST]; + hda_nid_t adcs[AUTO_PIN_LAST]; + hda_nid_t dig_out; + hda_nid_t dig_in; + unsigned int num_inputs; + long curr_hp_switch; + long curr_hp_volume[2]; + long curr_speaker_switch; + const char *input_labels[AUTO_PIN_LAST]; + struct hda_pcm pcm_rec[2]; /* PCM information */ + + /* chip access */ + struct mutex chipio_mutex; /* chip access mutex */ + u32 curr_chip_addx; + + /* DSP download related */ + enum dsp_download_state dsp_state; + unsigned int dsp_stream_id; + unsigned int wait_scp; + unsigned int wait_scp_header; + unsigned int wait_num_data; + unsigned int scp_resp_header; + unsigned int scp_resp_data[4]; + unsigned int scp_resp_count; +}; + +/* + * CA0132 codec access + */ +unsigned int codec_send_command(struct hda_codec *codec, hda_nid_t nid, + unsigned int verb, unsigned int parm, unsigned int *res) +{ + unsigned int response; + response = snd_hda_codec_read(codec, nid, 0, verb, parm); + *res = response; + + return ((response == -1) ? -1 : 0); +} + +static int codec_set_converter_format(struct hda_codec *codec, hda_nid_t nid, + unsigned short converter_format, unsigned int *res) +{ + return codec_send_command(codec, nid, VENDOR_CHIPIO_STREAM_FORMAT, + converter_format & 0xffff, res); +} + +static int codec_set_converter_stream_channel(struct hda_codec *codec, + hda_nid_t nid, unsigned char stream, + unsigned char channel, unsigned int *res) +{ + unsigned char converter_stream_channel = 0; + + converter_stream_channel = (stream << 4) | (channel & 0x0f); + return codec_send_command(codec, nid, AC_VERB_SET_CHANNEL_STREAMID, + converter_stream_channel, res); +} + +/* Chip access helper function */ +static int chipio_send(struct hda_codec *codec, + unsigned int reg, + unsigned int data) +{ + unsigned int res; + int retry = 50; + + /* send bits of data specified by reg */ + do { + res = snd_hda_codec_read(codec, WIDGET_CHIP_CTRL, 0, + reg, data); + if (res == VENDOR_STATUS_CHIPIO_OK) + return 0; + } while (--retry); + return -EIO; +} + +/* + * Write chip address through the vendor widget -- NOT protected by the Mutex! + */ +static int chipio_write_address(struct hda_codec *codec, + unsigned int chip_addx) +{ + int res; + + /* send low 16 bits of the address */ + res = chipio_send(codec, VENDOR_CHIPIO_ADDRESS_LOW, + chip_addx & 0xffff); + + if (res != -EIO) { + /* send high 16 bits of the address */ + res = chipio_send(codec, VENDOR_CHIPIO_ADDRESS_HIGH, + chip_addx >> 16); + } + + return res; +} + +static int chipio_write_addx(struct hda_codec *codec, u32 chip_addx) +{ + struct ca0132_spec *spec = codec->spec; + int status; + + if (spec->curr_chip_addx == chip_addx) + return 0; + + /* send low 16 bits of the address */ + status = chipio_send(codec, VENDOR_CHIPIO_ADDRESS_LOW, + chip_addx & 0xffff); + + if (status < 0) + return status; + + /* send high 16 bits of the address */ + status = chipio_send(codec, VENDOR_CHIPIO_ADDRESS_HIGH, + chip_addx >> 16); + + spec->curr_chip_addx = (status < 0) ? ~0UL : chip_addx; + + return status; +} + +/* + * Write data through the vendor widget -- NOT protected by the Mutex! + */ + +static int chipio_write_data(struct hda_codec *codec, unsigned int data) +{ + int res; + + /* send low 16 bits of the data */ + res = chipio_send(codec, VENDOR_CHIPIO_DATA_LOW, data & 0xffff); + + if (res != -EIO) { + /* send high 16 bits of the data */ + res = chipio_send(codec, VENDOR_CHIPIO_DATA_HIGH, + data >> 16); + } + + return res; +} + +static int chipio_write_data_multiple(struct hda_codec *codec, + const u32 *data, + unsigned int count) +{ + int status = 0; + + if (data == NULL) { + snd_printdd(KERN_ERR "chipio_write_data null ptr"); + return -EINVAL; + } + + while ((count-- != 0) && (status == 0)) + status = chipio_write_data(codec, *data++); + + return status; +} + + +/* + * Read data through the vendor widget -- NOT protected by the Mutex! + */ +static int chipio_read_data(struct hda_codec *codec, unsigned int *data) +{ + int res; + + /* post read */ + res = chipio_send(codec, VENDOR_CHIPIO_HIC_POST_READ, 0); + + if (res != -EIO) { + /* read status */ + res = chipio_send(codec, VENDOR_CHIPIO_STATUS, 0); + } + + if (res != -EIO) { + /* read data */ + *data = snd_hda_codec_read(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_HIC_READ_DATA, + 0); + } + + return res; +} + +/* + * Write given value to the given address through the chip I/O widget. + * protected by the Mutex + */ +static int chipio_write(struct hda_codec *codec, + unsigned int chip_addx, const unsigned int data) +{ + struct ca0132_spec *spec = codec->spec; + int err; + + mutex_lock(&spec->chipio_mutex); + + /* write the address, and if successful proceed to write data */ + err = chipio_write_address(codec, chip_addx); + if (err < 0) + goto exit; + + err = chipio_write_data(codec, data); + if (err < 0) + goto exit; + +exit: + mutex_unlock(&spec->chipio_mutex); + return err; +} + +static int chipio_write_multiple(struct hda_codec *codec, + u32 chip_addx, + const u32 *data, + unsigned int count) +{ + struct ca0132_spec *spec = codec->spec; + int status; + + mutex_lock(&spec->chipio_mutex); + status = chipio_write_addx(codec, chip_addx); + if (status < 0) + goto error; + + status = chipio_write_data_multiple(codec, data, count); +error: + mutex_unlock(&spec->chipio_mutex); + + return status; +} + +/* + * Read the given address through the chip I/O widget + * protected by the Mutex + */ +static int chipio_read(struct hda_codec *codec, + unsigned int chip_addx, unsigned int *data) +{ + struct ca0132_spec *spec = codec->spec; + int err; + + mutex_lock(&spec->chipio_mutex); + + /* write the address, and if successful proceed to write data */ + err = chipio_write_address(codec, chip_addx); + if (err < 0) + goto exit; + + err = chipio_read_data(codec, data); + if (err < 0) + goto exit; + +exit: + mutex_unlock(&spec->chipio_mutex); + return err; +} + +static void chipio_set_control_flag(struct hda_codec *codec, + enum control_flag_id flag_id, + bool flag_state) +{ + unsigned int val; + unsigned int flag_bit; + + flag_bit = (flag_state ? 1 : 0); + val = (flag_bit << 7) | (flag_id); + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_FLAG_SET, val); +} + +static void chipio_set_control_param(struct hda_codec *codec, + enum control_param_id param_id, int param_val) +{ + struct ca0132_spec *spec = codec->spec; + int val; + + if ((param_id < 32) && (param_val < 8)) { + val = (param_val << 5) | (param_id); + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_PARAM_SET, val); + } else { + mutex_lock(&spec->chipio_mutex); + if (chipio_send(codec, VENDOR_CHIPIO_STATUS, 0) == 0) { + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_PARAM_EX_ID_SET, + param_id); + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_PARAM_EX_VALUE_SET, + param_val); + } + mutex_unlock(&spec->chipio_mutex); + } +} + +static void chipio_set_conn_rate(struct hda_codec *codec, + int connid, enum ca0132_sample_rate rate) +{ + chipio_set_control_param(codec, CONTROL_PARAM_CONN_POINT_ID, connid); + chipio_set_control_param(codec, CONTROL_PARAM_CONN_POINT_SAMPLE_RATE, + rate); +} + +static void chipio_enable_clocks(struct hda_codec *codec) +{ + struct ca0132_spec *spec = codec->spec; + + mutex_lock(&spec->chipio_mutex); + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_8051_ADDRESS_LOW, 0); + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_PLL_PMU_WRITE, 0xff); + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_8051_ADDRESS_LOW, 5); + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_PLL_PMU_WRITE, 0x0b); + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_8051_ADDRESS_LOW, 6); + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_PLL_PMU_WRITE, 0xff); + mutex_unlock(&spec->chipio_mutex); +} + +/* + * CA0132 DSP IO stuffs + */ +static int dspio_send(struct hda_codec *codec, unsigned int reg, + unsigned int data) +{ + unsigned int res; + int retry = 50; + + /* send bits of data specified by reg to dsp */ + do { + res = snd_hda_codec_read(codec, WIDGET_DSP_CTRL, 0, reg, data); + if ((res >= 0) && (res != VENDOR_STATUS_DSPIO_BUSY)) + return res; + } while (--retry); + + return -EIO; +} + +static void dspio_write_wait(struct hda_codec *codec) +{ + int cur_val, prv_val; + int retry = 50; + + cur_val = 0; + do { + prv_val = cur_val; + msleep(20); + dspio_send(codec, VENDOR_DSPIO_SCP_POST_COUNT_QUERY, 1); + dspio_send(codec, VENDOR_DSPIO_STATUS, 0); + cur_val = snd_hda_codec_read(codec, WIDGET_DSP_CTRL, 0, + VENDOR_DSPIO_SCP_READ_COUNT, 0); + } while (cur_val && (cur_val == prv_val) && --retry); +} + +static int dspio_write(struct hda_codec *codec, unsigned int scp_data) +{ + struct ca0132_spec *spec = codec->spec; + int status; + + dspio_write_wait(codec); + + mutex_lock(&spec->chipio_mutex); + status = dspio_send(codec, VENDOR_DSPIO_SCP_WRITE_DATA_LOW, + scp_data & 0xffff); + if (status < 0) + goto error; + + status = dspio_send(codec, VENDOR_DSPIO_SCP_WRITE_DATA_HIGH, + scp_data >> 16); + if (status < 0) + goto error; + + /* OK, now check if the write itself has executed*/ + status = snd_hda_codec_read(codec, WIDGET_DSP_CTRL, 0, + VENDOR_DSPIO_STATUS, 0); +error: + mutex_unlock(&spec->chipio_mutex); + + return (status == VENDOR_STATUS_DSPIO_SCP_COMMAND_QUEUE_FULL) ? + -EIO : 0; +} + +static int dspio_write_multiple(struct hda_codec *codec, + unsigned int *buffer, unsigned int size) +{ + int status = 0; + unsigned int count; + + if ((buffer == NULL)) + return -EINVAL; + + count = 0; + while (count < size) { + status = dspio_write(codec, *buffer++); + if (status != 0) + break; + count++; + } + + return status; +} + +static inline unsigned int +make_scp_header(unsigned int target_id, unsigned int source_id, + unsigned int get_flag, unsigned int req, + unsigned int device_flag, unsigned int resp_flag, + unsigned int error_flag, unsigned int data_size) +{ + unsigned int header = 0; + + header = (data_size & 0x1f) << 27; + header |= (error_flag & 0x01) << 26; + header |= (resp_flag & 0x01) << 25; + header |= (device_flag & 0x01) << 24; + header |= (req & 0x7f) << 17; + header |= (get_flag & 0x01) << 16; + header |= (source_id & 0xff) << 8; + header |= target_id & 0xff; + + return header; +} + +static inline void +extract_scp_header(unsigned int header, + unsigned int *target_id, unsigned int *source_id, + unsigned int *get_flag, unsigned int *req, + unsigned int *device_flag, unsigned int *resp_flag, + unsigned int *error_flag, unsigned int *data_size) +{ + if (data_size) + *data_size = (header >> 27) & 0x1f; + if (error_flag) + *error_flag = (header >> 26) & 0x01; + if (resp_flag) + *resp_flag = (header >> 25) & 0x01; + if (device_flag) + *device_flag = (header >> 24) & 0x01; + if (req) + *req = (header >> 17) & 0x7f; + if (get_flag) + *get_flag = (header >> 16) & 0x01; + if (source_id) + *source_id = (header >> 8) & 0xff; + if (target_id) + *target_id = header & 0xff; +} + +#define SCP_MAX_DATA_WORDS (16) + +/* Structure to contain any SCP message */ +struct scp_msg { + unsigned int hdr; + unsigned int data[SCP_MAX_DATA_WORDS]; +}; + +static int dspio_send_scp_message(struct hda_codec *codec, + unsigned char *send_buf, + unsigned int send_buf_size, + unsigned char *return_buf, + unsigned int return_buf_size, + unsigned int *bytes_returned) +{ + struct ca0132_spec *spec = codec->spec; + int retry; + int status = -1; + unsigned int scp_send_size = 0; + unsigned int total_size; + bool waiting_for_resp = false; + unsigned int header; + struct scp_msg *ret_msg; + unsigned int resp_src_id, resp_target_id; + unsigned int data_size, src_id, target_id, get_flag, device_flag; + + if (bytes_returned) + *bytes_returned = 0; + + /* get scp header from buffer */ + header = *((unsigned int *)send_buf); + extract_scp_header(header, &target_id, &src_id, &get_flag, NULL, + &device_flag, NULL, NULL, &data_size); + scp_send_size = data_size + 1; + total_size = (scp_send_size * 4); + + if (send_buf_size < total_size) + return -EINVAL; + + if (get_flag || device_flag) { + if (!return_buf || return_buf_size < 4 || !bytes_returned) + return -EINVAL; + + spec->wait_scp_header = *((unsigned int *)send_buf); + + /* swap source id with target id */ + resp_target_id = src_id; + resp_src_id = target_id; + spec->wait_scp_header &= 0xffff0000; + spec->wait_scp_header |= (resp_src_id << 8) | (resp_target_id); + spec->wait_num_data = return_buf_size/sizeof(unsigned int) - 1; + spec->wait_scp = 1; + waiting_for_resp = true; + } + + status = dspio_write_multiple(codec, (unsigned int *)send_buf, + scp_send_size); + if (status < 0) { + spec->wait_scp = 0; + return status; + } + + if (waiting_for_resp) { + memset(return_buf, 0, return_buf_size); + retry = 50; + do { + msleep(20); + } while (spec->wait_scp && (--retry != 0)); + waiting_for_resp = false; + if (retry != 0) { + ret_msg = (struct scp_msg *)return_buf; + memcpy(&ret_msg->hdr, &spec->scp_resp_header, 4); + memcpy(&ret_msg->data, spec->scp_resp_data, + spec->wait_num_data); + *bytes_returned = (spec->scp_resp_count + 1) * 4; + status = 0; + } else { + status = -EIO; + } + spec->wait_scp = 0; + } + + return status; +} + +static int dspio_scp(struct hda_codec *codec, + int mod_id, int req, int dir, void *data, unsigned int len, + void *reply, unsigned int *reply_len) +{ + int status = 0; + struct scp_msg scp_send, scp_reply; + unsigned int ret_bytes, send_size, ret_size; + unsigned int send_get_flag, reply_resp_flag, reply_error_flag; + unsigned int reply_data_size; + + memset(&scp_send, 0, sizeof(scp_send)); + memset(&scp_reply, 0, sizeof(scp_reply)); + + if ((len != 0 && data == NULL) || (len > SCP_MAX_DATA_WORDS)) + return -EINVAL; + + if (dir == SCP_GET && reply == NULL) { + snd_printdd(KERN_ERR "dspio_scp get but has no buffer"); + return -EINVAL; + } + + if (reply != NULL && (reply_len == NULL || (*reply_len == 0))) { + snd_printdd(KERN_ERR "dspio_scp bad resp buf len parms"); + return -EINVAL; + } + + scp_send.hdr = make_scp_header(mod_id, 0x20, (dir == SCP_GET), req, + 0, 0, 0, len/sizeof(unsigned int)); + if (data != NULL && len > 0) { + len = min((unsigned int)(sizeof(scp_send.data)), len); + memcpy(scp_send.data, data, len); + } + + ret_bytes = 0; + send_size = sizeof(unsigned int) + len; + status = dspio_send_scp_message(codec, (unsigned char *)&scp_send, + send_size, (unsigned char *)&scp_reply, + sizeof(scp_reply), &ret_bytes); + + if (status < 0) { + snd_printdd(KERN_ERR "dspio_scp: send scp msg failed"); + return status; + } + + /* extract send and reply headers members */ + extract_scp_header(scp_send.hdr, NULL, NULL, &send_get_flag, + NULL, NULL, NULL, NULL, NULL); + extract_scp_header(scp_reply.hdr, NULL, NULL, NULL, NULL, NULL, + &reply_resp_flag, &reply_error_flag, + &reply_data_size); + + if (!send_get_flag) + return 0; + + if (reply_resp_flag && !reply_error_flag) { + ret_size = (ret_bytes - sizeof(scp_reply.hdr)) + / sizeof(unsigned int); + + if (*reply_len < ret_size*sizeof(unsigned int)) { + snd_printdd(KERN_ERR "reply too long for buf"); + return -EINVAL; + } else if (ret_size != reply_data_size) { + snd_printdd(KERN_ERR "RetLen and HdrLen .NE."); + return -EINVAL; + } else { + *reply_len = ret_size*sizeof(unsigned int); + memcpy(reply, scp_reply.data, *reply_len); + } + } else { + snd_printdd(KERN_ERR "reply ill-formed or errflag set"); + return -EIO; + } + + return status; +} + +static int dspio_alloc_dma_chan(struct hda_codec *codec, unsigned int *dma_chan) +{ + int status = 0; + unsigned int size = sizeof(dma_chan); + + snd_printdd(KERN_INFO " dspio_alloc_dma_chan() -- begin"); + status = dspio_scp(codec, MASTERCONTROL, MASTERCONTROL_ALLOC_DMA_CHAN, + SCP_GET, NULL, 0, dma_chan, &size); + + if (status < 0) { + snd_printdd(KERN_INFO "dspio_alloc_dma_chan: SCP Failed"); + return status; + } + + if ((*dma_chan + 1) == 0) { + snd_printdd(KERN_INFO "no free dma channels to allocate"); + return -EBUSY; + } + + snd_printdd("dspio_alloc_dma_chan: chan=%d\n", *dma_chan); + snd_printdd(KERN_INFO " dspio_alloc_dma_chan() -- complete"); + + return status; +} + +static int dspio_free_dma_chan(struct hda_codec *codec, unsigned int dma_chan) +{ + int status = 0; + unsigned int dummy = 0; + + snd_printdd(KERN_INFO " dspio_free_dma_chan() -- begin"); + snd_printdd("dspio_free_dma_chan: chan=%d\n", dma_chan); + + status = dspio_scp(codec, MASTERCONTROL, MASTERCONTROL_ALLOC_DMA_CHAN, + SCP_SET, &dma_chan, sizeof(dma_chan), NULL, &dummy); + + if (status < 0) { + snd_printdd(KERN_INFO "dspio_free_dma_chan: SCP Failed"); + return status; + } + + snd_printdd(KERN_INFO " dspio_free_dma_chan() -- complete"); + + return status; +} + +/* + * CA0132 DSP access stuffs + */ +static int dsp_set_run_state(struct hda_codec *codec) +{ + unsigned int dbg_ctrl_reg; + unsigned int halt_state; + int err; + + err = chipio_read(codec, DSP_DBGCNTL_INST_OFFSET, &dbg_ctrl_reg); + if (err < 0) + return err; + + halt_state = (dbg_ctrl_reg & DSP_DBGCNTL_STATE_MASK) >> + DSP_DBGCNTL_STATE_LOBIT; + + if (halt_state != 0) { + dbg_ctrl_reg &= ~((halt_state << DSP_DBGCNTL_SS_LOBIT) & + DSP_DBGCNTL_SS_MASK); + err = chipio_write(codec, DSP_DBGCNTL_INST_OFFSET, + dbg_ctrl_reg); + if (err < 0) + return err; + + dbg_ctrl_reg |= (halt_state << DSP_DBGCNTL_EXEC_LOBIT) & + DSP_DBGCNTL_EXEC_MASK; + err = chipio_write(codec, DSP_DBGCNTL_INST_OFFSET, + dbg_ctrl_reg); + if (err < 0) + return err; + } + + return 0; +} + +static int dsp_reset(struct hda_codec *codec) +{ + unsigned int res; + int retry = 20; + + snd_printdd("dsp_reset\n"); + do { + res = dspio_send(codec, VENDOR_DSPIO_DSP_INIT, 0); + retry--; + } while (res == -EIO && retry); + + if (!retry) { + snd_printdd("dsp_reset timeout\n"); + return -EIO; + } + + return 0; +} + +static unsigned int dsp_chip_to_dsp_addx(unsigned int chip_addx, + bool *code, bool *yram) +{ + *code = *yram = false; + + if (UC_RANGE(chip_addx, 1)) { + *code = true; + return UC_OFF(chip_addx); + } else if (X_RANGE_ALL(chip_addx, 1)) { + return X_OFF(chip_addx); + } else if (Y_RANGE_ALL(chip_addx, 1)) { + *yram = true; + return Y_OFF(chip_addx); + } + + return (unsigned int)INVALID_CHIP_ADDRESS; +} + +static bool dsp_is_dma_active(struct hda_codec *codec, unsigned int dma_chan) +{ + unsigned int dma_chnlstart_reg; + + chipio_read(codec, DSPDMAC_CHNLSTART_INST_OFFSET, &dma_chnlstart_reg); + + return ((dma_chnlstart_reg & (1 << + (DSPDMAC_CHNLSTART_EN_LOBIT + dma_chan))) != 0); +} + +static int dsp_dma_setup_common(struct hda_codec *codec, + unsigned int chip_addx, + unsigned int dma_chan, + unsigned int port_map_mask, + bool ovly) +{ + int status = 0; + unsigned int chnl_prop; + unsigned int dsp_addx; + unsigned int active; + bool code, yram; + + snd_printdd(KERN_INFO "-- dsp_dma_setup_common() -- Begin ---------"); + + if (dma_chan >= DSPDMAC_DMA_CFG_CHANNEL_COUNT) { + snd_printdd(KERN_ERR "dma chan num invalid"); + return -EINVAL; + } + + if (dsp_is_dma_active(codec, dma_chan)) { + snd_printdd(KERN_ERR "dma already active"); + return -EBUSY; + } + + dsp_addx = dsp_chip_to_dsp_addx(chip_addx, &code, &yram); + + if (dsp_addx == INVALID_CHIP_ADDRESS) { + snd_printdd(KERN_ERR "invalid chip addr"); + return -ENXIO; + } + + chnl_prop = DSPDMAC_CHNLPROP_AC_MASK; + active = 0; + + snd_printdd(KERN_INFO " dsp_dma_setup_common() start reg pgm"); + + if (ovly) { + status = chipio_read(codec, DSPDMAC_CHNLPROP_INST_OFFSET, + &chnl_prop); + + if (status < 0) { + snd_printdd(KERN_ERR "read CHNLPROP Reg fail"); + return status; + } + snd_printdd(KERN_INFO "dsp_dma_setup_common() Read CHNLPROP"); + } + + if (!code) + chnl_prop &= ~(1 << (DSPDMAC_CHNLPROP_MSPCE_LOBIT + dma_chan)); + else + chnl_prop |= (1 << (DSPDMAC_CHNLPROP_MSPCE_LOBIT + dma_chan)); + + chnl_prop &= ~(1 << (DSPDMAC_CHNLPROP_DCON_LOBIT + dma_chan)); + + status = chipio_write(codec, DSPDMAC_CHNLPROP_INST_OFFSET, chnl_prop); + if (status < 0) { + snd_printdd(KERN_ERR "write CHNLPROP Reg fail"); + return status; + } + snd_printdd(KERN_INFO " dsp_dma_setup_common() Write CHNLPROP"); + + if (ovly) { + status = chipio_read(codec, DSPDMAC_ACTIVE_INST_OFFSET, + &active); + + if (status < 0) { + snd_printdd(KERN_ERR "read ACTIVE Reg fail"); + return status; + } + snd_printdd(KERN_INFO "dsp_dma_setup_common() Read ACTIVE"); + } + + active &= (~(1 << (DSPDMAC_ACTIVE_AAR_LOBIT + dma_chan))) & + DSPDMAC_ACTIVE_AAR_MASK; + + status = chipio_write(codec, DSPDMAC_ACTIVE_INST_OFFSET, active); + if (status < 0) { + snd_printdd(KERN_ERR "write ACTIVE Reg fail"); + return status; + } + + snd_printdd(KERN_INFO " dsp_dma_setup_common() Write ACTIVE"); + + status = chipio_write(codec, DSPDMAC_AUDCHSEL_INST_OFFSET(dma_chan), + port_map_mask); + if (status < 0) { + snd_printdd(KERN_ERR "write AUDCHSEL Reg fail"); + return status; + } + snd_printdd(KERN_INFO " dsp_dma_setup_common() Write AUDCHSEL"); + + status = chipio_write(codec, DSPDMAC_IRQCNT_INST_OFFSET(dma_chan), + DSPDMAC_IRQCNT_BICNT_MASK | DSPDMAC_IRQCNT_CICNT_MASK); + if (status < 0) { + snd_printdd(KERN_ERR "write IRQCNT Reg fail"); + return status; + } + snd_printdd(KERN_INFO " dsp_dma_setup_common() Write IRQCNT"); + + snd_printdd( + "ChipA=0x%x,DspA=0x%x,dmaCh=%u, " + "CHSEL=0x%x,CHPROP=0x%x,Active=0x%x\n", + chip_addx, dsp_addx, dma_chan, + port_map_mask, chnl_prop, active); + + snd_printdd(KERN_INFO "-- dsp_dma_setup_common() -- Complete ------"); + + return 0; +} + +static int dsp_dma_setup(struct hda_codec *codec, + unsigned int chip_addx, + unsigned int count, + unsigned int dma_chan) +{ + int status = 0; + bool code, yram; + unsigned int dsp_addx; + unsigned int addr_field; + unsigned int incr_field; + unsigned int base_cnt; + unsigned int cur_cnt; + unsigned int dma_cfg = 0; + unsigned int adr_ofs = 0; + unsigned int xfr_cnt = 0; + const unsigned int max_dma_count = 1 << (DSPDMAC_XFRCNT_BCNT_HIBIT - + DSPDMAC_XFRCNT_BCNT_LOBIT + 1); + + snd_printdd(KERN_INFO "-- dsp_dma_setup() -- Begin ---------"); + + if (count > max_dma_count) { + snd_printdd(KERN_ERR "count too big"); + return -EINVAL; + } + + dsp_addx = dsp_chip_to_dsp_addx(chip_addx, &code, &yram); + if (dsp_addx == INVALID_CHIP_ADDRESS) { + snd_printdd(KERN_ERR "invalid chip addr"); + return -ENXIO; + } + + snd_printdd(KERN_INFO " dsp_dma_setup() start reg pgm"); + + addr_field = dsp_addx << DSPDMAC_DMACFG_DBADR_LOBIT; + incr_field = 0; + + if (!code) { + addr_field <<= 1; + if (yram) + addr_field |= (1 << DSPDMAC_DMACFG_DBADR_LOBIT); + + incr_field = (1 << DSPDMAC_DMACFG_AINCR_LOBIT); + } + + dma_cfg = addr_field + incr_field; + status = chipio_write(codec, DSPDMAC_DMACFG_INST_OFFSET(dma_chan), + dma_cfg); + if (status < 0) { + snd_printdd(KERN_ERR "write DMACFG Reg fail"); + return status; + } + snd_printdd(KERN_INFO " dsp_dma_setup() Write DMACFG"); + + adr_ofs = (count - 1) << (DSPDMAC_DSPADROFS_BOFS_LOBIT + + (code ? 0 : 1)); + + status = chipio_write(codec, DSPDMAC_DSPADROFS_INST_OFFSET(dma_chan), + adr_ofs); + if (status < 0) { + snd_printdd(KERN_ERR "write DSPADROFS Reg fail"); + return status; + } + snd_printdd(KERN_INFO " dsp_dma_setup() Write DSPADROFS"); + + base_cnt = (count - 1) << DSPDMAC_XFRCNT_BCNT_LOBIT; + + cur_cnt = (count - 1) << DSPDMAC_XFRCNT_CCNT_LOBIT; + + xfr_cnt = base_cnt | cur_cnt; + + status = chipio_write(codec, + DSPDMAC_XFRCNT_INST_OFFSET(dma_chan), xfr_cnt); + if (status < 0) { + snd_printdd(KERN_ERR "write XFRCNT Reg fail"); + return status; + } + snd_printdd(KERN_INFO " dsp_dma_setup() Write XFRCNT"); + + snd_printdd( + "ChipA=0x%x, cnt=0x%x, DMACFG=0x%x, " + "ADROFS=0x%x, XFRCNT=0x%x\n", + chip_addx, count, dma_cfg, adr_ofs, xfr_cnt); + + snd_printdd(KERN_INFO "-- dsp_dma_setup() -- Complete ---------"); + + return 0; +} + +static int dsp_dma_start(struct hda_codec *codec, + unsigned int dma_chan, bool ovly) +{ + unsigned int reg = 0; + int status = 0; + + snd_printdd(KERN_INFO "-- dsp_dma_start() -- Begin ---------"); + + if (ovly) { + status = chipio_read(codec, + DSPDMAC_CHNLSTART_INST_OFFSET, ®); + + if (status < 0) { + snd_printdd(KERN_ERR "read CHNLSTART reg fail"); + return status; + } + snd_printdd(KERN_INFO "-- dsp_dma_start() Read CHNLSTART"); + + reg &= ~(DSPDMAC_CHNLSTART_EN_MASK | + DSPDMAC_CHNLSTART_DIS_MASK); + } + + status = chipio_write(codec, DSPDMAC_CHNLSTART_INST_OFFSET, + reg | (1 << (dma_chan + DSPDMAC_CHNLSTART_EN_LOBIT))); + if (status < 0) { + snd_printdd(KERN_ERR "write CHNLSTART reg fail"); + return status; + } + snd_printdd(KERN_INFO "-- dsp_dma_start() -- Complete ---------"); + + return status; +} + +static int dsp_dma_stop(struct hda_codec *codec, + unsigned int dma_chan, bool ovly) +{ + unsigned int reg = 0; + int status = 0; + + snd_printdd(KERN_INFO "-- dsp_dma_stop() -- Begin ---------"); + + if (ovly) { + status = chipio_read(codec, + DSPDMAC_CHNLSTART_INST_OFFSET, ®); + + if (status < 0) { + snd_printdd(KERN_ERR "read CHNLSTART reg fail"); + return status; + } + snd_printdd(KERN_INFO "-- dsp_dma_stop() Read CHNLSTART"); + reg &= ~(DSPDMAC_CHNLSTART_EN_MASK | + DSPDMAC_CHNLSTART_DIS_MASK); + } + + status = chipio_write(codec, DSPDMAC_CHNLSTART_INST_OFFSET, + reg | (1 << (dma_chan + DSPDMAC_CHNLSTART_DIS_LOBIT))); + if (status < 0) { + snd_printdd(KERN_ERR "write CHNLSTART reg fail"); + return status; + } + snd_printdd(KERN_INFO "-- dsp_dma_stop() -- Complete ---------"); + + return status; +} + +static int dsp_allocate_router_ports(struct hda_codec *codec, + unsigned int num_chans, + unsigned int ports_per_channel, + unsigned int start_device, + unsigned int *port_map) +{ + int status = 0; + int res; + u8 val; + + status = chipio_send(codec, VENDOR_CHIPIO_STATUS, 0); + if (status < 0) + return status; + + val = start_device << 6; + val |= (ports_per_channel - 1) << 4; + val |= num_chans - 1; + + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_PORT_ALLOC_CONFIG_SET, + val); + + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_PORT_ALLOC_SET, + MEM_CONNID_DSP); + + status = chipio_send(codec, VENDOR_CHIPIO_STATUS, 0); + if (status < 0) + return status; + + res = snd_hda_codec_read(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_PORT_ALLOC_GET, 0); + + *port_map = res; + + return (res < 0) ? res : 0; +} + +static int dsp_free_router_ports(struct hda_codec *codec) +{ + int status = 0; + + status = chipio_send(codec, VENDOR_CHIPIO_STATUS, 0); + if (status < 0) + return status; + + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_PORT_FREE_SET, + MEM_CONNID_DSP); + + status = chipio_send(codec, VENDOR_CHIPIO_STATUS, 0); + + return status; +} + +static int dsp_allocate_ports(struct hda_codec *codec, + unsigned int num_chans, + unsigned int rate_multi, unsigned int *port_map) +{ + int status; + + snd_printdd(KERN_INFO " dsp_allocate_ports() -- begin"); + + if ((rate_multi != 1) && (rate_multi != 2) && (rate_multi != 4)) { + snd_printdd(KERN_ERR "bad rate multiple"); + return -EINVAL; + } + + status = dsp_allocate_router_ports(codec, num_chans, + rate_multi, 0, port_map); + + snd_printdd(KERN_INFO " dsp_allocate_ports() -- complete"); + + return status; +} + +static int dsp_free_ports(struct hda_codec *codec) +{ + int status; + + snd_printdd(KERN_INFO " dsp_free_ports() -- begin"); + + status = dsp_free_router_ports(codec); + if (status < 0) { + snd_printdd(KERN_ERR "free router ports fail"); + return status; + } + snd_printdd(KERN_INFO " dsp_free_ports() -- complete"); + + return status; +} + +static int dsp_allocate_ports_format(struct hda_codec *codec, + const unsigned short fmt, + unsigned int *port_map) +{ + int status; + unsigned int num_chans; + + unsigned int sample_rate_div = ((get_hdafmt_rate(fmt) >> 0) & 3) + 1; + unsigned int sample_rate_mul = ((get_hdafmt_rate(fmt) >> 3) & 3) + 1; + unsigned int rate_multi = sample_rate_mul / sample_rate_div; + + if ((rate_multi != 1) && (rate_multi != 2) && (rate_multi != 4)) { + snd_printdd(KERN_ERR "bad rate multiple"); + return -EINVAL; + } + + num_chans = get_hdafmt_chs(fmt) + 1; + + status = dsp_allocate_ports(codec, num_chans, rate_multi, port_map); + + return status; +} + +/* + * HDA DMA engine stuffs for DSP code download + */ +struct dma_engine { + struct hda_codec *codec; + unsigned short m_converter_format; + struct snd_dma_buffer *dmab; + unsigned int buf_size; +}; + + +enum dma_state { + DMA_STATE_STOP = 0, + DMA_STATE_RUN = 1 +}; + +static int dma_convert_to_hda_format( + struct hda_stream_format *stream_format, + unsigned short *hda_format) +{ + unsigned int format_val; + + format_val = snd_hda_calc_stream_format( + stream_format->sample_rate, + stream_format->number_channels, + SNDRV_PCM_FORMAT_S32_LE, + stream_format->container_size, 0); + + if (hda_format) + *hda_format = (unsigned short)format_val; + + return 0; +} + +static int dma_reset(struct dma_engine *dma) +{ + struct hda_codec *codec = dma->codec; + struct ca0132_spec *spec = codec->spec; + int status; + + if (dma->dmab) + snd_hda_codec_load_dsp_cleanup(codec, dma->dmab); + + status = snd_hda_codec_load_dsp_prepare(codec, + dma->m_converter_format, + dma->buf_size, + dma->dmab); + if (status < 0) + return status; + spec->dsp_stream_id = status; + return 0; +} + +static int dma_set_state(struct dma_engine *dma, enum dma_state state) { - char namestr[44]; - int type = dir ? HDA_INPUT : HDA_OUTPUT; - struct snd_kcontrol_new knew = - HDA_CODEC_VOLUME_MONO(namestr, nid, chan, 0, type); - if ((query_amp_caps(codec, nid, type) & AC_AMPCAP_NUM_STEPS) == 0) { - snd_printdd("Skipping '%s %s Volume' (no amp on node 0x%x)\n", pfx, dirstr[dir], nid); + bool cmd; + + snd_printdd("dma_set_state state=%d\n", state); + + switch (state) { + case DMA_STATE_STOP: + cmd = false; + break; + case DMA_STATE_RUN: + cmd = true; + break; + default: return 0; } - sprintf(namestr, "%s %s Volume", pfx, dirstr[dir]); - return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec)); + + snd_hda_codec_load_dsp_trigger(dma->codec, cmd); + return 0; } -#define add_out_switch(codec, nid, pfx) _add_switch(codec, nid, pfx, 3, 0) -#define add_out_volume(codec, nid, pfx) _add_volume(codec, nid, pfx, 3, 0) -#define add_in_switch(codec, nid, pfx) _add_switch(codec, nid, pfx, 3, 1) -#define add_in_volume(codec, nid, pfx) _add_volume(codec, nid, pfx, 3, 1) -#define add_mono_switch(codec, nid, pfx, chan) \ - _add_switch(codec, nid, pfx, chan, 0) -#define add_mono_volume(codec, nid, pfx, chan) \ - _add_volume(codec, nid, pfx, chan, 0) -#define add_in_mono_switch(codec, nid, pfx, chan) \ - _add_switch(codec, nid, pfx, chan, 1) -#define add_in_mono_volume(codec, nid, pfx, chan) \ - _add_volume(codec, nid, pfx, chan, 1) +static unsigned int dma_get_buffer_size(struct dma_engine *dma) +{ + return dma->dmab->bytes; +} +static unsigned char *dma_get_buffer_addr(struct dma_engine *dma) +{ + return dma->dmab->area; +} -/* - * CA0132 specific - */ +static int dma_xfer(struct dma_engine *dma, + const unsigned int *data, + unsigned int count) +{ + memcpy(dma->dmab->area, data, count); + return 0; +} -struct ca0132_spec { - struct auto_pin_cfg autocfg; - struct hda_multi_out multiout; - hda_nid_t out_pins[AUTO_CFG_MAX_OUTS]; - hda_nid_t dacs[AUTO_CFG_MAX_OUTS]; - hda_nid_t hp_dac; - hda_nid_t input_pins[AUTO_PIN_LAST]; - hda_nid_t adcs[AUTO_PIN_LAST]; - hda_nid_t dig_out; - hda_nid_t dig_in; - unsigned int num_inputs; - long curr_hp_switch; - long curr_hp_volume[2]; - long curr_speaker_switch; - struct mutex chipio_mutex; - const char *input_labels[AUTO_PIN_LAST]; - struct hda_pcm pcm_rec[2]; /* PCM information */ -}; +static void dma_get_converter_format( + struct dma_engine *dma, + unsigned short *format) +{ + if (format) + *format = dma->m_converter_format; +} -/* Chip access helper function */ -static int chipio_send(struct hda_codec *codec, - unsigned int reg, - unsigned int data) +static unsigned int dma_get_stream_id(struct dma_engine *dma) { - unsigned int res; - int retry = 50; + struct ca0132_spec *spec = dma->codec->spec; - /* send bits of data specified by reg */ - do { - res = snd_hda_codec_read(codec, WIDGET_CHIP_CTRL, 0, - reg, data); - if (res == VENDOR_STATUS_CHIPIO_OK) - return 0; - } while (--retry); - return -EIO; + return spec->dsp_stream_id; } -/* - * Write chip address through the vendor widget -- NOT protected by the Mutex! - */ -static int chipio_write_address(struct hda_codec *codec, - unsigned int chip_addx) +struct dsp_image_seg { + u32 magic; + u32 chip_addr; + u32 count; + u32 data[0]; +}; + +static const u32 g_magic_value = 0x4c46584d; +static const u32 g_chip_addr_magic_value = 0xFFFFFF01; + +static bool is_valid(const struct dsp_image_seg *p) { - int res; + return p->magic == g_magic_value; +} - /* send low 16 bits of the address */ - res = chipio_send(codec, VENDOR_CHIPIO_ADDRESS_LOW, - chip_addx & 0xffff); +static bool is_hci_prog_list_seg(const struct dsp_image_seg *p) +{ + return g_chip_addr_magic_value == p->chip_addr; +} - if (res != -EIO) { - /* send high 16 bits of the address */ - res = chipio_send(codec, VENDOR_CHIPIO_ADDRESS_HIGH, - chip_addx >> 16); - } +static bool is_last(const struct dsp_image_seg *p) +{ + return p->count == 0; +} - return res; +static size_t dsp_sizeof(const struct dsp_image_seg *p) +{ + return sizeof(*p) + p->count*sizeof(u32); +} + +static const struct dsp_image_seg *get_next_seg_ptr( + const struct dsp_image_seg *p) +{ + return (struct dsp_image_seg *)((unsigned char *)(p) + dsp_sizeof(p)); } /* - * Write data through the vendor widget -- NOT protected by the Mutex! + * CA0132 chip DSP transfer stuffs. For DSP download. */ +#define INVALID_DMA_CHANNEL (~0UL) -static int chipio_write_data(struct hda_codec *codec, unsigned int data) +static int dspxfr_hci_write(struct hda_codec *codec, + const struct dsp_image_seg *fls) { - int res; + int status; + const u32 *data; + unsigned int count; - /* send low 16 bits of the data */ - res = chipio_send(codec, VENDOR_CHIPIO_DATA_LOW, data & 0xffff); - - if (res != -EIO) { - /* send high 16 bits of the data */ - res = chipio_send(codec, VENDOR_CHIPIO_DATA_HIGH, - data >> 16); + if (fls == NULL || fls->chip_addr != g_chip_addr_magic_value) { + snd_printdd(KERN_ERR "hci_write invalid params"); + return -EINVAL; } - return res; + count = fls->count; + data = (u32 *)(fls->data); + while (count >= 2) { + status = chipio_write(codec, data[0], data[1]); + if (status < 0) { + snd_printdd(KERN_ERR "hci_write chipio failed"); + return status; + } + count -= 2; + data += 2; + } + return 0; } -/* - * Read data through the vendor widget -- NOT protected by the Mutex! - */ -static int chipio_read_data(struct hda_codec *codec, unsigned int *data) +static int dspxfr_one_seg(struct hda_codec *codec, + const struct dsp_image_seg *fls, + unsigned int reloc, + struct dma_engine *dma_engine, + unsigned int dma_chan, + unsigned int port_map_mask, + bool ovly) { - int res; + int status; + bool comm_dma_setup_done = false; + const unsigned int *data; + unsigned int chip_addx; + unsigned int words_to_write; + unsigned int buffer_size_words; + unsigned char *buffer_addx; + unsigned short hda_format; + unsigned int sample_rate_div; + unsigned int sample_rate_mul; + unsigned int num_chans; + unsigned int hda_frame_size_words; + unsigned int remainder_words; + const u32 *data_remainder; + u32 chip_addx_remainder; + unsigned int run_size_words; + const struct dsp_image_seg *hci_write = NULL; + int retry; + + if (fls == NULL) + return -EINVAL; + if (is_hci_prog_list_seg(fls)) { + hci_write = fls; + fls = get_next_seg_ptr(fls); + } - /* post read */ - res = chipio_send(codec, VENDOR_CHIPIO_HIC_POST_READ, 0); + if (hci_write && (!fls || is_last(fls))) { + snd_printdd("hci_write\n"); + return dspxfr_hci_write(codec, hci_write); + } - if (res != -EIO) { - /* read status */ - res = chipio_send(codec, VENDOR_CHIPIO_STATUS, 0); + if (fls == NULL || dma_engine == NULL || port_map_mask == 0) { + snd_printdd("Invalid Params\n"); + return -EINVAL; } - if (res != -EIO) { - /* read data */ - *data = snd_hda_codec_read(codec, WIDGET_CHIP_CTRL, 0, - VENDOR_CHIPIO_HIC_READ_DATA, - 0); + data = fls->data; + chip_addx = fls->chip_addr, + words_to_write = fls->count; + + if (!words_to_write) + return hci_write ? dspxfr_hci_write(codec, hci_write) : 0; + if (reloc) + chip_addx = (chip_addx & (0xFFFF0000 << 2)) + (reloc << 2); + + if (!UC_RANGE(chip_addx, words_to_write) && + !X_RANGE_ALL(chip_addx, words_to_write) && + !Y_RANGE_ALL(chip_addx, words_to_write)) { + snd_printdd("Invalid chip_addx Params\n"); + return -EINVAL; } - return res; + buffer_size_words = (unsigned int)dma_get_buffer_size(dma_engine) / + sizeof(u32); + + buffer_addx = dma_get_buffer_addr(dma_engine); + + if (buffer_addx == NULL) { + snd_printdd(KERN_ERR "dma_engine buffer NULL\n"); + return -EINVAL; + } + + dma_get_converter_format(dma_engine, &hda_format); + sample_rate_div = ((get_hdafmt_rate(hda_format) >> 0) & 3) + 1; + sample_rate_mul = ((get_hdafmt_rate(hda_format) >> 3) & 3) + 1; + num_chans = get_hdafmt_chs(hda_format) + 1; + + hda_frame_size_words = ((sample_rate_div == 0) ? 0 : + (num_chans * sample_rate_mul / sample_rate_div)); + + buffer_size_words = min(buffer_size_words, + (unsigned int)(UC_RANGE(chip_addx, 1) ? + 65536 : 32768)); + buffer_size_words -= buffer_size_words % hda_frame_size_words; + snd_printdd( + "chpadr=0x%08x frmsz=%u nchan=%u " + "rate_mul=%u div=%u bufsz=%u\n", + chip_addx, hda_frame_size_words, num_chans, + sample_rate_mul, sample_rate_div, buffer_size_words); + + if ((buffer_addx == NULL) || (hda_frame_size_words == 0) || + (buffer_size_words < hda_frame_size_words)) { + snd_printdd(KERN_ERR "dspxfr_one_seg:failed\n"); + return -EINVAL; + } + + remainder_words = words_to_write % hda_frame_size_words; + data_remainder = data; + chip_addx_remainder = chip_addx; + + data += remainder_words; + chip_addx += remainder_words*sizeof(u32); + words_to_write -= remainder_words; + + while (words_to_write != 0) { + run_size_words = min(buffer_size_words, words_to_write); + snd_printdd("dspxfr (seg loop)cnt=%u rs=%u remainder=%u\n", + words_to_write, run_size_words, remainder_words); + dma_xfer(dma_engine, data, run_size_words*sizeof(u32)); + if (!comm_dma_setup_done) { + status = dsp_dma_stop(codec, dma_chan, ovly); + if (status < 0) + return -EIO; + status = dsp_dma_setup_common(codec, chip_addx, + dma_chan, port_map_mask, ovly); + if (status < 0) + return status; + comm_dma_setup_done = true; + } + + status = dsp_dma_setup(codec, chip_addx, + run_size_words, dma_chan); + if (status < 0) + return status; + status = dsp_dma_start(codec, dma_chan, ovly); + if (status < 0) + return status; + if (!dsp_is_dma_active(codec, dma_chan)) { + snd_printdd(KERN_ERR "dspxfr:DMA did not start"); + return -EIO; + } + status = dma_set_state(dma_engine, DMA_STATE_RUN); + if (status < 0) + return status; + if (remainder_words != 0) { + status = chipio_write_multiple(codec, + chip_addx_remainder, + data_remainder, + remainder_words); + remainder_words = 0; + } + if (hci_write) { + status = dspxfr_hci_write(codec, hci_write); + hci_write = NULL; + } + retry = 5000; + while (dsp_is_dma_active(codec, dma_chan)) { + if (--retry <= 0) + break; + } + snd_printdd(KERN_INFO "+++++ DMA complete"); + dma_set_state(dma_engine, DMA_STATE_STOP); + dma_reset(dma_engine); + + if (status < 0) + return status; + + data += run_size_words; + chip_addx += run_size_words*sizeof(u32); + words_to_write -= run_size_words; + } + + if (remainder_words != 0) { + status = chipio_write_multiple(codec, chip_addx_remainder, + data_remainder, remainder_words); + } + + return status; } -/* - * Write given value to the given address through the chip I/O widget. - * protected by the Mutex - */ -static int chipio_write(struct hda_codec *codec, - unsigned int chip_addx, const unsigned int data) +static int dspxfr_image(struct hda_codec *codec, + const struct dsp_image_seg *fls_data, + unsigned int reloc, struct hda_stream_format *format, + bool ovly) { struct ca0132_spec *spec = codec->spec; - int err; + int status; + unsigned short hda_format = 0; + unsigned int response; + unsigned char stream_id = 0; + struct dma_engine *dma_engine; + unsigned int dma_chan; + unsigned int port_map_mask; + + if (fls_data == NULL) + return -EINVAL; + + dma_engine = kzalloc(sizeof(*dma_engine), GFP_KERNEL); + if (!dma_engine) { + status = -ENOMEM; + goto exit; + } + memset((void *)dma_engine, 0, sizeof(*dma_engine)); - mutex_lock(&spec->chipio_mutex); + dma_engine->dmab = kzalloc(sizeof(*dma_engine->dmab), GFP_KERNEL); + if (!dma_engine->dmab) { + status = -ENOMEM; + goto exit; + } - /* write the address, and if successful proceed to write data */ - err = chipio_write_address(codec, chip_addx); - if (err < 0) + dma_engine->codec = codec; + dma_convert_to_hda_format(format, &hda_format); + dma_engine->m_converter_format = hda_format; + dma_engine->buf_size = (ovly ? DSP_DMA_WRITE_BUFLEN_OVLY : + DSP_DMA_WRITE_BUFLEN_INIT) * 2; + + dma_chan = 0; + + status = codec_set_converter_format(codec, WIDGET_CHIP_CTRL, + hda_format, &response); + + if (status < 0) { + snd_printdd(KERN_ERR "set converter format fail"); + goto exit; + } + + status = snd_hda_codec_load_dsp_prepare(codec, + dma_engine->m_converter_format, + dma_engine->buf_size, + dma_engine->dmab); + if (status < 0) goto exit; + spec->dsp_stream_id = status; + + if (ovly) { + status = dspio_alloc_dma_chan(codec, &dma_chan); + if (status < 0) { + snd_printdd(KERN_ERR "alloc dmachan fail"); + dma_chan = (unsigned int)INVALID_DMA_CHANNEL; + goto exit; + } + } - err = chipio_write_data(codec, data); - if (err < 0) + port_map_mask = 0; + status = dsp_allocate_ports_format(codec, hda_format, + &port_map_mask); + if (status < 0) { + snd_printdd(KERN_ERR "alloc ports fail"); + goto exit; + } + + stream_id = dma_get_stream_id(dma_engine); + status = codec_set_converter_stream_channel(codec, + WIDGET_CHIP_CTRL, stream_id, 0, &response); + if (status < 0) { + snd_printdd(KERN_ERR "set stream chan fail"); + goto exit; + } + + while ((fls_data != NULL) && !is_last(fls_data)) { + if (!is_valid(fls_data)) { + snd_printdd(KERN_ERR "FLS check fail"); + status = -EINVAL; + goto exit; + } + status = dspxfr_one_seg(codec, fls_data, reloc, + dma_engine, dma_chan, + port_map_mask, ovly); + if (status < 0) + break; + + if (is_hci_prog_list_seg(fls_data)) + fls_data = get_next_seg_ptr(fls_data); + + if ((fls_data != NULL) && !is_last(fls_data)) + fls_data = get_next_seg_ptr(fls_data); + } + + if (port_map_mask != 0) + status = dsp_free_ports(codec); + + if (status < 0) goto exit; + status = codec_set_converter_stream_channel(codec, + WIDGET_CHIP_CTRL, 0, 0, &response); + exit: - mutex_unlock(&spec->chipio_mutex); - return err; + if (ovly && (dma_chan != INVALID_DMA_CHANNEL)) + dspio_free_dma_chan(codec, dma_chan); + + if (dma_engine->dmab) + snd_hda_codec_load_dsp_cleanup(codec, dma_engine->dmab); + kfree(dma_engine->dmab); + kfree(dma_engine); + + return status; } /* - * Read the given address through the chip I/O widget - * protected by the Mutex + * CA0132 DSP download stuffs. */ -static int chipio_read(struct hda_codec *codec, - unsigned int chip_addx, unsigned int *data) +static void dspload_post_setup(struct hda_codec *codec) { - struct ca0132_spec *spec = codec->spec; - int err; + snd_printdd(KERN_INFO "---- dspload_post_setup ------"); - mutex_lock(&spec->chipio_mutex); + /*set DSP speaker to 2.0 configuration*/ + chipio_write(codec, XRAM_XRAM_INST_OFFSET(0x18), 0x08080080); + chipio_write(codec, XRAM_XRAM_INST_OFFSET(0x19), 0x3f800000); - /* write the address, and if successful proceed to write data */ - err = chipio_write_address(codec, chip_addx); - if (err < 0) - goto exit; + /*update write pointer*/ + chipio_write(codec, XRAM_XRAM_INST_OFFSET(0x29), 0x00000002); +} - err = chipio_read_data(codec, data); - if (err < 0) - goto exit; +static int dspload_image(struct hda_codec *codec, + const struct dsp_image_seg *fls, + bool ovly, + unsigned int reloc, + bool autostart, + int router_chans) +{ + int status = 0; + struct hda_stream_format stream_format; + + snd_printdd(KERN_INFO "---- dspload_image begin ------"); + if (router_chans == 0) { + if (!ovly) + router_chans = DMA_TRANSFER_FRAME_SIZE_NWORDS; + else + router_chans = DMA_OVERLAY_FRAME_SIZE_NWORDS; + } -exit: - mutex_unlock(&spec->chipio_mutex); - return err; + stream_format.sample_rate = 48000; + stream_format.number_channels = (unsigned short)router_chans; + + while (stream_format.number_channels > 16) { + stream_format.sample_rate *= 2; + stream_format.number_channels /= 2; + } + + stream_format.container_size = 32; + stream_format.valid_bits_per_sample = 32; + + do { + snd_printdd(KERN_INFO "Ready to program DMA"); + if (!ovly) + status = dsp_reset(codec); + + if (status < 0) + break; + + snd_printdd(KERN_INFO "dsp_reset() complete"); + status = dspxfr_image(codec, fls, reloc, &stream_format, ovly); + + if (status < 0) + break; + + snd_printdd(KERN_INFO "dspxfr_image() complete"); + if (autostart && !ovly) { + dspload_post_setup(codec); + status = dsp_set_run_state(codec); + } + + snd_printdd(KERN_INFO "LOAD FINISHED"); + } while (0); + + return status; +} + +static bool dspload_is_loaded(struct hda_codec *codec) +{ + unsigned int data = 0; + int status = 0; + + status = chipio_read(codec, 0x40004, &data); + if ((status < 0) || (data != 1)) + return false; + + return true; +} + +static bool dspload_wait_loaded(struct hda_codec *codec) +{ + int retry = 100; + + do { + msleep(20); + if (dspload_is_loaded(codec)) { + pr_info("ca0132 DOWNLOAD OK :-) DSP IS RUNNING.\n"); + return true; + } + } while (--retry); + + pr_err("ca0132 DOWNLOAD FAILED!!! DSP IS NOT RUNNING.\n"); + return false; } /* @@ -979,12 +2524,68 @@ static void ca0132_exit_chip(struct hda_codec *codec) /* put any chip cleanup stuffs here. */ } +static void ca0132_set_dsp_msr(struct hda_codec *codec, bool is96k) +{ + chipio_set_control_flag(codec, CONTROL_FLAG_DSP_96KHZ, is96k); + chipio_set_control_flag(codec, CONTROL_FLAG_DAC_96KHZ, is96k); + chipio_set_control_flag(codec, CONTROL_FLAG_SRC_RATE_96KHZ, is96k); + chipio_set_control_flag(codec, CONTROL_FLAG_SRC_CLOCK_196MHZ, is96k); + chipio_set_control_flag(codec, CONTROL_FLAG_ADC_B_96KHZ, is96k); + chipio_set_control_flag(codec, CONTROL_FLAG_ADC_C_96KHZ, is96k); + + chipio_set_conn_rate(codec, MEM_CONNID_MICIN1, SR_16_000); + chipio_set_conn_rate(codec, MEM_CONNID_MICOUT1, SR_16_000); + chipio_set_conn_rate(codec, MEM_CONNID_WUH, SR_48_000); +} + +static bool ca0132_download_dsp_images(struct hda_codec *codec) +{ + bool dsp_loaded = false; + const struct dsp_image_seg *dsp_os_image; + const struct firmware *fw_entry; + + if (request_firmware(&fw_entry, EFX_FILE, codec->bus->card->dev) != 0) + return false; + + dsp_os_image = (struct dsp_image_seg *)(fw_entry->data); + dspload_image(codec, dsp_os_image, 0, 0, true, 0); + dsp_loaded = dspload_wait_loaded(codec); + + release_firmware(fw_entry); + + + return dsp_loaded; +} + +static void ca0132_download_dsp(struct hda_codec *codec) +{ + struct ca0132_spec *spec = codec->spec; + + spec->dsp_state = DSP_DOWNLOAD_INIT; + + if (spec->dsp_state == DSP_DOWNLOAD_INIT) { + chipio_enable_clocks(codec); + spec->dsp_state = DSP_DOWNLOADING; + if (!ca0132_download_dsp_images(codec)) + spec->dsp_state = DSP_DOWNLOAD_FAILED; + else + spec->dsp_state = DSP_DOWNLOADED; + } + + if (spec->dsp_state == DSP_DOWNLOADED) + ca0132_set_dsp_msr(codec, true); +} + static int ca0132_init(struct hda_codec *codec) { struct ca0132_spec *spec = codec->spec; struct auto_pin_cfg *cfg = &spec->autocfg; int i; +#ifdef CONFIG_SND_HDA_DSP_LOADER + ca0132_download_dsp(codec); +#endif + for (i = 0; i < spec->multiout.num_dacs; i++) { init_output(codec, spec->out_pins[i], spec->multiout.dac_nids[i]); -- cgit v1.2.3 From c3b4eea26208b8e247ece9d3a9ec8b2eab48c464 Mon Sep 17 00:00:00 2001 From: Ian Minett Date: Thu, 20 Sep 2012 20:29:17 -0700 Subject: ALSA: hda - Add firmware caching to CA0132 codec Signed-off-by: Ian Minett Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_ca0132.c | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index f5aea7865dbf..4d8a7edfd308 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -2025,6 +2025,24 @@ static int dspload_image(struct hda_codec *codec, return status; } +static const struct firmware *fw_efx; + +static int request_firmware_cached(const struct firmware **firmware_p, + const char *name, struct device *device) +{ + if (*firmware_p) + return 0; /* already loaded */ + return request_firmware(firmware_p, name, device); +} + +static void release_cached_firmware(void) +{ + if (fw_efx) { + release_firmware(fw_efx); + fw_efx = NULL; + } +} + static bool dspload_is_loaded(struct hda_codec *codec) { unsigned int data = 0; @@ -2542,18 +2560,15 @@ static bool ca0132_download_dsp_images(struct hda_codec *codec) { bool dsp_loaded = false; const struct dsp_image_seg *dsp_os_image; - const struct firmware *fw_entry; - if (request_firmware(&fw_entry, EFX_FILE, codec->bus->card->dev) != 0) + if (request_firmware_cached(&fw_efx, EFX_FILE, + codec->bus->card->dev) != 0) return false; - dsp_os_image = (struct dsp_image_seg *)(fw_entry->data); + dsp_os_image = (struct dsp_image_seg *)(fw_efx->data); dspload_image(codec, dsp_os_image, 0, 0, true, 0); dsp_loaded = dspload_wait_loaded(codec); - release_firmware(fw_entry); - - return dsp_loaded; } @@ -2665,6 +2680,7 @@ static int __init patch_ca0132_init(void) static void __exit patch_ca0132_exit(void) { + release_cached_firmware(); snd_hda_delete_codec_preset(&ca0132_list); } -- cgit v1.2.3 From d5c21b88e8df0701f33eaa33ef33601d8314a4f4 Mon Sep 17 00:00:00 2001 From: Ian Minett Date: Thu, 20 Sep 2012 20:29:18 -0700 Subject: ALSA: hda - Add comments and descriptions to CA0132 functions Add comments and descriptions to functions. Bump dsp_free_ports() to below dsp_allocate_ports_format() to group the alloc functions together for commenting. Signed-off-by: Ian Minett Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_ca0132.c | 180 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 163 insertions(+), 17 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index 4d8a7edfd308..a7b216ea9df3 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -519,6 +519,9 @@ static int chipio_write_data(struct hda_codec *codec, unsigned int data) return res; } +/* + * Write multiple data through the vendor widget -- NOT protected by the Mutex! + */ static int chipio_write_data_multiple(struct hda_codec *codec, const u32 *data, unsigned int count) @@ -588,6 +591,10 @@ exit: return err; } +/* + * Write multiple values to the given address through the chip I/O widget. + * protected by the Mutex + */ static int chipio_write_multiple(struct hda_codec *codec, u32 chip_addx, const u32 *data, @@ -634,6 +641,9 @@ exit: return err; } +/* + * Set chip control flags through the chip I/O widget. + */ static void chipio_set_control_flag(struct hda_codec *codec, enum control_flag_id flag_id, bool flag_state) @@ -647,6 +657,9 @@ static void chipio_set_control_flag(struct hda_codec *codec, VENDOR_CHIPIO_FLAG_SET, val); } +/* + * Set chip parameters through the chip I/O widget. + */ static void chipio_set_control_param(struct hda_codec *codec, enum control_param_id param_id, int param_val) { @@ -671,6 +684,9 @@ static void chipio_set_control_param(struct hda_codec *codec, } } +/* + * Set sampling rate of the connection point. + */ static void chipio_set_conn_rate(struct hda_codec *codec, int connid, enum ca0132_sample_rate rate) { @@ -679,6 +695,9 @@ static void chipio_set_conn_rate(struct hda_codec *codec, rate); } +/* + * Enable clocks. + */ static void chipio_enable_clocks(struct hda_codec *codec) { struct ca0132_spec *spec = codec->spec; @@ -718,6 +737,9 @@ static int dspio_send(struct hda_codec *codec, unsigned int reg, return -EIO; } +/* + * Wait for DSP to be ready for commands + */ static void dspio_write_wait(struct hda_codec *codec) { int cur_val, prv_val; @@ -734,6 +756,9 @@ static void dspio_write_wait(struct hda_codec *codec) } while (cur_val && (cur_val == prv_val) && --retry); } +/* + * Write SCP data to DSP + */ static int dspio_write(struct hda_codec *codec, unsigned int scp_data) { struct ca0132_spec *spec = codec->spec; @@ -762,6 +787,9 @@ error: -EIO : 0; } +/* + * Write multiple SCP data to DSP + */ static int dspio_write_multiple(struct hda_codec *codec, unsigned int *buffer, unsigned int size) { @@ -782,6 +810,9 @@ static int dspio_write_multiple(struct hda_codec *codec, return status; } +/* + * Construct the SCP header using corresponding fields + */ static inline unsigned int make_scp_header(unsigned int target_id, unsigned int source_id, unsigned int get_flag, unsigned int req, @@ -802,6 +833,9 @@ make_scp_header(unsigned int target_id, unsigned int source_id, return header; } +/* + * Extract corresponding fields from SCP header + */ static inline void extract_scp_header(unsigned int header, unsigned int *target_id, unsigned int *source_id, @@ -835,6 +869,9 @@ struct scp_msg { unsigned int data[SCP_MAX_DATA_WORDS]; }; +/* + * Send SCP message to DSP + */ static int dspio_send_scp_message(struct hda_codec *codec, unsigned char *send_buf, unsigned int send_buf_size, @@ -912,6 +949,19 @@ static int dspio_send_scp_message(struct hda_codec *codec, return status; } +/** + * Prepare and send the SCP message to DSP + * @codec: the HDA codec + * @mod_id: ID of the DSP module to send the command + * @req: ID of request to send to the DSP module + * @dir: SET or GET + * @data: pointer to the data to send with the request, request specific + * @len: length of the data, in bytes + * @reply: point to the buffer to hold data returned for a reply + * @reply_len: length of the reply buffer returned from GET + * + * Returns zero or a negative error code. + */ static int dspio_scp(struct hda_codec *codec, int mod_id, int req, int dir, void *data, unsigned int len, void *reply, unsigned int *reply_len) @@ -988,6 +1038,9 @@ static int dspio_scp(struct hda_codec *codec, return status; } +/* + * Allocate a DSP DMA channel via an SCP message + */ static int dspio_alloc_dma_chan(struct hda_codec *codec, unsigned int *dma_chan) { int status = 0; @@ -1013,6 +1066,9 @@ static int dspio_alloc_dma_chan(struct hda_codec *codec, unsigned int *dma_chan) return status; } +/* + * Free a DSP DMA via an SCP message + */ static int dspio_free_dma_chan(struct hda_codec *codec, unsigned int dma_chan) { int status = 0; @@ -1035,7 +1091,7 @@ static int dspio_free_dma_chan(struct hda_codec *codec, unsigned int dma_chan) } /* - * CA0132 DSP access stuffs + * (Re)start the DSP */ static int dsp_set_run_state(struct hda_codec *codec) { @@ -1069,6 +1125,9 @@ static int dsp_set_run_state(struct hda_codec *codec) return 0; } +/* + * Reset the DSP + */ static int dsp_reset(struct hda_codec *codec) { unsigned int res; @@ -1088,6 +1147,9 @@ static int dsp_reset(struct hda_codec *codec) return 0; } +/* + * Convert chip address to DSP address + */ static unsigned int dsp_chip_to_dsp_addx(unsigned int chip_addx, bool *code, bool *yram) { @@ -1106,6 +1168,9 @@ static unsigned int dsp_chip_to_dsp_addx(unsigned int chip_addx, return (unsigned int)INVALID_CHIP_ADDRESS; } +/* + * Check if the DSP DMA is active + */ static bool dsp_is_dma_active(struct hda_codec *codec, unsigned int dma_chan) { unsigned int dma_chnlstart_reg; @@ -1226,6 +1291,9 @@ static int dsp_dma_setup_common(struct hda_codec *codec, return 0; } +/* + * Setup the DSP DMA per-transfer-specific registers + */ static int dsp_dma_setup(struct hda_codec *codec, unsigned int chip_addx, unsigned int count, @@ -1314,6 +1382,9 @@ static int dsp_dma_setup(struct hda_codec *codec, return 0; } +/* + * Start the DSP DMA + */ static int dsp_dma_start(struct hda_codec *codec, unsigned int dma_chan, bool ovly) { @@ -1347,6 +1418,9 @@ static int dsp_dma_start(struct hda_codec *codec, return status; } +/* + * Stop the DSP DMA + */ static int dsp_dma_stop(struct hda_codec *codec, unsigned int dma_chan, bool ovly) { @@ -1379,6 +1453,17 @@ static int dsp_dma_stop(struct hda_codec *codec, return status; } +/** + * Allocate router ports + * + * @codec: the HDA codec + * @num_chans: number of channels in the stream + * @ports_per_channel: number of ports per channel + * @start_device: start device + * @port_map: pointer to the port list to hold the allocated ports + * + * Returns zero or a negative error code. + */ static int dsp_allocate_router_ports(struct hda_codec *codec, unsigned int num_chans, unsigned int ports_per_channel, @@ -1417,6 +1502,9 @@ static int dsp_allocate_router_ports(struct hda_codec *codec, return (res < 0) ? res : 0; } +/* + * Free router ports + */ static int dsp_free_router_ports(struct hda_codec *codec) { int status = 0; @@ -1434,6 +1522,9 @@ static int dsp_free_router_ports(struct hda_codec *codec) return status; } +/* + * Allocate DSP ports for the download stream + */ static int dsp_allocate_ports(struct hda_codec *codec, unsigned int num_chans, unsigned int rate_multi, unsigned int *port_map) @@ -1455,22 +1546,6 @@ static int dsp_allocate_ports(struct hda_codec *codec, return status; } -static int dsp_free_ports(struct hda_codec *codec) -{ - int status; - - snd_printdd(KERN_INFO " dsp_free_ports() -- begin"); - - status = dsp_free_router_ports(codec); - if (status < 0) { - snd_printdd(KERN_ERR "free router ports fail"); - return status; - } - snd_printdd(KERN_INFO " dsp_free_ports() -- complete"); - - return status; -} - static int dsp_allocate_ports_format(struct hda_codec *codec, const unsigned short fmt, unsigned int *port_map) @@ -1494,6 +1569,25 @@ static int dsp_allocate_ports_format(struct hda_codec *codec, return status; } +/* + * free DSP ports + */ +static int dsp_free_ports(struct hda_codec *codec) +{ + int status; + + snd_printdd(KERN_INFO " dsp_free_ports() -- begin"); + + status = dsp_free_router_ports(codec); + if (status < 0) { + snd_printdd(KERN_ERR "free router ports fail"); + return status; + } + snd_printdd(KERN_INFO " dsp_free_ports() -- complete"); + + return status; +} + /* * HDA DMA engine stuffs for DSP code download */ @@ -1528,6 +1622,9 @@ static int dma_convert_to_hda_format( return 0; } +/* + * Reset DMA for DSP download + */ static int dma_reset(struct dma_engine *dma) { struct hda_codec *codec = dma->codec; @@ -1642,6 +1739,11 @@ static const struct dsp_image_seg *get_next_seg_ptr( */ #define INVALID_DMA_CHANNEL (~0UL) +/* + * Program a list of address/data pairs via the ChipIO widget. + * The segment data is in the format of successive pairs of words. + * These are repeated as indicated by the segment's count field. + */ static int dspxfr_hci_write(struct hda_codec *codec, const struct dsp_image_seg *fls) { @@ -1668,6 +1770,21 @@ static int dspxfr_hci_write(struct hda_codec *codec, return 0; } +/** + * Write a block of data into DSP code or data RAM using pre-allocated + * DMA engine. + * + * @codec: the HDA codec + * @fls: pointer to a fast load image + * @reloc: Relocation address for loading single-segment overlays, or 0 for + * no relocation + * @dma_engine: pointer to DMA engine to be used for DSP download + * @dma_chan: The number of DMA channels used for DSP download + * @port_map_mask: port mapping + * @ovly: TRUE if overlay format is required + * + * Returns zero or a negative error code. + */ static int dspxfr_one_seg(struct hda_codec *codec, const struct dsp_image_seg *fls, unsigned int reloc, @@ -1836,6 +1953,18 @@ static int dspxfr_one_seg(struct hda_codec *codec, return status; } +/** + * Write the entire DSP image of a DSP code/data overlay to DSP memories + * + * @codec: the HDA codec + * @fls_data: pointer to a fast load image + * @reloc: Relocation address for loading single-segment overlays, or 0 for + * no relocation + * @format: format of the stream used for DSP download + * @ovly: TRUE if overlay format is required + * + * Returns zero or a negative error code. + */ static int dspxfr_image(struct hda_codec *codec, const struct dsp_image_seg *fls_data, unsigned int reloc, struct hda_stream_format *format, @@ -1970,6 +2099,23 @@ static void dspload_post_setup(struct hda_codec *codec) chipio_write(codec, XRAM_XRAM_INST_OFFSET(0x29), 0x00000002); } +/** + * Download DSP from a DSP Image Fast Load structure. This structure is a + * linear, non-constant sized element array of structures, each of which + * contain the count of the data to be loaded, the data itself, and the + * corresponding starting chip address of the starting data location. + * + * @codec: the HDA codec + * @fls: pointer to a fast load image + * @ovly: TRUE if overlay format is required + * @reloc: Relocation address for loading single-segment overlays, or 0 for + * no relocation + * @autostart: TRUE if DSP starts after loading; ignored if ovly is TRUE + * @router_chans: number of audio router channels to be allocated (0 means use + * internal defaults; max is 32) + * + * Returns zero or a negative error code. + */ static int dspload_image(struct hda_codec *codec, const struct dsp_image_seg *fls, bool ovly, -- cgit v1.2.3 From a3af4807fa6766d0772396989cb6bcf93847bc53 Mon Sep 17 00:00:00 2001 From: Ian Minett Date: Thu, 20 Sep 2012 20:29:19 -0700 Subject: ALSA: hda - Change return value for load_dsp_prepare() to -ENOSYS Signed-off-by: Ian Minett Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_codec.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index c218bf41261a..c9f53bd587b0 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -1165,7 +1165,7 @@ snd_hda_codec_load_dsp_prepare(struct hda_codec *codec, unsigned int format, unsigned int size, struct snd_dma_buffer *bufp) { - return 0; + return -ENOSYS; } static inline void snd_hda_codec_load_dsp_trigger(struct hda_codec *codec, bool start) {} -- cgit v1.2.3 From 4861af8075d91feb9df0e2f6539dad20debbeb67 Mon Sep 17 00:00:00 2001 From: Ian Minett Date: Thu, 20 Sep 2012 20:29:20 -0700 Subject: ALSA: hda - Update chipio functions and DSP write wait timeout Tidy up and condense chipio_write_address|addx() functions. Improve dspio_write_wait() to use jiffies for timeout calc. Signed-off-by: Ian Minett Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_ca0132.c | 52 ++++++++++++++------------------------------ 1 file changed, 16 insertions(+), 36 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index a7b216ea9df3..7a0425ff8b7b 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -460,8 +460,12 @@ static int chipio_send(struct hda_codec *codec, static int chipio_write_address(struct hda_codec *codec, unsigned int chip_addx) { + struct ca0132_spec *spec = codec->spec; int res; + if (spec->curr_chip_addx == chip_addx) + return 0; + /* send low 16 bits of the address */ res = chipio_send(codec, VENDOR_CHIPIO_ADDRESS_LOW, chip_addx & 0xffff); @@ -472,37 +476,14 @@ static int chipio_write_address(struct hda_codec *codec, chip_addx >> 16); } - return res; -} - -static int chipio_write_addx(struct hda_codec *codec, u32 chip_addx) -{ - struct ca0132_spec *spec = codec->spec; - int status; - - if (spec->curr_chip_addx == chip_addx) - return 0; - - /* send low 16 bits of the address */ - status = chipio_send(codec, VENDOR_CHIPIO_ADDRESS_LOW, - chip_addx & 0xffff); - - if (status < 0) - return status; - - /* send high 16 bits of the address */ - status = chipio_send(codec, VENDOR_CHIPIO_ADDRESS_HIGH, - chip_addx >> 16); - - spec->curr_chip_addx = (status < 0) ? ~0UL : chip_addx; + spec->curr_chip_addx = (res < 0) ? ~0UL : chip_addx; - return status; + return res; } /* * Write data through the vendor widget -- NOT protected by the Mutex! */ - static int chipio_write_data(struct hda_codec *codec, unsigned int data) { int res; @@ -604,7 +585,7 @@ static int chipio_write_multiple(struct hda_codec *codec, int status; mutex_lock(&spec->chipio_mutex); - status = chipio_write_addx(codec, chip_addx); + status = chipio_write_address(codec, chip_addx); if (status < 0) goto error; @@ -742,18 +723,17 @@ static int dspio_send(struct hda_codec *codec, unsigned int reg, */ static void dspio_write_wait(struct hda_codec *codec) { - int cur_val, prv_val; - int retry = 50; + int status; + unsigned long timeout = jiffies + msecs_to_jiffies(1000); - cur_val = 0; do { - prv_val = cur_val; - msleep(20); - dspio_send(codec, VENDOR_DSPIO_SCP_POST_COUNT_QUERY, 1); - dspio_send(codec, VENDOR_DSPIO_STATUS, 0); - cur_val = snd_hda_codec_read(codec, WIDGET_DSP_CTRL, 0, - VENDOR_DSPIO_SCP_READ_COUNT, 0); - } while (cur_val && (cur_val == prv_val) && --retry); + status = snd_hda_codec_read(codec, WIDGET_DSP_CTRL, 0, + VENDOR_DSPIO_STATUS, 0); + if ((status == VENDOR_STATUS_DSPIO_OK) || + (status == VENDOR_STATUS_DSPIO_SCP_RESPONSE_QUEUE_EMPTY)) + break; + msleep(1); + } while (time_before(jiffies, timeout)); } /* -- cgit v1.2.3 From e97249dd6d70b657a4c6bc47e436b2b981031144 Mon Sep 17 00:00:00 2001 From: Ian Minett Date: Thu, 20 Sep 2012 20:29:21 -0700 Subject: ALSA: hda - Remove unnecessary struct hda_stream_format from CA0132 Signed-off-by: Ian Minett Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_ca0132.c | 44 ++++++++++++++++++++------------------------ 1 file changed, 20 insertions(+), 24 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index 7a0425ff8b7b..5c6a0569c611 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -356,13 +356,6 @@ enum dsp_download_state { DSP_DOWNLOADED = 2 }; -struct hda_stream_format { - unsigned int sample_rate; - unsigned short valid_bits_per_sample; - unsigned short container_size; - unsigned short number_channels; -}; - /* retrieve parameters from hda format */ #define get_hdafmt_chs(fmt) (fmt & 0xf) #define get_hdafmt_bits(fmt) ((fmt >> 4) & 0x7) @@ -1585,16 +1578,17 @@ enum dma_state { }; static int dma_convert_to_hda_format( - struct hda_stream_format *stream_format, + unsigned int sample_rate, + unsigned short channels, unsigned short *hda_format) { unsigned int format_val; format_val = snd_hda_calc_stream_format( - stream_format->sample_rate, - stream_format->number_channels, + sample_rate, + channels, SNDRV_PCM_FORMAT_S32_LE, - stream_format->container_size, 0); + 32, 0); if (hda_format) *hda_format = (unsigned short)format_val; @@ -1940,14 +1934,17 @@ static int dspxfr_one_seg(struct hda_codec *codec, * @fls_data: pointer to a fast load image * @reloc: Relocation address for loading single-segment overlays, or 0 for * no relocation - * @format: format of the stream used for DSP download + * @sample_rate: sampling rate of the stream used for DSP download + * @number_channels: channels of the stream used for DSP download * @ovly: TRUE if overlay format is required * * Returns zero or a negative error code. */ static int dspxfr_image(struct hda_codec *codec, const struct dsp_image_seg *fls_data, - unsigned int reloc, struct hda_stream_format *format, + unsigned int reloc, + unsigned int sample_rate, + unsigned short channels, bool ovly) { struct ca0132_spec *spec = codec->spec; @@ -1976,7 +1973,7 @@ static int dspxfr_image(struct hda_codec *codec, } dma_engine->codec = codec; - dma_convert_to_hda_format(format, &hda_format); + dma_convert_to_hda_format(sample_rate, channels, &hda_format); dma_engine->m_converter_format = hda_format; dma_engine->buf_size = (ovly ? DSP_DMA_WRITE_BUFLEN_OVLY : DSP_DMA_WRITE_BUFLEN_INIT) * 2; @@ -2104,7 +2101,8 @@ static int dspload_image(struct hda_codec *codec, int router_chans) { int status = 0; - struct hda_stream_format stream_format; + unsigned int sample_rate; + unsigned short channels; snd_printdd(KERN_INFO "---- dspload_image begin ------"); if (router_chans == 0) { @@ -2114,17 +2112,14 @@ static int dspload_image(struct hda_codec *codec, router_chans = DMA_OVERLAY_FRAME_SIZE_NWORDS; } - stream_format.sample_rate = 48000; - stream_format.number_channels = (unsigned short)router_chans; + sample_rate = 48000; + channels = (unsigned short)router_chans; - while (stream_format.number_channels > 16) { - stream_format.sample_rate *= 2; - stream_format.number_channels /= 2; + while (channels > 16) { + sample_rate *= 2; + channels /= 2; } - stream_format.container_size = 32; - stream_format.valid_bits_per_sample = 32; - do { snd_printdd(KERN_INFO "Ready to program DMA"); if (!ovly) @@ -2134,7 +2129,8 @@ static int dspload_image(struct hda_codec *codec, break; snd_printdd(KERN_INFO "dsp_reset() complete"); - status = dspxfr_image(codec, fls, reloc, &stream_format, ovly); + status = dspxfr_image(codec, fls, reloc, sample_rate, channels, + ovly); if (status < 0) break; -- cgit v1.2.3 From 063bca096829e2fdfc2bde7b6b2a7453bf9b7218 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 21 Sep 2012 13:44:34 +0200 Subject: ALSA: hda - Fix NULL dereference in error path of patch_ca0132.c Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_ca0132.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index 5c6a0569c611..03f57c98b54e 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -1960,10 +1960,8 @@ static int dspxfr_image(struct hda_codec *codec, return -EINVAL; dma_engine = kzalloc(sizeof(*dma_engine), GFP_KERNEL); - if (!dma_engine) { - status = -ENOMEM; - goto exit; - } + if (!dma_engine) + return -ENOMEM; memset((void *)dma_engine, 0, sizeof(*dma_engine)); dma_engine->dmab = kzalloc(sizeof(*dma_engine->dmab), GFP_KERNEL); -- cgit v1.2.3 From cdc83c59e59d57b250be46c4b9d31e3b2b5ae589 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 21 Sep 2012 14:01:19 +0200 Subject: ALSA: hda - Remove superfluous zero-clear memset in patch_ca0132.c Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_ca0132.c | 1 - 1 file changed, 1 deletion(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index 03f57c98b54e..2fd3121afb10 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -1962,7 +1962,6 @@ static int dspxfr_image(struct hda_codec *codec, dma_engine = kzalloc(sizeof(*dma_engine), GFP_KERNEL); if (!dma_engine) return -ENOMEM; - memset((void *)dma_engine, 0, sizeof(*dma_engine)); dma_engine->dmab = kzalloc(sizeof(*dma_engine->dmab), GFP_KERNEL); if (!dma_engine->dmab) { -- cgit v1.2.3 From f4f0a8c4783a0258f2d0020a67fd619ce41a02a3 Mon Sep 17 00:00:00 2001 From: David Henningsson Date: Tue, 8 Jan 2013 15:01:18 +0100 Subject: ALSA: hda - print power state for AFG node in proc file It seems useful, and power states are required for AFG nodes, so I see no reason not to print it. As a bonus, also print the AFG nid. Signed-off-by: David Henningsson Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_proc.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_proc.c b/sound/pci/hda/hda_proc.c index 045e5d32f5de..d6c8416eff67 100644 --- a/sound/pci/hda/hda_proc.c +++ b/sound/pci/hda/hda_proc.c @@ -603,6 +603,8 @@ static void print_codec_info(struct snd_info_entry *entry, print_amp_caps(buffer, codec, codec->afg, HDA_INPUT); snd_iprintf(buffer, "Default Amp-Out caps: "); print_amp_caps(buffer, codec, codec->afg, HDA_OUTPUT); + snd_iprintf(buffer, "State of AFG node 0x%02x:\n", codec->afg); + print_power_state(buffer, codec, codec->afg); nodes = snd_hda_get_sub_nodes(codec, codec->afg, &nid); if (! nid || nodes < 0) { -- cgit v1.2.3 From 6ab317419c62850a71e2adfd1573e5ee87d8774f Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 9 Jan 2013 11:15:13 +0100 Subject: ALSA: hda - Allow power_save_controller option override DCAPS Change the power_save_controller option to bint from bool so that user can override the runtime PM capability bit and force to enable or disable the runtime PM. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_intel.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index cca87277baf0..988323577834 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -134,8 +134,8 @@ MODULE_PARM_DESC(power_save, "Automatic power-saving timeout " * this may give more power-saving, but will take longer time to * wake up. */ -static bool power_save_controller = 1; -module_param(power_save_controller, bool, 0644); +static int power_save_controller = -1; +module_param(power_save_controller, bint, 0644); MODULE_PARM_DESC(power_save_controller, "Reset controller in power save mode."); #endif /* CONFIG_PM */ @@ -2711,6 +2711,8 @@ static int azx_runtime_idle(struct device *dev) struct snd_card *card = dev_get_drvdata(dev); struct azx *chip = card->private_data; + if (power_save_controller > 0) + return 0; if (!power_save_controller || !(chip->driver_caps & AZX_DCAPS_PM_RUNTIME)) return -EBUSY; -- cgit v1.2.3 From 6a84c305f03425bab09ffedb71be942ebc22454d Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 5 Dec 2012 14:08:45 +0100 Subject: ALSA: hda/realtek - Simplify alc_auto_is_dac_reachable() Use the helper function snd_hda_get_conn_index() instead of open codes. This also improves the detection of some routes to DAC on ALC260 (although the difference doesn't influence on the end results of the mapping). Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index f5196277b6e9..ca077402d6c0 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -2938,19 +2938,9 @@ static hda_nid_t alc_auto_look_for_dac(struct hda_codec *codec, hda_nid_t pin) static bool alc_auto_is_dac_reachable(struct hda_codec *codec, hda_nid_t pin, hda_nid_t dac) { - hda_nid_t srcs[5]; - int i, num; - if (!pin || !dac) return false; - pin = alc_go_down_to_selector(codec, pin); - num = snd_hda_get_connections(codec, pin, srcs, ARRAY_SIZE(srcs)); - for (i = 0; i < num; i++) { - hda_nid_t nid = alc_auto_mix_to_dac(codec, srcs[i]); - if (nid == dac) - return true; - } - return false; + return snd_hda_get_conn_index(codec, pin, dac, true) >= 0; } static hda_nid_t get_dac_if_single(struct hda_codec *codec, hda_nid_t pin) -- cgit v1.2.3 From 463419de865622e4b13e977e1536375ab897a53f Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 5 Dec 2012 14:17:37 +0100 Subject: ALSA: hda/realtek - List up all available DACs In the probing phase, create a list of all available DACs in the codec and use it for checking the single DAC connections. This list will be used in more other places in the later commits, too. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 76 +++++++++++++++++++++++++++---------------- 1 file changed, 48 insertions(+), 28 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index ca077402d6c0..8b768a566be0 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -172,6 +172,10 @@ struct alc_spec { int int_mic_idx, ext_mic_idx, dock_mic_idx; /* for auto-mic */ hda_nid_t inv_dmic_pin; + /* DAC list */ + int num_all_dacs; + hda_nid_t all_dacs[16]; + /* hooks */ void (*init_hook)(struct hda_codec *codec); #ifdef CONFIG_PM @@ -2916,48 +2920,42 @@ static bool alc_is_dac_already_used(struct hda_codec *codec, hda_nid_t nid) return false; } +/* check whether the DAC is reachable from the pin */ +static bool alc_auto_is_dac_reachable(struct hda_codec *codec, + hda_nid_t pin, hda_nid_t dac) +{ + if (!pin || !dac) + return false; + return snd_hda_get_conn_index(codec, pin, dac, true) >= 0; +} + /* look for an empty DAC slot */ static hda_nid_t alc_auto_look_for_dac(struct hda_codec *codec, hda_nid_t pin) { - hda_nid_t srcs[5]; - int i, num; + struct alc_spec *spec = codec->spec; + int i; - pin = alc_go_down_to_selector(codec, pin); - num = snd_hda_get_connections(codec, pin, srcs, ARRAY_SIZE(srcs)); - for (i = 0; i < num; i++) { - hda_nid_t nid = alc_auto_mix_to_dac(codec, srcs[i]); - if (!nid) + for (i = 0; i < spec->num_all_dacs; i++) { + hda_nid_t nid = spec->all_dacs[i]; + if (!nid || alc_is_dac_already_used(codec, nid)) continue; - if (!alc_is_dac_already_used(codec, nid)) + if (alc_auto_is_dac_reachable(codec, pin, nid)) return nid; } return 0; } -/* check whether the DAC is reachable from the pin */ -static bool alc_auto_is_dac_reachable(struct hda_codec *codec, - hda_nid_t pin, hda_nid_t dac) -{ - if (!pin || !dac) - return false; - return snd_hda_get_conn_index(codec, pin, dac, true) >= 0; -} - static hda_nid_t get_dac_if_single(struct hda_codec *codec, hda_nid_t pin) { struct alc_spec *spec = codec->spec; - hda_nid_t sel = alc_go_down_to_selector(codec, pin); - hda_nid_t nid, nid_found, srcs[5]; - int i, num = snd_hda_get_connections(codec, sel, srcs, - ARRAY_SIZE(srcs)); - if (num == 1) - return alc_auto_look_for_dac(codec, pin); - nid_found = 0; - for (i = 0; i < num; i++) { - if (srcs[i] == spec->mixer_nid) + int i; + hda_nid_t nid_found = 0; + + for (i = 0; i < spec->num_all_dacs; i++) { + hda_nid_t nid = spec->all_dacs[i]; + if (!nid || alc_is_dac_already_used(codec, nid)) continue; - nid = alc_auto_mix_to_dac(codec, srcs[i]); - if (nid && !alc_is_dac_already_used(codec, nid)) { + if (alc_auto_is_dac_reachable(codec, pin, nid)) { if (nid_found) return 0; nid_found = nid; @@ -3308,6 +3306,26 @@ static void debug_show_configs(struct alc_spec *spec, struct auto_pin_cfg *cfg) spec->multiout.extra_out_nid[3]); } +/* find all available DACs of the codec */ +static void alc_fill_all_nids(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + int i; + hda_nid_t nid = codec->start_nid; + + spec->num_all_dacs = 0; + memset(spec->all_dacs, 0, sizeof(spec->all_dacs)); + for (i = 0; i < codec->num_nodes; i++, nid++) { + if (get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_AUD_OUT) + continue; + if (spec->num_all_dacs >= ARRAY_SIZE(spec->all_dacs)) { + snd_printk(KERN_ERR "hda: Too many DACs!\n"); + break; + } + spec->all_dacs[spec->num_all_dacs++] = nid; + } +} + static int alc_auto_fill_dac_nids(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; @@ -3319,6 +3337,8 @@ static int alc_auto_fill_dac_nids(struct hda_codec *codec) bool best_wired = true, best_mio = true; bool hp_spk_swapped = false; + alc_fill_all_nids(codec); + best_cfg = kmalloc(sizeof(*best_cfg), GFP_KERNEL); if (!best_cfg) return -ENOMEM; -- cgit v1.2.3 From 30dcd3b40409a4db272998b0cba1b9e80c15b1c8 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 6 Dec 2012 15:45:38 +0100 Subject: ALSA: hda/realtek - Add output path parser Add the output path parser to Realtek codec driver as we already have in patch_via.c. The nid_path struct represents the complete output path from a DAC to a pin. The alc_spec contains an array of these paths, and a new path is added at each time when a new DAC is assigned. So far, this path list is used only in limited codes: namely in this patch, only alc_is_dac_already_used() checks the list instead of dac arrays in all possible outputs. In the later development, the path list will be referred from more places, such as the mixer control assignment / check, the mute/unmute of active routes, etc. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 139 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 128 insertions(+), 11 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 8b768a566be0..1f178d6da9d4 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -99,6 +99,23 @@ enum { #define ALC_FIXUP_ACT_BUILD HDA_FIXUP_ACT_BUILD +#define MAX_NID_PATH_DEPTH 5 + +/* output-path: DAC -> ... -> pin + * idx[] contains the source index number of the next widget; + * e.g. idx[0] is the index of the DAC selected by path[1] widget + * multi[] indicates whether it's a selector widget with multi-connectors + * (i.e. the connection selection is mandatory) + * vol_ctl and mute_ctl contains the NIDs for the assigned mixers + */ +struct nid_path { + int depth; + hda_nid_t path[MAX_NID_PATH_DEPTH]; + unsigned char idx[MAX_NID_PATH_DEPTH]; + unsigned char multi[MAX_NID_PATH_DEPTH]; + unsigned int ctls[2]; /* 0 = volume, 1 = mute */ +}; + struct alc_spec { struct hda_gen_spec gen; @@ -176,6 +193,9 @@ struct alc_spec { int num_all_dacs; hda_nid_t all_dacs[16]; + /* output paths */ + struct snd_array out_path; + /* hooks */ void (*init_hook)(struct hda_codec *codec); #ifdef CONFIG_PM @@ -2407,6 +2427,7 @@ static void alc_free(struct hda_codec *codec) alc_free_kctls(codec); alc_free_bind_ctls(codec); + snd_array_free(&spec->out_path); snd_hda_gen_free(&spec->gen); kfree(spec); snd_hda_detach_beep_device(codec); @@ -2906,15 +2927,10 @@ static bool alc_is_dac_already_used(struct hda_codec *codec, hda_nid_t nid) { struct alc_spec *spec = codec->spec; int i; - if (found_in_nid_list(nid, spec->multiout.dac_nids, - ARRAY_SIZE(spec->private_dac_nids)) || - found_in_nid_list(nid, spec->multiout.hp_out_nid, - ARRAY_SIZE(spec->multiout.hp_out_nid)) || - found_in_nid_list(nid, spec->multiout.extra_out_nid, - ARRAY_SIZE(spec->multiout.extra_out_nid))) - return true; - for (i = 0; i < spec->multi_ios; i++) { - if (spec->multi_io[i].dac == nid) + + for (i = 0; i < spec->out_path.used; i++) { + struct nid_path *path = snd_array_elem(&spec->out_path, i); + if (path->path[0] == nid) return true; } return false; @@ -2945,6 +2961,75 @@ static hda_nid_t alc_auto_look_for_dac(struct hda_codec *codec, hda_nid_t pin) return 0; } +/* called recursively */ +static bool __parse_output_path(struct hda_codec *codec, hda_nid_t nid, + hda_nid_t target_dac, int with_aa_mix, + struct nid_path *path, int depth) +{ + struct alc_spec *spec = codec->spec; + hda_nid_t conn[8]; + int i, nums; + + if (nid == spec->mixer_nid) { + if (!with_aa_mix) + return false; + with_aa_mix = 2; /* mark aa-mix is included */ + } + + nums = snd_hda_get_connections(codec, nid, conn, ARRAY_SIZE(conn)); + for (i = 0; i < nums; i++) { + if (get_wcaps_type(get_wcaps(codec, conn[i])) != AC_WID_AUD_OUT) + continue; + if (conn[i] == target_dac || + (!target_dac && !alc_is_dac_already_used(codec, conn[i]))) { + /* aa-mix is requested but not included? */ + if (!(spec->mixer_nid && with_aa_mix == 1)) + goto found; + } + } + if (depth >= MAX_NID_PATH_DEPTH) + return false; + for (i = 0; i < nums; i++) { + unsigned int type; + type = get_wcaps_type(get_wcaps(codec, conn[i])); + if (type == AC_WID_AUD_OUT) + continue; + if (__parse_output_path(codec, conn[i], target_dac, + with_aa_mix, path, depth + 1)) + goto found; + } + return false; + + found: + path->path[path->depth] = conn[i]; + path->idx[path->depth] = i; + if (nums > 1 && get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_AUD_MIX) + path->multi[path->depth] = 1; + path->depth++; + return true; +} + +/* parse the output path from the given nid to the target DAC; + * when target_dac is 0, try to find an empty DAC; + * when with_aa_mix is 0, paths with spec->mixer_nid are excluded + */ +static bool parse_output_path(struct hda_codec *codec, hda_nid_t nid, + hda_nid_t target_dac, int with_aa_mix, + struct nid_path *path) +{ + if (__parse_output_path(codec, nid, target_dac, with_aa_mix, path, 1)) { + path->path[path->depth] = nid; + path->depth++; +#if 0 + snd_printdd("output-path: depth=%d, %02x/%02x/%02x/%02x/%02x\n", + path->depth, path->path[0], path->path[1], + path->path[2], path->path[3], path->path[4]); +#endif + return true; + } + return false; +} + static hda_nid_t get_dac_if_single(struct hda_codec *codec, hda_nid_t pin) { struct alc_spec *spec = codec->spec; @@ -3016,6 +3101,23 @@ static hda_nid_t alc_look_for_out_mute_nid(struct hda_codec *codec, static hda_nid_t alc_look_for_out_vol_nid(struct hda_codec *codec, hda_nid_t pin, hda_nid_t dac); +static bool add_new_out_path(struct hda_codec *codec, hda_nid_t pin, + hda_nid_t dac) +{ + struct alc_spec *spec = codec->spec; + struct nid_path *path; + + path = snd_array_new(&spec->out_path); + if (!path) + return false; + memset(path, 0, sizeof(*path)); + if (parse_output_path(codec, pin, dac, 0, path)) + return true; + /* push back */ + spec->out_path.used--; + return false; +} + static int eval_shared_vol_badness(struct hda_codec *codec, hda_nid_t pin, hda_nid_t dac) { @@ -3127,6 +3229,8 @@ static int alc_auto_fill_dacs(struct hda_codec *codec, int num_outs, else badness += bad->no_dac; } + if (!add_new_out_path(codec, pin, dac)) + dac = dacs[i] = 0; if (dac) badness += eval_shared_vol_badness(codec, pin, dac); } @@ -3144,11 +3248,16 @@ static bool alc_map_singles(struct hda_codec *codec, int outs, int i; bool found = false; for (i = 0; i < outs; i++) { + hda_nid_t dac; if (dacs[i]) continue; - dacs[i] = get_dac_if_single(codec, pins[i]); - if (dacs[i]) + dac = get_dac_if_single(codec, pins[i]); + if (!dac) + continue; + if (add_new_out_path(codec, pins[i], dac)) { + dacs[i] = dac; found = true; + } } return found; } @@ -3169,6 +3278,7 @@ static int fill_and_eval_dacs(struct hda_codec *codec, memset(spec->multiout.hp_out_nid, 0, sizeof(spec->multiout.hp_out_nid)); memset(spec->multiout.extra_out_nid, 0, sizeof(spec->multiout.extra_out_nid)); spec->multi_ios = 0; + snd_array_free(&spec->out_path); clear_vol_marks(codec); badness = 0; @@ -3882,6 +3992,10 @@ static int alc_auto_fill_multi_ios(struct hda_codec *codec, badness++; continue; } + if (!add_new_out_path(codec, nid, dac)) { + badness++; + continue; + } spec->multi_io[spec->multi_ios].pin = nid; spec->multi_io[spec->multi_ios].dac = dac; spec->multi_ios++; @@ -3899,6 +4013,8 @@ static int alc_auto_fill_multi_ios(struct hda_codec *codec, return badness; /* no badness if nothing found */ } if (!hardwired && spec->multi_ios < 2) { + /* cancel newly assigned paths */ + spec->out_path.used -= spec->multi_ios - old_pins; spec->multi_ios = old_pins; return badness; } @@ -4388,6 +4504,7 @@ static int alc_alloc_spec(struct hda_codec *codec, hda_nid_t mixer_nid) snd_hda_gen_init(&spec->gen); snd_array_init(&spec->kctls, sizeof(struct snd_kcontrol_new), 32); snd_array_init(&spec->bind_ctls, sizeof(struct hda_bind_ctls *), 8); + snd_array_init(&spec->out_path, sizeof(struct nid_path), 8); err = alc_codec_rename_from_preset(codec); if (err < 0) { -- cgit v1.2.3 From ba8111276f2cc10b9851613bc8300cabda3c7e0d Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 6 Dec 2012 18:06:23 +0100 Subject: ALSA: hda/realtek - Manage mixer controls in out_path list As we parse the output paths more precisely now, we can use this path list for parsing the widgets for volume and mute mixer controls. The spec->vol_ctls[] and sw_ctls[] bitmasks are replaced with the ctls[] in each output path instance. Interestingly, this move alone automagically fixes some bugs that the conflicting volume or mute NIDs weren't properly detected. Also, by parsing the whole path, there are more chances to get a free widget for volume/mute controls. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 230 ++++++++++++++++++++++++------------------ 1 file changed, 131 insertions(+), 99 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 1f178d6da9d4..cacfbc05786a 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -116,6 +116,8 @@ struct nid_path { unsigned int ctls[2]; /* 0 = volume, 1 = mute */ }; +enum { NID_PATH_VOL_CTL = 0, NID_PATH_MUTE_CTL = 1 }; + struct alc_spec { struct hda_gen_spec gen; @@ -150,8 +152,6 @@ struct alc_spec { const hda_nid_t *capsrc_nids; hda_nid_t dig_in_nid; /* digital-in NID; optional */ hda_nid_t mixer_nid; /* analog-mixer NID */ - DECLARE_BITMAP(vol_ctls, MAX_VOL_NIDS << 1); - DECLARE_BITMAP(sw_ctls, MAX_VOL_NIDS << 1); /* capture setup for dynamic dual-adc switch */ hda_nid_t cur_adc; @@ -2886,22 +2886,6 @@ static hda_nid_t alc_go_down_to_selector(struct hda_codec *codec, hda_nid_t pin) return srcs[0]; } -/* get MIX nid connected to the given pin targeted to DAC */ -static hda_nid_t alc_auto_dac_to_mix(struct hda_codec *codec, hda_nid_t pin, - hda_nid_t dac) -{ - hda_nid_t mix[5]; - int i, num; - - pin = alc_go_down_to_selector(codec, pin); - num = snd_hda_get_connections(codec, pin, mix, ARRAY_SIZE(mix)); - for (i = 0; i < num; i++) { - if (alc_auto_mix_to_dac(codec, mix[i]) == dac) - return mix[i]; - } - return 0; -} - /* select the connection from pin to DAC if needed */ static int alc_auto_select_dac(struct hda_codec *codec, hda_nid_t pin, hda_nid_t dac) @@ -3049,29 +3033,28 @@ static hda_nid_t get_dac_if_single(struct hda_codec *codec, hda_nid_t pin) return nid_found; } -/* mark up volume and mute control NIDs: used during badness parsing and - * at creating actual controls - */ -static inline unsigned int get_ctl_pos(unsigned int data) +static bool is_ctl_used(struct hda_codec *codec, unsigned int val, int type) { - hda_nid_t nid = get_amp_nid_(data); - unsigned int dir; - if (snd_BUG_ON(nid >= MAX_VOL_NIDS)) - return 0; - dir = get_amp_direction_(data); - return (nid << 1) | dir; -} + struct alc_spec *spec = codec->spec; + int i; -#define is_ctl_used(bits, data) \ - test_bit(get_ctl_pos(data), bits) -#define mark_ctl_usage(bits, data) \ - set_bit(get_ctl_pos(data), bits) + for (i = 0; i < spec->out_path.used; i++) { + struct nid_path *path = snd_array_elem(&spec->out_path, i); + if (path->ctls[type] == val) + return true; + } + return false; +} static void clear_vol_marks(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - memset(spec->vol_ctls, 0, sizeof(spec->vol_ctls)); - memset(spec->sw_ctls, 0, sizeof(spec->sw_ctls)); + int i; + + for (i = 0; i < spec->out_path.used; i++) { + struct nid_path *path = snd_array_elem(&spec->out_path, i); + path->ctls[0] = path->ctls[1] = 0; + } } /* badness definition */ @@ -3097,9 +3080,9 @@ enum { }; static hda_nid_t alc_look_for_out_mute_nid(struct hda_codec *codec, - hda_nid_t pin, hda_nid_t dac); + struct nid_path *path); static hda_nid_t alc_look_for_out_vol_nid(struct hda_codec *codec, - hda_nid_t pin, hda_nid_t dac); + struct nid_path *path); static bool add_new_out_path(struct hda_codec *codec, hda_nid_t pin, hda_nid_t dac) @@ -3118,34 +3101,56 @@ static bool add_new_out_path(struct hda_codec *codec, hda_nid_t pin, return false; } +/* get the path pointing from the given dac to pin; + * passing 0 to either @pin or @dac behaves as a wildcard + */ +static struct nid_path *get_out_path(struct hda_codec *codec, hda_nid_t pin, + hda_nid_t dac) +{ + struct alc_spec *spec = codec->spec; + int i; + + for (i = 0; i < spec->out_path.used; i++) { + struct nid_path *path = snd_array_elem(&spec->out_path, i); + if (path->depth <= 0) + continue; + if ((!dac || path->path[0] == dac) && + (!pin || path->path[path->depth - 1] == pin)) + return path; + } + return NULL; +} + static int eval_shared_vol_badness(struct hda_codec *codec, hda_nid_t pin, hda_nid_t dac) { - struct alc_spec *spec = codec->spec; + struct nid_path *path = get_out_path(codec, pin, dac); hda_nid_t nid; unsigned int val; int badness = 0; - nid = alc_look_for_out_vol_nid(codec, pin, dac); + if (!path) + return BAD_SHARED_VOL * 2; + nid = alc_look_for_out_vol_nid(codec, path); if (nid) { val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT); - if (is_ctl_used(spec->vol_ctls, nid)) + if (is_ctl_used(codec, val, NID_PATH_VOL_CTL)) badness += BAD_SHARED_VOL; else - mark_ctl_usage(spec->vol_ctls, val); + path->ctls[NID_PATH_VOL_CTL] = val; } else badness += BAD_SHARED_VOL; - nid = alc_look_for_out_mute_nid(codec, pin, dac); + nid = alc_look_for_out_mute_nid(codec, path); if (nid) { unsigned int wid_type = get_wcaps_type(get_wcaps(codec, nid)); if (wid_type == AC_WID_PIN || wid_type == AC_WID_AUD_OUT) val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT); else val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT); - if (is_ctl_used(spec->sw_ctls, val)) + if (is_ctl_used(codec, val, NID_PATH_MUTE_CTL)) badness += BAD_SHARED_VOL; else - mark_ctl_usage(spec->sw_ctls, val); + path->ctls[NID_PATH_MUTE_CTL] = val; } else badness += BAD_SHARED_VOL; return badness; @@ -3279,7 +3284,6 @@ static int fill_and_eval_dacs(struct hda_codec *codec, memset(spec->multiout.extra_out_nid, 0, sizeof(spec->multiout.extra_out_nid)); spec->multi_ios = 0; snd_array_free(&spec->out_path); - clear_vol_marks(codec); badness = 0; /* fill hard-wired DACs first */ @@ -3521,10 +3525,13 @@ static int alc_auto_fill_dac_nids(struct hda_codec *codec) cfg->line_out_type, best_wired, best_mio); debug_show_configs(spec, cfg); - if (cfg->line_out_pins[0]) - spec->vmaster_nid = - alc_look_for_out_vol_nid(codec, cfg->line_out_pins[0], - spec->multiout.dac_nids[0]); + if (cfg->line_out_pins[0]) { + struct nid_path *path = get_out_path(codec, + cfg->line_out_pins[0], + spec->multiout.dac_nids[0]); + if (path) + spec->vmaster_nid = alc_look_for_out_vol_nid(codec, path); + } /* clear the bitmap flags for creating controls */ clear_vol_marks(codec); @@ -3533,43 +3540,43 @@ static int alc_auto_fill_dac_nids(struct hda_codec *codec) } static int alc_auto_add_vol_ctl(struct hda_codec *codec, - const char *pfx, int cidx, - hda_nid_t nid, unsigned int chs) + const char *pfx, int cidx, + hda_nid_t nid, unsigned int chs, + struct nid_path *path) { - struct alc_spec *spec = codec->spec; unsigned int val; - if (!nid) + if (!nid || !path) return 0; val = HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT); - if (is_ctl_used(spec->vol_ctls, val) && chs != 2) /* exclude LFE */ + if (is_ctl_used(codec, val, NID_PATH_VOL_CTL) && chs != 2) /* exclude LFE */ return 0; - mark_ctl_usage(spec->vol_ctls, val); + path->ctls[NID_PATH_VOL_CTL] = val; return __add_pb_vol_ctrl(codec->spec, ALC_CTL_WIDGET_VOL, pfx, cidx, val); } static int alc_auto_add_stereo_vol(struct hda_codec *codec, const char *pfx, int cidx, - hda_nid_t nid) + hda_nid_t nid, struct nid_path *path) { int chs = 1; if (get_wcaps(codec, nid) & AC_WCAP_STEREO) chs = 3; - return alc_auto_add_vol_ctl(codec, pfx, cidx, nid, chs); + return alc_auto_add_vol_ctl(codec, pfx, cidx, nid, chs, path); } /* create a mute-switch for the given mixer widget; * if it has multiple sources (e.g. DAC and loopback), create a bind-mute */ static int alc_auto_add_sw_ctl(struct hda_codec *codec, - const char *pfx, int cidx, - hda_nid_t nid, unsigned int chs) + const char *pfx, int cidx, + hda_nid_t nid, unsigned int chs, + struct nid_path *path) { - struct alc_spec *spec = codec->spec; int wid_type; int type; unsigned long val; - if (!nid) + if (!nid || !path) return 0; wid_type = get_wcaps_type(get_wcaps(codec, nid)); if (wid_type == AC_WID_PIN || wid_type == AC_WID_AUD_OUT) { @@ -3582,44 +3589,46 @@ static int alc_auto_add_sw_ctl(struct hda_codec *codec, type = ALC_CTL_BIND_MUTE; val = HDA_COMPOSE_AMP_VAL(nid, chs, 2, HDA_INPUT); } - if (is_ctl_used(spec->sw_ctls, val) && chs != 2) /* exclude LFE */ + if (is_ctl_used(codec, val, NID_PATH_MUTE_CTL) && chs != 2) /* exclude LFE */ return 0; - mark_ctl_usage(spec->sw_ctls, val); + path->ctls[NID_PATH_MUTE_CTL] = val; return __add_pb_sw_ctrl(codec->spec, type, pfx, cidx, val); } static int alc_auto_add_stereo_sw(struct hda_codec *codec, const char *pfx, - int cidx, hda_nid_t nid) + int cidx, hda_nid_t nid, + struct nid_path *path) { int chs = 1; if (get_wcaps(codec, nid) & AC_WCAP_STEREO) chs = 3; - return alc_auto_add_sw_ctl(codec, pfx, cidx, nid, chs); + return alc_auto_add_sw_ctl(codec, pfx, cidx, nid, chs, path); } static hda_nid_t alc_look_for_out_mute_nid(struct hda_codec *codec, - hda_nid_t pin, hda_nid_t dac) + struct nid_path *path) { - hda_nid_t mix = alc_auto_dac_to_mix(codec, pin, dac); - if (nid_has_mute(codec, pin, HDA_OUTPUT)) - return pin; - else if (mix && nid_has_mute(codec, mix, HDA_INPUT)) - return mix; - else if (nid_has_mute(codec, dac, HDA_OUTPUT)) - return dac; + int i; + + for (i = path->depth - 1; i >= 0; i--) { + if (nid_has_mute(codec, path->path[i], HDA_OUTPUT)) + return path->path[i]; + if (i != path->depth - 1 && i != 0 && + nid_has_mute(codec, path->path[i], HDA_INPUT)) + return path->path[i]; + } return 0; } static hda_nid_t alc_look_for_out_vol_nid(struct hda_codec *codec, - hda_nid_t pin, hda_nid_t dac) -{ - hda_nid_t mix = alc_auto_dac_to_mix(codec, pin, dac); - if (nid_has_volume(codec, dac, HDA_OUTPUT)) - return dac; - else if (nid_has_volume(codec, mix, HDA_OUTPUT)) - return mix; - else if (nid_has_volume(codec, pin, HDA_OUTPUT)) - return pin; + struct nid_path *path) +{ + int i; + + for (i = path->depth - 1; i >= 0; i--) { + if (nid_has_volume(codec, path->path[i], HDA_OUTPUT)) + return path->path[i]; + } return 0; } @@ -3639,6 +3648,7 @@ static int alc_auto_create_multi_out_ctls(struct hda_codec *codec, int index; hda_nid_t dac, pin; hda_nid_t sw, vol; + struct nid_path *path; dac = spec->multiout.dac_nids[i]; if (!dac) @@ -3652,27 +3662,36 @@ static int alc_auto_create_multi_out_ctls(struct hda_codec *codec, name = alc_get_line_out_pfx(spec, i, true, &index); } - sw = alc_look_for_out_mute_nid(codec, pin, dac); - vol = alc_look_for_out_vol_nid(codec, pin, dac); + path = get_out_path(codec, pin, dac); + if (!path) + continue; + sw = alc_look_for_out_mute_nid(codec, path); + vol = alc_look_for_out_vol_nid(codec, path); if (!name || !strcmp(name, "CLFE")) { /* Center/LFE */ - err = alc_auto_add_vol_ctl(codec, "Center", 0, vol, 1); + err = alc_auto_add_vol_ctl(codec, "Center", 0, vol, 1, + path); if (err < 0) return err; - err = alc_auto_add_vol_ctl(codec, "LFE", 0, vol, 2); + err = alc_auto_add_vol_ctl(codec, "LFE", 0, vol, 2, + path); if (err < 0) return err; - err = alc_auto_add_sw_ctl(codec, "Center", 0, sw, 1); + err = alc_auto_add_sw_ctl(codec, "Center", 0, sw, 1, + path); if (err < 0) return err; - err = alc_auto_add_sw_ctl(codec, "LFE", 0, sw, 2); + err = alc_auto_add_sw_ctl(codec, "LFE", 0, sw, 2, + path); if (err < 0) return err; } else { - err = alc_auto_add_stereo_vol(codec, name, index, vol); + err = alc_auto_add_stereo_vol(codec, name, index, vol, + path); if (err < 0) return err; - err = alc_auto_add_stereo_sw(codec, name, index, sw); + err = alc_auto_add_stereo_sw(codec, name, index, sw, + path); if (err < 0) return err; } @@ -3685,9 +3704,14 @@ static int alc_auto_create_extra_out(struct hda_codec *codec, hda_nid_t pin, int cidx) { struct alc_spec *spec = codec->spec; + struct nid_path *path; hda_nid_t sw, vol; int err; + path = get_out_path(codec, pin, dac); + if (!path) + return 0; + if (!dac) { unsigned int val; /* the corresponding DAC is already occupied */ @@ -3695,18 +3719,18 @@ static int alc_auto_create_extra_out(struct hda_codec *codec, hda_nid_t pin, return 0; /* no way */ /* create a switch only */ val = HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT); - if (is_ctl_used(spec->sw_ctls, val)) + if (is_ctl_used(codec, val, NID_PATH_MUTE_CTL)) return 0; /* already created */ - mark_ctl_usage(spec->sw_ctls, val); + path->ctls[NID_PATH_MUTE_CTL] = val; return __add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, pfx, cidx, val); } - sw = alc_look_for_out_mute_nid(codec, pin, dac); - vol = alc_look_for_out_vol_nid(codec, pin, dac); - err = alc_auto_add_stereo_vol(codec, pfx, cidx, vol); + sw = alc_look_for_out_mute_nid(codec, path); + vol = alc_look_for_out_vol_nid(codec, path); + err = alc_auto_add_stereo_vol(codec, pfx, cidx, vol, path); if (err < 0) return err; - err = alc_auto_add_stereo_sw(codec, pfx, cidx, sw); + err = alc_auto_add_stereo_sw(codec, pfx, cidx, sw, path); if (err < 0) return err; return 0; @@ -3780,9 +3804,13 @@ static int alc_auto_create_extra_outs(struct hda_codec *codec, int num_pins, n = 0; for (i = 0; i < num_pins; i++) { hda_nid_t vol; + struct nid_path *path; if (!pins[i] || !dacs[i]) continue; - vol = alc_look_for_out_vol_nid(codec, pins[i], dacs[i]); + path = get_out_path(codec, pins[i], dacs[i]); + if (!path) + continue; + vol = alc_look_for_out_vol_nid(codec, path); if (vol) ctl->values[n++] = HDA_COMPOSE_AMP_VAL(vol, 3, 0, HDA_OUTPUT); @@ -3821,6 +3849,7 @@ static void alc_auto_set_output_and_unmute(struct hda_codec *codec, int i, num; hda_nid_t nid, mix = 0; hda_nid_t srcs[HDA_MAX_CONNECTIONS]; + struct nid_path *path; alc_set_pin_output(codec, pin, pin_type); nid = alc_go_down_to_selector(codec, pin); @@ -3845,13 +3874,16 @@ static void alc_auto_set_output_and_unmute(struct hda_codec *codec, AMP_IN_UNMUTE(1)); } /* initialize volume */ - nid = alc_look_for_out_vol_nid(codec, pin, dac); + path = get_out_path(codec, pin, dac); + if (!path) + return; + nid = alc_look_for_out_vol_nid(codec, path); if (nid) snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO); /* unmute DAC if it's not assigned to a mixer */ - nid = alc_look_for_out_mute_nid(codec, pin, dac); + nid = alc_look_for_out_mute_nid(codec, path); if (nid == mix && nid_has_mute(codec, dac, HDA_OUTPUT)) snd_hda_codec_write(codec, dac, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO); -- cgit v1.2.3 From 2f179721c4a2d1035a93e9c908740d87ac9952e2 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 10 Dec 2012 15:58:34 +0100 Subject: ALSA: hda - Fix mono amp values in proc output The mono widget is always connected to the left channel, thus the left channel amp value also should be referred for mono widgets instead of the right channel. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_proc.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_proc.c b/sound/pci/hda/hda_proc.c index 045e5d32f5de..740f46ac98db 100644 --- a/sound/pci/hda/hda_proc.c +++ b/sound/pci/hda/hda_proc.c @@ -138,16 +138,17 @@ static void print_amp_vals(struct snd_info_buffer *buffer, dir = dir == HDA_OUTPUT ? AC_AMP_GET_OUTPUT : AC_AMP_GET_INPUT; for (i = 0; i < indices; i++) { snd_iprintf(buffer, " ["); + val = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_AMP_GAIN_MUTE, + AC_AMP_GET_LEFT | dir | i); + snd_iprintf(buffer, "0x%02x", val); if (stereo) { val = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_AMP_GAIN_MUTE, - AC_AMP_GET_LEFT | dir | i); - snd_iprintf(buffer, "0x%02x ", val); + AC_AMP_GET_RIGHT | dir | i); + snd_iprintf(buffer, " 0x%02x", val); } - val = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_AMP_GAIN_MUTE, - AC_AMP_GET_RIGHT | dir | i); - snd_iprintf(buffer, "0x%02x]", val); + snd_iprintf(buffer, "]"); } snd_iprintf(buffer, "\n"); } -- cgit v1.2.3 From 792cf2fa2ea926c0c07277c0b67ab60745e8f898 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 10 Dec 2012 16:04:30 +0100 Subject: ALSA: hda/realtek - Reduce vol/mute ctl lookups at parsing codec So far, Realtek codec driver evaluates the NIDs for volume and mute controls twice, once while parsing the DACs and evaluating the assignment, and another while creating the mixer elements. This is utterly redundant and even fragile, as it's assuming that the ctl element evaluation is identical between both parsing DACs and creating mixer elements. This patch simplifies the code flow by doing the volume / mute controls evaluation only once while parsing the DACs. The patch ended up in larger changes than expected because of some cleanups became mandatory. As a gratis bonus, this patch also fixes some cases where the stereo channels are used wrongly for mono amps. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 168 ++++++++++++++++++++---------------------- 1 file changed, 78 insertions(+), 90 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index cacfbc05786a..870ac583bea2 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -3046,17 +3046,6 @@ static bool is_ctl_used(struct hda_codec *codec, unsigned int val, int type) return false; } -static void clear_vol_marks(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - int i; - - for (i = 0; i < spec->out_path.used; i++) { - struct nid_path *path = snd_array_elem(&spec->out_path, i); - path->ctls[0] = path->ctls[1] = 0; - } -} - /* badness definition */ enum { /* No primary DAC is found for the main output */ @@ -3121,8 +3110,15 @@ static struct nid_path *get_out_path(struct hda_codec *codec, hda_nid_t pin, return NULL; } -static int eval_shared_vol_badness(struct hda_codec *codec, hda_nid_t pin, - hda_nid_t dac) +/* look for widgets in the path between the given NIDs appropriate for + * volume and mute controls, and assign the values to ctls[]. + * + * When no appropriate widget is found in the path, the badness value + * is incremented depending on the situation. The function returns the + * total badness for both volume and mute controls. + */ +static int assign_out_path_ctls(struct hda_codec *codec, hda_nid_t pin, + hda_nid_t dac) { struct nid_path *path = get_out_path(codec, pin, dac); hda_nid_t nid; @@ -3143,7 +3139,8 @@ static int eval_shared_vol_badness(struct hda_codec *codec, hda_nid_t pin, nid = alc_look_for_out_mute_nid(codec, path); if (nid) { unsigned int wid_type = get_wcaps_type(get_wcaps(codec, nid)); - if (wid_type == AC_WID_PIN || wid_type == AC_WID_AUD_OUT) + if (wid_type == AC_WID_PIN || wid_type == AC_WID_AUD_OUT || + nid_has_mute(codec, nid, HDA_OUTPUT)) val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT); else val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT); @@ -3237,7 +3234,7 @@ static int alc_auto_fill_dacs(struct hda_codec *codec, int num_outs, if (!add_new_out_path(codec, pin, dac)) dac = dacs[i] = 0; if (dac) - badness += eval_shared_vol_badness(codec, pin, dac); + badness += assign_out_path_ctls(codec, pin, dac); } return badness; @@ -3533,36 +3530,52 @@ static int alc_auto_fill_dac_nids(struct hda_codec *codec) spec->vmaster_nid = alc_look_for_out_vol_nid(codec, path); } - /* clear the bitmap flags for creating controls */ - clear_vol_marks(codec); kfree(best_cfg); return 0; } +/* replace the channels in the composed amp value with the given number */ +static unsigned int amp_val_replace_channels(unsigned int val, unsigned int chs) +{ + val &= ~(0x3U << 16); + val |= chs << 16; + return val; +} + static int alc_auto_add_vol_ctl(struct hda_codec *codec, const char *pfx, int cidx, - hda_nid_t nid, unsigned int chs, + unsigned int chs, struct nid_path *path) { unsigned int val; - if (!nid || !path) + if (!path) return 0; - val = HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT); - if (is_ctl_used(codec, val, NID_PATH_VOL_CTL) && chs != 2) /* exclude LFE */ + val = path->ctls[NID_PATH_VOL_CTL]; + if (!val) return 0; - path->ctls[NID_PATH_VOL_CTL] = val; - return __add_pb_vol_ctrl(codec->spec, ALC_CTL_WIDGET_VOL, pfx, cidx, - val); + val = amp_val_replace_channels(val, chs); + return __add_pb_vol_ctrl(codec->spec, ALC_CTL_WIDGET_VOL, pfx, cidx, val); +} + +/* return the channel bits suitable for the given path->ctls[] */ +static int get_default_ch_nums(struct hda_codec *codec, struct nid_path *path, + int type) +{ + int chs = 1; /* mono (left only) */ + if (path) { + hda_nid_t nid = get_amp_nid_(path->ctls[type]); + if (nid && (get_wcaps(codec, nid) & AC_WCAP_STEREO)) + chs = 3; /* stereo */ + } + return chs; } static int alc_auto_add_stereo_vol(struct hda_codec *codec, const char *pfx, int cidx, - hda_nid_t nid, struct nid_path *path) + struct nid_path *path) { - int chs = 1; - if (get_wcaps(codec, nid) & AC_WCAP_STEREO) - chs = 3; - return alc_auto_add_vol_ctl(codec, pfx, cidx, nid, chs, path); + int chs = get_default_ch_nums(codec, path, NID_PATH_VOL_CTL); + return alc_auto_add_vol_ctl(codec, pfx, cidx, chs, path); } /* create a mute-switch for the given mixer widget; @@ -3570,39 +3583,33 @@ static int alc_auto_add_stereo_vol(struct hda_codec *codec, */ static int alc_auto_add_sw_ctl(struct hda_codec *codec, const char *pfx, int cidx, - hda_nid_t nid, unsigned int chs, + unsigned int chs, struct nid_path *path) { - int wid_type; - int type; - unsigned long val; - if (!nid || !path) + unsigned int val; + int type = ALC_CTL_WIDGET_MUTE; + + if (!path) return 0; - wid_type = get_wcaps_type(get_wcaps(codec, nid)); - if (wid_type == AC_WID_PIN || wid_type == AC_WID_AUD_OUT) { - type = ALC_CTL_WIDGET_MUTE; - val = HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT); - } else if (snd_hda_get_num_conns(codec, nid) == 1) { - type = ALC_CTL_WIDGET_MUTE; - val = HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_INPUT); - } else { - type = ALC_CTL_BIND_MUTE; - val = HDA_COMPOSE_AMP_VAL(nid, chs, 2, HDA_INPUT); - } - if (is_ctl_used(codec, val, NID_PATH_MUTE_CTL) && chs != 2) /* exclude LFE */ + val = path->ctls[NID_PATH_MUTE_CTL]; + if (!val) return 0; - path->ctls[NID_PATH_MUTE_CTL] = val; + val = amp_val_replace_channels(val, chs); + if (get_amp_direction_(val) == HDA_INPUT) { + hda_nid_t nid = get_amp_nid_(val); + if (snd_hda_get_num_conns(codec, nid) > 1) { + type = ALC_CTL_BIND_MUTE; + val |= 2 << 19; /* FIXME: fixed two widgets, so far */ + } + } return __add_pb_sw_ctrl(codec->spec, type, pfx, cidx, val); } static int alc_auto_add_stereo_sw(struct hda_codec *codec, const char *pfx, - int cidx, hda_nid_t nid, - struct nid_path *path) + int cidx, struct nid_path *path) { - int chs = 1; - if (get_wcaps(codec, nid) & AC_WCAP_STEREO) - chs = 3; - return alc_auto_add_sw_ctl(codec, pfx, cidx, nid, chs, path); + int chs = get_default_ch_nums(codec, path, NID_PATH_MUTE_CTL); + return alc_auto_add_sw_ctl(codec, pfx, cidx, chs, path); } static hda_nid_t alc_look_for_out_mute_nid(struct hda_codec *codec, @@ -3647,7 +3654,6 @@ static int alc_auto_create_multi_out_ctls(struct hda_codec *codec, const char *name; int index; hda_nid_t dac, pin; - hda_nid_t sw, vol; struct nid_path *path; dac = spec->multiout.dac_nids[i]; @@ -3665,33 +3671,25 @@ static int alc_auto_create_multi_out_ctls(struct hda_codec *codec, path = get_out_path(codec, pin, dac); if (!path) continue; - sw = alc_look_for_out_mute_nid(codec, path); - vol = alc_look_for_out_vol_nid(codec, path); if (!name || !strcmp(name, "CLFE")) { /* Center/LFE */ - err = alc_auto_add_vol_ctl(codec, "Center", 0, vol, 1, - path); + err = alc_auto_add_vol_ctl(codec, "Center", 0, 1, path); if (err < 0) return err; - err = alc_auto_add_vol_ctl(codec, "LFE", 0, vol, 2, - path); + err = alc_auto_add_vol_ctl(codec, "LFE", 0, 2, path); if (err < 0) return err; - err = alc_auto_add_sw_ctl(codec, "Center", 0, sw, 1, - path); + err = alc_auto_add_sw_ctl(codec, "Center", 0, 1, path); if (err < 0) return err; - err = alc_auto_add_sw_ctl(codec, "LFE", 0, sw, 2, - path); + err = alc_auto_add_sw_ctl(codec, "LFE", 0, 2, path); if (err < 0) return err; } else { - err = alc_auto_add_stereo_vol(codec, name, index, vol, - path); + err = alc_auto_add_stereo_vol(codec, name, index, path); if (err < 0) return err; - err = alc_auto_add_stereo_sw(codec, name, index, sw, - path); + err = alc_auto_add_stereo_sw(codec, name, index, path); if (err < 0) return err; } @@ -3703,34 +3701,19 @@ static int alc_auto_create_extra_out(struct hda_codec *codec, hda_nid_t pin, hda_nid_t dac, const char *pfx, int cidx) { - struct alc_spec *spec = codec->spec; struct nid_path *path; - hda_nid_t sw, vol; int err; path = get_out_path(codec, pin, dac); if (!path) return 0; - - if (!dac) { - unsigned int val; - /* the corresponding DAC is already occupied */ - if (!(get_wcaps(codec, pin) & AC_WCAP_OUT_AMP)) - return 0; /* no way */ - /* create a switch only */ - val = HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT); - if (is_ctl_used(codec, val, NID_PATH_MUTE_CTL)) - return 0; /* already created */ - path->ctls[NID_PATH_MUTE_CTL] = val; - return __add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, pfx, cidx, val); + /* bind volume control will be created in the case of dac = 0 */ + if (dac) { + err = alc_auto_add_stereo_vol(codec, pfx, cidx, path); + if (err < 0) + return err; } - - sw = alc_look_for_out_mute_nid(codec, path); - vol = alc_look_for_out_vol_nid(codec, path); - err = alc_auto_add_stereo_vol(codec, pfx, cidx, vol, path); - if (err < 0) - return err; - err = alc_auto_add_stereo_sw(codec, pfx, cidx, sw, path); + err = alc_auto_add_stereo_sw(codec, pfx, cidx, path); if (err < 0) return err; return 0; @@ -4051,7 +4034,12 @@ static int alc_auto_fill_multi_ios(struct hda_codec *codec, return badness; } - return 0; + /* assign volume and mute controls */ + for (i = old_pins; i < spec->multi_ios; i++) + badness += assign_out_path_ctls(codec, spec->multi_io[i].pin, + spec->multi_io[i].dac); + + return badness; } static int alc_auto_ch_mode_info(struct snd_kcontrol *kcontrol, -- cgit v1.2.3 From 78e635c93b0e385dc23d18c2a4047fc8857467bd Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 10 Dec 2012 17:07:16 +0100 Subject: ALSA: hda/realtek - Simplify the output volume initialization Simplify the output path initialization using the existing path information instead of assuming the topology specific to Realtek codecs. This is also implicitly a fix for some amp values on output pins where the old parser missed (e.g. ALC260 output pins). The same function alc_auto_set_output_and_unmute() can be used now for the multi-io activation, since the output selection means nothing but activating the given output path. And, finally at this stage, we can get rid of alc_go_down_to_selector() and other functions that are codec really specifically to Realtek codecs. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 182 +++++++++++++++++++++--------------------- 1 file changed, 93 insertions(+), 89 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 870ac583bea2..f893fb1b23d4 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -2858,55 +2858,6 @@ static void alc_auto_init_analog_input(struct hda_codec *codec) } } -/* convert from MIX nid to DAC */ -static hda_nid_t alc_auto_mix_to_dac(struct hda_codec *codec, hda_nid_t nid) -{ - hda_nid_t list[5]; - int i, num; - - if (get_wcaps_type(get_wcaps(codec, nid)) == AC_WID_AUD_OUT) - return nid; - num = snd_hda_get_connections(codec, nid, list, ARRAY_SIZE(list)); - for (i = 0; i < num; i++) { - if (get_wcaps_type(get_wcaps(codec, list[i])) == AC_WID_AUD_OUT) - return list[i]; - } - return 0; -} - -/* go down to the selector widget before the mixer */ -static hda_nid_t alc_go_down_to_selector(struct hda_codec *codec, hda_nid_t pin) -{ - hda_nid_t srcs[5]; - int num = snd_hda_get_connections(codec, pin, srcs, - ARRAY_SIZE(srcs)); - if (num != 1 || - get_wcaps_type(get_wcaps(codec, srcs[0])) != AC_WID_AUD_SEL) - return pin; - return srcs[0]; -} - -/* select the connection from pin to DAC if needed */ -static int alc_auto_select_dac(struct hda_codec *codec, hda_nid_t pin, - hda_nid_t dac) -{ - hda_nid_t mix[5]; - int i, num; - - pin = alc_go_down_to_selector(codec, pin); - num = snd_hda_get_connections(codec, pin, mix, ARRAY_SIZE(mix)); - if (num < 2) - return 0; - for (i = 0; i < num; i++) { - if (alc_auto_mix_to_dac(codec, mix[i]) == dac) { - snd_hda_codec_update_cache(codec, pin, 0, - AC_VERB_SET_CONNECT_SEL, i); - return 0; - } - } - return 0; -} - static bool alc_is_dac_already_used(struct hda_codec *codec, hda_nid_t nid) { struct alc_spec *spec = codec->spec; @@ -3825,51 +3776,102 @@ static int alc_auto_create_speaker_out(struct hda_codec *codec) "Speaker"); } -static void alc_auto_set_output_and_unmute(struct hda_codec *codec, - hda_nid_t pin, int pin_type, - hda_nid_t dac) +/* is a volume or mute control already present? */ +static bool __is_out_ctl_present(struct hda_codec *codec, + struct nid_path *exclude_path, + hda_nid_t nid, int dir, int types) { - int i, num; - hda_nid_t nid, mix = 0; - hda_nid_t srcs[HDA_MAX_CONNECTIONS]; - struct nid_path *path; + struct alc_spec *spec = codec->spec; + int i, type; - alc_set_pin_output(codec, pin, pin_type); - nid = alc_go_down_to_selector(codec, pin); - num = snd_hda_get_connections(codec, nid, srcs, ARRAY_SIZE(srcs)); - for (i = 0; i < num; i++) { - if (alc_auto_mix_to_dac(codec, srcs[i]) != dac) + for (i = 0; i < spec->out_path.used; i++) { + struct nid_path *p = snd_array_elem(&spec->out_path, i); + if (p == exclude_path || p->depth <= 0) continue; - mix = srcs[i]; - break; + for (type = 0; type < 2; type++) { + if (types & (1 << type)) { + unsigned int val = p->ctls[type]; + if (get_amp_nid_(val) == nid && + get_amp_direction_(val) == dir) + return true; + } + } } - if (!mix) - return; + return false; +} + +#define is_out_ctl_present(codec, path, nid, dir) \ + __is_out_ctl_present(codec, path, nid, dir, 3) /* check both types */ +#define is_out_vol_ctl_present(codec, nid, dir) \ + __is_out_ctl_present(codec, NULL, nid, dir, 1 << NID_PATH_VOL_CTL) +#define is_out_mute_ctl_present(codec, nid, dir) \ + __is_out_ctl_present(codec, NULL, nid, dir, 1 << NID_PATH_MUTE_CTL) + +static int get_default_amp_val(struct hda_codec *codec, hda_nid_t nid, int dir) +{ + unsigned int caps, offset; + unsigned int val = 0; + + caps = query_amp_caps(codec, nid, dir); + if (caps & AC_AMPCAP_NUM_STEPS) { + offset = (caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT; + /* if a volume control is assigned, set the lowest level + * as default; otherwise set to 0dB + */ + if (is_out_vol_ctl_present(codec, nid, dir)) + val = 0; + else + val = offset; + } + if (caps & AC_AMPCAP_MUTE) { + /* if a mute control is assigned, mute as default */ + if (is_out_mute_ctl_present(codec, nid, dir)) + val |= HDA_AMP_MUTE; + } + return val; +} + +/* configure the path from the given dac to the pin as the proper output */ +static void alc_auto_set_output_and_unmute(struct hda_codec *codec, + hda_nid_t pin, int pin_type, + hda_nid_t dac, bool force) +{ + int i, val; + struct nid_path *path; - /* need the manual connection? */ - if (num > 1) - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, i); - /* unmute mixer widget inputs */ - if (nid_has_mute(codec, mix, HDA_INPUT)) { - snd_hda_codec_write(codec, mix, 0, AC_VERB_SET_AMP_GAIN_MUTE, - AMP_IN_UNMUTE(0)); - snd_hda_codec_write(codec, mix, 0, AC_VERB_SET_AMP_GAIN_MUTE, - AMP_IN_UNMUTE(1)); - } - /* initialize volume */ + alc_set_pin_output(codec, pin, pin_type); path = get_out_path(codec, pin, dac); if (!path) return; - nid = alc_look_for_out_vol_nid(codec, path); - if (nid) - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, - AMP_OUT_ZERO); - /* unmute DAC if it's not assigned to a mixer */ - nid = alc_look_for_out_mute_nid(codec, path); - if (nid == mix && nid_has_mute(codec, dac, HDA_OUTPUT)) - snd_hda_codec_write(codec, dac, 0, AC_VERB_SET_AMP_GAIN_MUTE, - AMP_OUT_ZERO); + for (i = path->depth - 1; i >= 0; i--) { + hda_nid_t nid = path->path[i]; + if (i > 0 && path->multi[i - 1]) + snd_hda_codec_write(codec, nid, 0, + AC_VERB_SET_CONNECT_SEL, + path->idx[i - 1]); + + if (i != 0 && i != path->depth - 1 && + (get_wcaps(codec, nid) & AC_WCAP_IN_AMP) && + (force || !is_out_ctl_present(codec, path, nid, + HDA_INPUT))) { + val = get_default_amp_val(codec, nid, HDA_INPUT); + snd_hda_codec_write(codec, nid, 0, + AC_VERB_SET_AMP_GAIN_MUTE, + AMP_IN_UNMUTE(0) | val); + snd_hda_codec_write(codec, nid, 0, + AC_VERB_SET_AMP_GAIN_MUTE, + AMP_IN_UNMUTE(1) | val); + } + if ((get_wcaps(codec, nid) & AC_WCAP_OUT_AMP) && + (force || !is_out_ctl_present(codec, path, nid, + HDA_OUTPUT))) { + val = get_default_amp_val(codec, nid, HDA_OUTPUT); + snd_hda_codec_write(codec, nid, 0, + AC_VERB_SET_AMP_GAIN_MUTE, + AMP_OUT_UNMUTE | val); + } + } } static void alc_auto_init_multi_out(struct hda_codec *codec) @@ -3882,7 +3884,8 @@ static void alc_auto_init_multi_out(struct hda_codec *codec) hda_nid_t nid = spec->autocfg.line_out_pins[i]; if (nid) alc_auto_set_output_and_unmute(codec, nid, pin_type, - spec->multiout.dac_nids[i]); + spec->multiout.dac_nids[i], true); + } } @@ -3905,7 +3908,7 @@ static void alc_auto_init_extra_out(struct hda_codec *codec) else dac = spec->multiout.dac_nids[0]; } - alc_auto_set_output_and_unmute(codec, pin, PIN_HP, dac); + alc_auto_set_output_and_unmute(codec, pin, PIN_HP, dac, true); } for (i = 0; i < spec->autocfg.speaker_outs; i++) { if (spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT) @@ -3920,7 +3923,7 @@ static void alc_auto_init_extra_out(struct hda_codec *codec) else dac = spec->multiout.dac_nids[0]; } - alc_auto_set_output_and_unmute(codec, pin, PIN_OUT, dac); + alc_auto_set_output_and_unmute(codec, pin, PIN_OUT, dac, true); } } @@ -4081,7 +4084,8 @@ static int alc_set_multi_io(struct hda_codec *codec, int idx, bool output) if (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP) snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0, HDA_AMP_MUTE, 0); - alc_auto_select_dac(codec, nid, spec->multi_io[idx].dac); + alc_auto_set_output_and_unmute(codec, nid, PIN_OUT, + spec->multi_io[idx].dac, false); } else { if (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP) snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0, -- cgit v1.2.3 From 95e960cece76cb538fcac03ac80893db0f1e6a15 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 10 Dec 2012 17:27:57 +0100 Subject: ALSA: hda/realtek - Make path->idx[] and path->multi[] consistent So far, idx[i] and multi[i] indicate the attribute of the widget path[i - 1]. This was just for simplifying the code in __parse_output_path(), but this is rather confusing for later use. It's more natural if both idx[i] and multi[i] point to the same widget of path[i]. This patch changes to that way. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index f893fb1b23d4..434856376bf0 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -102,8 +102,8 @@ enum { #define MAX_NID_PATH_DEPTH 5 /* output-path: DAC -> ... -> pin - * idx[] contains the source index number of the next widget; - * e.g. idx[0] is the index of the DAC selected by path[1] widget + * idx[i] contains the source index number to select on of the widget path[i]; + * e.g. idx[1] is the index of the DAC (path[0]) selected by path[1] widget * multi[] indicates whether it's a selector widget with multi-connectors * (i.e. the connection selection is mandatory) * vol_ctl and mute_ctl contains the NIDs for the assigned mixers @@ -2937,9 +2937,9 @@ static bool __parse_output_path(struct hda_codec *codec, hda_nid_t nid, found: path->path[path->depth] = conn[i]; - path->idx[path->depth] = i; + path->idx[path->depth + 1] = i; if (nums > 1 && get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_AUD_MIX) - path->multi[path->depth] = 1; + path->multi[path->depth + 1] = 1; path->depth++; return true; } @@ -3846,10 +3846,10 @@ static void alc_auto_set_output_and_unmute(struct hda_codec *codec, for (i = path->depth - 1; i >= 0; i--) { hda_nid_t nid = path->path[i]; - if (i > 0 && path->multi[i - 1]) + if (path->multi[i]) snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, - path->idx[i - 1]); + path->idx[i]); if (i != 0 && i != path->depth - 1 && (get_wcaps(codec, nid) & AC_WCAP_IN_AMP) && -- cgit v1.2.3 From 36f0fd540e299c7746601ce7bff7d062a6757c2f Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 12 Dec 2012 17:25:00 +0100 Subject: ALSA: hda/realtek - Parse input paths Just like the output paths, parse the whole paths for inputs as well and store in a path list. For that purpose, rewrite the output parser code to be generically usable. The input path list is not referred at all in this patch. It'll be used to replace the fixed adc/capsrc array in later patches for more flexible input path selections. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 103 ++++++++++++++++++++++++++++++------------ 1 file changed, 73 insertions(+), 30 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 434856376bf0..fbc4a97ea8f6 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -101,7 +101,11 @@ enum { #define MAX_NID_PATH_DEPTH 5 -/* output-path: DAC -> ... -> pin +/* Widget connection path + * + * For output, stored in the order of DAC -> ... -> pin, + * for input, pin -> ... -> ADC. + * * idx[i] contains the source index number to select on of the widget path[i]; * e.g. idx[1] is the index of the DAC (path[0]) selected by path[1] widget * multi[] indicates whether it's a selector widget with multi-connectors @@ -196,6 +200,9 @@ struct alc_spec { /* output paths */ struct snd_array out_path; + /* input paths */ + struct snd_array in_path; + /* hooks */ void (*init_hook)(struct hda_codec *codec); #ifdef CONFIG_PM @@ -2428,6 +2435,7 @@ static void alc_free(struct hda_codec *codec) alc_free_kctls(codec); alc_free_bind_ctls(codec); snd_array_free(&spec->out_path); + snd_array_free(&spec->in_path); snd_hda_gen_free(&spec->gen); kfree(spec); snd_hda_detach_beep_device(codec); @@ -2628,6 +2636,10 @@ static const char *alc_get_line_out_pfx(struct alc_spec *spec, int ch, return channel_name[ch]; } +static bool parse_nid_path(struct hda_codec *codec, hda_nid_t from_nid, + hda_nid_t to_nid, int with_aa_mix, + struct nid_path *path); + #ifdef CONFIG_PM /* add the powersave loopback-list entry */ static void add_loopback_list(struct alc_spec *spec, hda_nid_t mix, int idx) @@ -2666,6 +2678,28 @@ static int new_analog_input(struct alc_spec *spec, hda_nid_t pin, return 0; } +static int new_capture_source(struct hda_codec *codec, int adc_idx, + hda_nid_t pin, int idx, const char *label) +{ + struct alc_spec *spec = codec->spec; + struct hda_input_mux *imux = &spec->private_imux[0]; + struct nid_path *path; + + path = snd_array_new(&spec->in_path); + if (!path) + return -ENOMEM; + memset(path, 0, sizeof(*path)); + if (!parse_nid_path(codec, pin, spec->adc_nids[adc_idx], 2, path)) { + snd_printd(KERN_ERR "invalid input path 0x%x -> 0x%x\n", + pin, spec->adc_nids[adc_idx]); + return -EINVAL; + } + + spec->imux_pins[imux->num_items] = pin; + snd_hda_add_imux_item(imux, label, idx, NULL); + return 0; +} + static int alc_is_input_pin(struct hda_codec *codec, hda_nid_t nid) { unsigned int pincap = snd_hda_query_pin_caps(codec, nid); @@ -2767,8 +2801,9 @@ static int alc_auto_create_input_ctls(struct hda_codec *codec) hda_nid_t cap = get_capsrc(spec, c); idx = get_connection_index(codec, cap, pin); if (idx >= 0) { - spec->imux_pins[imux->num_items] = pin; - snd_hda_add_imux_item(imux, label, idx, NULL); + err = new_capture_source(codec, c, pin, idx, label); + if (err < 0) + return err; break; } } @@ -2897,40 +2932,45 @@ static hda_nid_t alc_auto_look_for_dac(struct hda_codec *codec, hda_nid_t pin) } /* called recursively */ -static bool __parse_output_path(struct hda_codec *codec, hda_nid_t nid, - hda_nid_t target_dac, int with_aa_mix, - struct nid_path *path, int depth) +static bool __parse_nid_path(struct hda_codec *codec, + hda_nid_t from_nid, hda_nid_t to_nid, + int with_aa_mix, struct nid_path *path, int depth) { struct alc_spec *spec = codec->spec; - hda_nid_t conn[8]; + hda_nid_t conn[16]; int i, nums; - if (nid == spec->mixer_nid) { + if (to_nid == spec->mixer_nid) { if (!with_aa_mix) return false; with_aa_mix = 2; /* mark aa-mix is included */ } - nums = snd_hda_get_connections(codec, nid, conn, ARRAY_SIZE(conn)); + nums = snd_hda_get_connections(codec, to_nid, conn, ARRAY_SIZE(conn)); for (i = 0; i < nums; i++) { - if (get_wcaps_type(get_wcaps(codec, conn[i])) != AC_WID_AUD_OUT) - continue; - if (conn[i] == target_dac || - (!target_dac && !alc_is_dac_already_used(codec, conn[i]))) { - /* aa-mix is requested but not included? */ - if (!(spec->mixer_nid && with_aa_mix == 1)) - goto found; + if (conn[i] != from_nid) { + /* special case: when from_nid is 0, + * try to find an empty DAC + */ + if (from_nid || + get_wcaps_type(get_wcaps(codec, conn[i])) != AC_WID_AUD_OUT || + alc_is_dac_already_used(codec, conn[i])) + continue; } + /* aa-mix is requested but not included? */ + if (!(spec->mixer_nid && with_aa_mix == 1)) + goto found; } if (depth >= MAX_NID_PATH_DEPTH) return false; for (i = 0; i < nums; i++) { unsigned int type; type = get_wcaps_type(get_wcaps(codec, conn[i])); - if (type == AC_WID_AUD_OUT) + if (type == AC_WID_AUD_OUT || type == AC_WID_AUD_IN || + type == AC_WID_PIN) continue; - if (__parse_output_path(codec, conn[i], target_dac, - with_aa_mix, path, depth + 1)) + if (__parse_nid_path(codec, from_nid, conn[i], + with_aa_mix, path, depth + 1)) goto found; } return false; @@ -2938,25 +2978,27 @@ static bool __parse_output_path(struct hda_codec *codec, hda_nid_t nid, found: path->path[path->depth] = conn[i]; path->idx[path->depth + 1] = i; - if (nums > 1 && get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_AUD_MIX) + if (nums > 1 && get_wcaps_type(get_wcaps(codec, to_nid)) != AC_WID_AUD_MIX) path->multi[path->depth + 1] = 1; path->depth++; return true; } -/* parse the output path from the given nid to the target DAC; - * when target_dac is 0, try to find an empty DAC; - * when with_aa_mix is 0, paths with spec->mixer_nid are excluded +/* parse the widget path from the given nid to the target nid; + * when @from_nid is 0, try to find an empty DAC; + * when @with_aa_mix is 0, paths with spec->mixer_nid are excluded. + * when @with_aa_mix is 1, paths without spec->mixer_nid are excluded. + * when @with_aa_mix is 2, no special handling about spec->mixer_nid. */ -static bool parse_output_path(struct hda_codec *codec, hda_nid_t nid, - hda_nid_t target_dac, int with_aa_mix, - struct nid_path *path) +static bool parse_nid_path(struct hda_codec *codec, hda_nid_t from_nid, + hda_nid_t to_nid, int with_aa_mix, + struct nid_path *path) { - if (__parse_output_path(codec, nid, target_dac, with_aa_mix, path, 1)) { - path->path[path->depth] = nid; + if (__parse_nid_path(codec, from_nid, to_nid, with_aa_mix, path, 1)) { + path->path[path->depth] = to_nid; path->depth++; #if 0 - snd_printdd("output-path: depth=%d, %02x/%02x/%02x/%02x/%02x\n", + snd_printdd("path: depth=%d, %02x/%02x/%02x/%02x/%02x\n", path->depth, path->path[0], path->path[1], path->path[2], path->path[3], path->path[4]); #endif @@ -3034,7 +3076,7 @@ static bool add_new_out_path(struct hda_codec *codec, hda_nid_t pin, if (!path) return false; memset(path, 0, sizeof(*path)); - if (parse_output_path(codec, pin, dac, 0, path)) + if (parse_nid_path(codec, dac, pin, 0, path)) return true; /* push back */ spec->out_path.used--; @@ -4529,6 +4571,7 @@ static int alc_alloc_spec(struct hda_codec *codec, hda_nid_t mixer_nid) snd_array_init(&spec->kctls, sizeof(struct snd_kcontrol_new), 32); snd_array_init(&spec->bind_ctls, sizeof(struct hda_bind_ctls *), 8); snd_array_init(&spec->out_path, sizeof(struct nid_path), 8); + snd_array_init(&spec->in_path, sizeof(struct nid_path), 8); err = alc_codec_rename_from_preset(codec); if (err < 0) { -- cgit v1.2.3 From c2fd19c2fc3cc5e609c573f3c66711fd809f9d3c Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 12 Dec 2012 18:02:41 +0100 Subject: ALSA: hda/realtek - Parse analog loopback paths more generically Improve the parser of analog loopback paths and handle in a more generic way. The following changes are included in this patch: - Instead of assuming direct connections between pins and the mixer widget, track the whole path between them. This fixes some missing connections like ALC660. - Introduce the path list for loopback paths like input and output path lists. Currently it's not used for any real purposes, yet. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 38 ++++++++++++++++++++++++++++++-------- 1 file changed, 30 insertions(+), 8 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index fbc4a97ea8f6..8ce0a0d4f083 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -203,6 +203,9 @@ struct alc_spec { /* input paths */ struct snd_array in_path; + /* analog loopback paths */ + struct snd_array loopback_path; + /* hooks */ void (*init_hook)(struct hda_codec *codec); #ifdef CONFIG_PM @@ -2436,6 +2439,7 @@ static void alc_free(struct hda_codec *codec) alc_free_bind_ctls(codec); snd_array_free(&spec->out_path); snd_array_free(&spec->in_path); + snd_array_free(&spec->loopback_path); snd_hda_gen_free(&spec->gen); kfree(spec); snd_hda_detach_beep_device(codec); @@ -2660,12 +2664,22 @@ static void add_loopback_list(struct alc_spec *spec, hda_nid_t mix, int idx) #endif /* create input playback/capture controls for the given pin */ -static int new_analog_input(struct alc_spec *spec, hda_nid_t pin, +static int new_analog_input(struct hda_codec *codec, hda_nid_t pin, const char *ctlname, int ctlidx, - int idx, hda_nid_t mix_nid) + hda_nid_t mix_nid) { - int err; + struct alc_spec *spec = codec->spec; + struct nid_path *path; + int err, idx; + + path = snd_array_new(&spec->loopback_path); + if (!path) + return -ENOMEM; + memset(path, 0, sizeof(*path)); + if (!parse_nid_path(codec, pin, mix_nid, 2, path)) + return -EINVAL; + idx = path->idx[path->depth - 1]; err = __add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, ctlname, ctlidx, HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT)); if (err < 0) @@ -2706,6 +2720,15 @@ static int alc_is_input_pin(struct hda_codec *codec, hda_nid_t nid) return (pincap & AC_PINCAP_IN) != 0; } +/* check whether the given two widgets can be connected */ +static bool is_reachable_path(struct hda_codec *codec, + hda_nid_t from_nid, hda_nid_t to_nid) +{ + if (!from_nid || !to_nid) + return false; + return snd_hda_get_conn_index(codec, to_nid, from_nid, true) >= 0; +} + /* Parse the codec tree and retrieve ADCs and corresponding capsrc MUXs */ static int alc_auto_fill_adc_caps(struct hda_codec *codec) { @@ -2787,11 +2810,9 @@ static int alc_auto_create_input_ctls(struct hda_codec *codec) prev_label = label; if (mixer) { - idx = get_connection_index(codec, mixer, pin); - if (idx >= 0) { - err = new_analog_input(spec, pin, - label, type_idx, - idx, mixer); + if (is_reachable_path(codec, pin, mixer)) { + err = new_analog_input(codec, pin, + label, type_idx, mixer); if (err < 0) return err; } @@ -4572,6 +4593,7 @@ static int alc_alloc_spec(struct hda_codec *codec, hda_nid_t mixer_nid) snd_array_init(&spec->bind_ctls, sizeof(struct hda_bind_ctls *), 8); snd_array_init(&spec->out_path, sizeof(struct nid_path), 8); snd_array_init(&spec->in_path, sizeof(struct nid_path), 8); + snd_array_init(&spec->loopback_path, sizeof(struct nid_path), 8); err = alc_codec_rename_from_preset(codec); if (err < 0) { -- cgit v1.2.3 From bd32f782b956e6a3040696899a818387ecbe17bc Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 12 Dec 2012 18:08:52 +0100 Subject: ALSA: hda/realtek - Check amp capabilities of aa-mixer widget For handling the analog-loopback paths more generically, check the amp capabilities of the aa-mixer widget, and create only the appropriate mixer elements. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 8ce0a0d4f083..7a22ab0b047b 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -2672,6 +2672,10 @@ static int new_analog_input(struct hda_codec *codec, hda_nid_t pin, struct nid_path *path; int err, idx; + if (!nid_has_volume(codec, mix_nid, HDA_INPUT) && + !nid_has_mute(codec, mix_nid, HDA_INPUT)) + return 0; /* no need for analog loopback */ + path = snd_array_new(&spec->loopback_path); if (!path) return -ENOMEM; @@ -2680,14 +2684,20 @@ static int new_analog_input(struct hda_codec *codec, hda_nid_t pin, return -EINVAL; idx = path->idx[path->depth - 1]; - err = __add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, ctlname, ctlidx, + if (nid_has_volume(codec, mix_nid, HDA_INPUT)) { + err = __add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, ctlname, ctlidx, HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT)); - if (err < 0) - return err; - err = __add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, ctlname, ctlidx, + if (err < 0) + return err; + } + + if (nid_has_mute(codec, mix_nid, HDA_INPUT)) { + err = __add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, ctlname, ctlidx, HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT)); - if (err < 0) - return err; + if (err < 0) + return err; + } + add_loopback_list(spec, mix_nid, idx); return 0; } -- cgit v1.2.3 From 9366ede7fd2c3ac7ed3a79c5d7c5ee4ad97043b8 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 13 Dec 2012 16:43:52 +0100 Subject: ALSA: hda/realtek - Fix initialization of input amps in output paths When initializing the output paths, we assumed the input amps have almost two inputs blindly. It's not only generic but even incorrect for some codecs like ALC268 & co. Also, the same assumption (two sources) exists for the bind input-amp controls. This patch changes the codes in these places to handle the input connections in a more generic way. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 7a22ab0b047b..18c4a78efbc7 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -3621,9 +3621,10 @@ static int alc_auto_add_sw_ctl(struct hda_codec *codec, val = amp_val_replace_channels(val, chs); if (get_amp_direction_(val) == HDA_INPUT) { hda_nid_t nid = get_amp_nid_(val); - if (snd_hda_get_num_conns(codec, nid) > 1) { + int nums = snd_hda_get_num_conns(codec, nid); + if (nums > 1) { type = ALC_CTL_BIND_MUTE; - val |= 2 << 19; /* FIXME: fixed two widgets, so far */ + val |= nums << 19; } } return __add_pb_sw_ctrl(codec->spec, type, pfx, cidx, val); @@ -3909,6 +3910,7 @@ static void alc_auto_set_output_and_unmute(struct hda_codec *codec, hda_nid_t pin, int pin_type, hda_nid_t dac, bool force) { + struct alc_spec *spec = codec->spec; int i, val; struct nid_path *path; @@ -3928,13 +3930,19 @@ static void alc_auto_set_output_and_unmute(struct hda_codec *codec, (get_wcaps(codec, nid) & AC_WCAP_IN_AMP) && (force || !is_out_ctl_present(codec, path, nid, HDA_INPUT))) { + hda_nid_t conn[16]; + int n, nums; + nums = snd_hda_get_connections(codec, nid, conn, + ARRAY_SIZE(conn)); val = get_default_amp_val(codec, nid, HDA_INPUT); - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_AMP_GAIN_MUTE, - AMP_IN_UNMUTE(0) | val); - snd_hda_codec_write(codec, nid, 0, + for (n = 0; n < nums; n++) { + if (n != path->idx[i] && + conn[n] != spec->mixer_nid) + continue; + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, - AMP_IN_UNMUTE(1) | val); + AMP_IN_UNMUTE(n) | val); + } } if ((get_wcaps(codec, nid) & AC_WCAP_OUT_AMP) && (force || !is_out_ctl_present(codec, path, nid, -- cgit v1.2.3 From 8092e6065435d75a68873fa66cd003a1b829e0fe Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 13 Dec 2012 17:03:30 +0100 Subject: ALSA: hda - Remove snd_hda_codec_amp_update() call from patch_*.c It's used only in one place in patch_analog.c, and it can be replaced with others better. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_analog.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index 89fc5030ec79..308a5b9e6b9d 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c @@ -995,15 +995,7 @@ static int ad1986a_hp_master_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - long *valp = ucontrol->value.integer.value; - int change; - - change = snd_hda_codec_amp_update(codec, 0x1a, 0, HDA_OUTPUT, 0, - HDA_AMP_MUTE, - valp[0] ? 0 : HDA_AMP_MUTE); - change |= snd_hda_codec_amp_update(codec, 0x1a, 1, HDA_OUTPUT, 0, - HDA_AMP_MUTE, - valp[1] ? 0 : HDA_AMP_MUTE); + int change = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol); if (change) ad1986a_update_hp(codec); return change; -- cgit v1.2.3 From c370dd6e9faae4b2e699a1f210827aceaa0c3399 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 13 Dec 2012 18:30:04 +0100 Subject: ALSA: hda - Introduce cache & flush cmd / amp writes For optimizing the verb executions, a new mechanism to cache the verbs and amp update commands is introduced. With the new "write to cache and flush" way, you can reduce the same verbs that have been written multiple times. When codec->cached_write flag is set, the further snd_hda_codec_write_cache() and snd_hda_codec_amp_stereo() calls will be performed only on the command or amp cache table, but not sent to the hardware yet. Once after you call all commands and update amps, call snd_hda_codec_resume_amp() and snd_hda_codec_resume_cache(). Then all cached writes and amp updates will be written to the hardware, and the dirty flags are cleared. In this implementation, the existing cache table is reused, so actually no big code change is seen here. Each cache entry has a new dirty flag now (so the cache key is now reduced to 31bit). As a good side-effect by this change, snd_hda_codec_resume_*() will no longer execute verbs that have been already issued during the resume phase by checking the dirty flags. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_codec.c | 77 +++++++++++++++++++++++++++++++++++++---------- sound/pci/hda/hda_codec.h | 12 +++----- sound/pci/hda/hda_local.h | 2 -- 3 files changed, 65 insertions(+), 26 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index b8fb0a5adb9b..2f890af820b2 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -1610,6 +1610,7 @@ static struct hda_cache_head *get_alloc_hash(struct hda_cache_rec *cache, cur = snd_array_index(&cache->buf, info); info->key = key; info->val = 0; + info->dirty = 0; idx = key % (u16)ARRAY_SIZE(cache->hash); info->next = cache->hash[idx]; cache->hash[idx] = cur; @@ -1873,8 +1874,11 @@ int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch, return 0; } info->vol[ch] = val; + if (codec->cached_write) + info->head.dirty = 1; mutex_unlock(&codec->hash_mutex); - put_vol_mute(codec, info, nid, ch, direction, idx, val); + if (!codec->cached_write) + put_vol_mute(codec, info, nid, ch, direction, idx, val); return 1; } EXPORT_SYMBOL_HDA(snd_hda_codec_amp_update); @@ -1905,7 +1909,6 @@ int snd_hda_codec_amp_stereo(struct hda_codec *codec, hda_nid_t nid, } EXPORT_SYMBOL_HDA(snd_hda_codec_amp_stereo); -#ifdef CONFIG_PM /** * snd_hda_codec_resume_amp - Resume all AMP commands from the cache * @codec: HD-audio codec @@ -1914,13 +1917,17 @@ EXPORT_SYMBOL_HDA(snd_hda_codec_amp_stereo); */ void snd_hda_codec_resume_amp(struct hda_codec *codec) { - struct hda_amp_info *buffer = codec->amp_cache.buf.list; int i; - for (i = 0; i < codec->amp_cache.buf.used; i++, buffer++) { - u32 key = buffer->head.key; + mutex_lock(&codec->hash_mutex); + for (i = 0; i < codec->amp_cache.buf.used; i++) { + struct hda_amp_info *buffer; + u32 key; hda_nid_t nid; unsigned int idx, dir, ch; + + buffer = snd_array_elem(&codec->amp_cache.buf, i); + key = buffer->head.key; if (!key) continue; nid = key & 0xff; @@ -1929,13 +1936,18 @@ void snd_hda_codec_resume_amp(struct hda_codec *codec) for (ch = 0; ch < 2; ch++) { if (!(buffer->head.val & INFO_AMP_VOL(ch))) continue; + if (!buffer->head.dirty) + continue; + buffer->head.dirty = 0; + mutex_unlock(&codec->hash_mutex); put_vol_mute(codec, buffer, nid, ch, dir, idx, buffer->vol[ch]); + mutex_lock(&codec->hash_mutex); } } + mutex_unlock(&codec->hash_mutex); } EXPORT_SYMBOL_HDA(snd_hda_codec_resume_amp); -#endif /* CONFIG_PM */ static u32 get_amp_max_value(struct hda_codec *codec, hda_nid_t nid, int dir, unsigned int ofs) @@ -3375,12 +3387,11 @@ int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid) } EXPORT_SYMBOL_HDA(snd_hda_create_spdif_in_ctls); -#ifdef CONFIG_PM /* * command cache */ -/* build a 32bit cache key with the widget id and the command parameter */ +/* build a 31bit cache key with the widget id and the command parameter */ #define build_cmd_cache_key(nid, verb) ((verb << 8) | nid) #define get_cmd_cache_nid(key) ((key) & 0xff) #define get_cmd_cache_cmd(key) (((key) >> 8) & 0xffff) @@ -3400,20 +3411,27 @@ EXPORT_SYMBOL_HDA(snd_hda_create_spdif_in_ctls); int snd_hda_codec_write_cache(struct hda_codec *codec, hda_nid_t nid, int direct, unsigned int verb, unsigned int parm) { - int err = snd_hda_codec_write(codec, nid, direct, verb, parm); + int err; struct hda_cache_head *c; u32 key; - if (err < 0) - return err; + if (!codec->cached_write) { + err = snd_hda_codec_write(codec, nid, direct, verb, parm); + if (err < 0) + return err; + } + /* parm may contain the verb stuff for get/set amp */ verb = verb | (parm >> 8); parm &= 0xff; key = build_cmd_cache_key(nid, verb); mutex_lock(&codec->bus->cmd_mutex); c = get_alloc_hash(&codec->cmd_cache, key); - if (c) + if (c) { c->val = parm; + if (codec->cached_write) + c->dirty = 1; + } mutex_unlock(&codec->bus->cmd_mutex); return 0; } @@ -3462,16 +3480,26 @@ EXPORT_SYMBOL_HDA(snd_hda_codec_update_cache); */ void snd_hda_codec_resume_cache(struct hda_codec *codec) { - struct hda_cache_head *buffer = codec->cmd_cache.buf.list; int i; - for (i = 0; i < codec->cmd_cache.buf.used; i++, buffer++) { - u32 key = buffer->key; + mutex_lock(&codec->hash_mutex); + for (i = 0; i < codec->cmd_cache.buf.used; i++) { + struct hda_cache_head *buffer; + u32 key; + + buffer = snd_array_elem(&codec->cmd_cache.buf, i); + key = buffer->key; if (!key) continue; + if (!buffer->dirty) + continue; + buffer->dirty = 0; + mutex_unlock(&codec->hash_mutex); snd_hda_codec_write(codec, get_cmd_cache_nid(key), 0, get_cmd_cache_cmd(key), buffer->val); + mutex_lock(&codec->hash_mutex); } + mutex_unlock(&codec->hash_mutex); } EXPORT_SYMBOL_HDA(snd_hda_codec_resume_cache); @@ -3492,7 +3520,6 @@ void snd_hda_sequence_write_cache(struct hda_codec *codec, seq->param); } EXPORT_SYMBOL_HDA(snd_hda_sequence_write_cache); -#endif /* CONFIG_PM */ void snd_hda_codec_set_power_to_all(struct hda_codec *codec, hda_nid_t fg, unsigned int power_state, @@ -3640,6 +3667,22 @@ static unsigned int hda_call_codec_suspend(struct hda_codec *codec, bool in_wq) return state; } +/* mark all entries of cmd and amp caches dirty */ +static void hda_mark_cmd_cache_dirty(struct hda_codec *codec) +{ + int i; + for (i = 0; i < codec->cmd_cache.buf.used; i++) { + struct hda_cache_head *cmd; + cmd = snd_array_elem(&codec->cmd_cache.buf, i); + cmd->dirty = 1; + } + for (i = 0; i < codec->amp_cache.buf.used; i++) { + struct hda_amp_info *amp; + amp = snd_array_elem(&codec->cmd_cache.buf, i); + amp->head.dirty = 1; + } +} + /* * kick up codec; used both from PM and power-save */ @@ -3647,6 +3690,8 @@ static void hda_call_codec_resume(struct hda_codec *codec) { codec->in_pm = 1; + hda_mark_cmd_cache_dirty(codec); + /* set as if powered on for avoiding re-entering the resume * in the resume / power-save sequence */ diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index 8665540e55aa..cab39b23d37c 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -719,9 +719,10 @@ struct hda_codec_ops { /* record for amp information cache */ struct hda_cache_head { - u32 key; /* hash key */ + u32 key:31; /* hash key */ + u32 dirty:1; u16 val; /* assigned value */ - u16 next; /* next link; -1 = terminal */ + u16 next; }; struct hda_amp_info { @@ -867,6 +868,7 @@ struct hda_codec { unsigned int no_jack_detect:1; /* Machine has no jack-detection */ unsigned int pcm_format_first:1; /* PCM format must be set first */ unsigned int epss:1; /* supporting EPSS? */ + unsigned int cached_write:1; /* write only to caches */ #ifdef CONFIG_PM unsigned int power_on :1; /* current (global) power-state */ unsigned int d3_stop_clk:1; /* support D3 operation without BCLK */ @@ -952,7 +954,6 @@ void snd_hda_sequence_write(struct hda_codec *codec, int snd_hda_queue_unsol_event(struct hda_bus *bus, u32 res, u32 res_ex); /* cached write */ -#ifdef CONFIG_PM int snd_hda_codec_write_cache(struct hda_codec *codec, hda_nid_t nid, int direct, unsigned int verb, unsigned int parm); void snd_hda_sequence_write_cache(struct hda_codec *codec, @@ -960,11 +961,6 @@ void snd_hda_sequence_write_cache(struct hda_codec *codec, int snd_hda_codec_update_cache(struct hda_codec *codec, hda_nid_t nid, int direct, unsigned int verb, unsigned int parm); void snd_hda_codec_resume_cache(struct hda_codec *codec); -#else -#define snd_hda_codec_write_cache snd_hda_codec_write -#define snd_hda_codec_update_cache snd_hda_codec_write -#define snd_hda_sequence_write_cache snd_hda_sequence_write -#endif /* the struct for codec->pin_configs */ struct hda_pincfg { diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index 4b40a5e7a8f5..f765296cc943 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -133,9 +133,7 @@ int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch, int direction, int idx, int mask, int val); int snd_hda_codec_amp_stereo(struct hda_codec *codec, hda_nid_t nid, int dir, int idx, int mask, int val); -#ifdef CONFIG_PM void snd_hda_codec_resume_amp(struct hda_codec *codec); -#endif void snd_hda_set_vmaster_tlv(struct hda_codec *codec, hda_nid_t nid, int dir, unsigned int *tlv); -- cgit v1.2.3 From 280e57d544f5f9f599de8e11aacb7c087da254b8 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 14 Dec 2012 10:32:21 +0100 Subject: ALSA: hda - Introduce snd_hda_codec_amp_init*() The new function snd_hda_codec_amp_init() (and the stereo variant) initializes the amp value only once at the first access. If the amp was already initialized or updated, this won't do anything more. It's useful for initializing the input amps that are in the part of the path but never used. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_codec.c | 71 ++++++++++++++++++++++++++++++++++------------- sound/pci/hda/hda_local.h | 4 +++ 2 files changed, 56 insertions(+), 19 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 2f890af820b2..0037147dcd54 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -1765,7 +1765,7 @@ EXPORT_SYMBOL_HDA(snd_hda_override_pin_caps); */ static struct hda_amp_info * update_amp_hash(struct hda_codec *codec, hda_nid_t nid, int ch, - int direction, int index) + int direction, int index, bool init_only) { struct hda_amp_info *info; unsigned int parm, val = 0; @@ -1791,7 +1791,8 @@ update_amp_hash(struct hda_codec *codec, hda_nid_t nid, int ch, } info->vol[ch] = val; info->head.val |= INFO_AMP_VOL(ch); - } + } else if (init_only) + return NULL; return info; } @@ -1832,7 +1833,7 @@ int snd_hda_codec_amp_read(struct hda_codec *codec, hda_nid_t nid, int ch, unsigned int val = 0; mutex_lock(&codec->hash_mutex); - info = update_amp_hash(codec, nid, ch, direction, index); + info = update_amp_hash(codec, nid, ch, direction, index, false); if (info) val = info->vol[ch]; mutex_unlock(&codec->hash_mutex); @@ -1840,21 +1841,9 @@ int snd_hda_codec_amp_read(struct hda_codec *codec, hda_nid_t nid, int ch, } EXPORT_SYMBOL_HDA(snd_hda_codec_amp_read); -/** - * snd_hda_codec_amp_update - update the AMP value - * @codec: HD-audio codec - * @nid: NID to read the AMP value - * @ch: channel (left=0 or right=1) - * @direction: #HDA_INPUT or #HDA_OUTPUT - * @idx: the index value (only for input direction) - * @mask: bit mask to set - * @val: the bits value to set - * - * Update the AMP value with a bit mask. - * Returns 0 if the value is unchanged, 1 if changed. - */ -int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch, - int direction, int idx, int mask, int val) +static int codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch, + int direction, int idx, int mask, int val, + bool init_only) { struct hda_amp_info *info; @@ -1863,7 +1852,7 @@ int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch, val &= mask; mutex_lock(&codec->hash_mutex); - info = update_amp_hash(codec, nid, ch, direction, idx); + info = update_amp_hash(codec, nid, ch, direction, idx, init_only); if (!info) { mutex_unlock(&codec->hash_mutex); return 0; @@ -1881,6 +1870,25 @@ int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch, put_vol_mute(codec, info, nid, ch, direction, idx, val); return 1; } + +/** + * snd_hda_codec_amp_update - update the AMP value + * @codec: HD-audio codec + * @nid: NID to read the AMP value + * @ch: channel (left=0 or right=1) + * @direction: #HDA_INPUT or #HDA_OUTPUT + * @idx: the index value (only for input direction) + * @mask: bit mask to set + * @val: the bits value to set + * + * Update the AMP value with a bit mask. + * Returns 0 if the value is unchanged, 1 if changed. + */ +int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch, + int direction, int idx, int mask, int val) +{ + return codec_amp_update(codec, nid, ch, direction, idx, mask, val, false); +} EXPORT_SYMBOL_HDA(snd_hda_codec_amp_update); /** @@ -1909,6 +1917,31 @@ int snd_hda_codec_amp_stereo(struct hda_codec *codec, hda_nid_t nid, } EXPORT_SYMBOL_HDA(snd_hda_codec_amp_stereo); +/* Works like snd_hda_codec_amp_update() but it writes the value only at + * the first access. If the amp was already initialized / updated beforehand, + * this does nothing. + */ +int snd_hda_codec_amp_init(struct hda_codec *codec, hda_nid_t nid, int ch, + int dir, int idx, int mask, int val) +{ + return codec_amp_update(codec, nid, ch, dir, idx, mask, val, true); +} +EXPORT_SYMBOL_HDA(snd_hda_codec_amp_init); + +int snd_hda_codec_amp_init_stereo(struct hda_codec *codec, hda_nid_t nid, + int dir, int idx, int mask, int val) +{ + int ch, ret = 0; + + if (snd_BUG_ON(mask & ~0xff)) + mask &= 0xff; + for (ch = 0; ch < 2; ch++) + ret |= snd_hda_codec_amp_init(codec, nid, ch, dir, + idx, mask, val); + return ret; +} +EXPORT_SYMBOL_HDA(snd_hda_codec_amp_init_stereo); + /** * snd_hda_codec_resume_amp - Resume all AMP commands from the cache * @codec: HD-audio codec diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index f765296cc943..e38519b52f41 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -133,6 +133,10 @@ int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch, int direction, int idx, int mask, int val); int snd_hda_codec_amp_stereo(struct hda_codec *codec, hda_nid_t nid, int dir, int idx, int mask, int val); +int snd_hda_codec_amp_init(struct hda_codec *codec, hda_nid_t nid, int ch, + int direction, int idx, int mask, int val); +int snd_hda_codec_amp_init_stereo(struct hda_codec *codec, hda_nid_t nid, + int dir, int idx, int mask, int val); void snd_hda_codec_resume_amp(struct hda_codec *codec); void snd_hda_set_vmaster_tlv(struct hda_codec *codec, hda_nid_t nid, int dir, -- cgit v1.2.3 From b8a47c79b28c34652acf9594ef48b0c9fc875401 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 14 Dec 2012 14:20:34 +0100 Subject: ALSA: hda/realtek - Remove non-standard automute mode We are using only AUTOMUTE_MODE_PIN in patch_realtek.c and all others have been already dropped. Let's remove the old superfluous codes. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 46 ++++++++++--------------------------------- 1 file changed, 10 insertions(+), 36 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 18c4a78efbc7..9046f1c216a1 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -73,12 +73,6 @@ struct alc_multi_io { unsigned int ctl_in; /* cached input-pin control value */ }; -enum { - ALC_AUTOMUTE_PIN, /* change the pin control */ - ALC_AUTOMUTE_AMP, /* mute/unmute the pin AMP */ - ALC_AUTOMUTE_MIXER, /* mute/unmute mixer widget AMP */ -}; - #define MAX_VOL_NIDS 0x40 /* make compatible with old code */ @@ -542,7 +536,6 @@ static void do_automute(struct hda_codec *codec, int num_pins, hda_nid_t *pins, bool mute, bool hp_out) { struct alc_spec *spec = codec->spec; - unsigned int mute_bits = mute ? HDA_AMP_MUTE : 0; unsigned int pin_bits = mute ? 0 : (hp_out ? PIN_HP : PIN_OUT); int i; @@ -551,34 +544,17 @@ static void do_automute(struct hda_codec *codec, int num_pins, hda_nid_t *pins, unsigned int val; if (!nid) break; - switch (spec->automute_mode) { - case ALC_AUTOMUTE_PIN: - /* don't reset VREF value in case it's controlling - * the amp (see alc861_fixup_asus_amp_vref_0f()) - */ - if (spec->keep_vref_in_automute) { - val = snd_hda_codec_read(codec, nid, 0, + /* don't reset VREF value in case it's controlling + * the amp (see alc861_fixup_asus_amp_vref_0f()) + */ + if (spec->keep_vref_in_automute) { + val = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0); - val &= ~PIN_HP; - } else - val = 0; - val |= pin_bits; - snd_hda_set_pin_ctl(codec, nid, val); - break; - case ALC_AUTOMUTE_AMP: - snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0, - HDA_AMP_MUTE, mute_bits); - break; - case ALC_AUTOMUTE_MIXER: - nid = spec->automute_mixer_nid[i]; - if (!nid) - break; - snd_hda_codec_amp_stereo(codec, nid, HDA_INPUT, 0, - HDA_AMP_MUTE, mute_bits); - snd_hda_codec_amp_stereo(codec, nid, HDA_INPUT, 1, - HDA_AMP_MUTE, mute_bits); - break; - } + val &= ~PIN_HP; + } else + val = 0; + val |= pin_bits; + snd_hda_set_pin_ctl(codec, nid, val); } } @@ -979,8 +955,6 @@ static int alc_init_automute(struct hda_codec *codec) cfg->hp_outs = cfg->line_outs; } - spec->automute_mode = ALC_AUTOMUTE_PIN; - for (i = 0; i < cfg->hp_outs; i++) { hda_nid_t nid = cfg->hp_pins[i]; if (!is_jack_detectable(codec, nid)) -- cgit v1.2.3 From 130e5f0642de99a61f46c4f0468bfc5db6030967 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 14 Dec 2012 16:09:29 +0100 Subject: ALSA: hda/realtek - Add path active flag ... and rewrite the initialization of output paths as a generic function that is applicable for both i/o directions. The new flag, active, is introduced to each nid_path entry. This indicates whether the given path is active, and it's used for checking whether a certain widget can be turned off or changed when a path is no longer used or newly enabled. It's still used only in the output paths. More wider adaption for input and loopback paths will be achieved in the later patch. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_local.h | 3 +- sound/pci/hda/patch_realtek.c | 261 ++++++++++++++++++++++++++++-------------- 2 files changed, 176 insertions(+), 88 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index e38519b52f41..ff56da82b7a0 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -598,7 +598,8 @@ int snd_hda_check_amp_list_power(struct hda_codec *codec, #define get_amp_channels(kc) (((kc)->private_value >> 16) & 0x3) #define get_amp_direction_(pv) (((pv) >> 18) & 0x1) #define get_amp_direction(kc) get_amp_direction_((kc)->private_value) -#define get_amp_index(kc) (((kc)->private_value >> 19) & 0xf) +#define get_amp_index_(pv) (((pv) >> 19) & 0xf) +#define get_amp_index(kc) get_amp_index_((kc)->private_value) #define get_amp_offset(kc) (((kc)->private_value >> 23) & 0x3f) #define get_amp_min_mute(kc) (((kc)->private_value >> 29) & 0x1) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 9046f1c216a1..269d41a2fea0 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -112,6 +112,7 @@ struct nid_path { unsigned char idx[MAX_NID_PATH_DEPTH]; unsigned char multi[MAX_NID_PATH_DEPTH]; unsigned int ctls[2]; /* 0 = volume, 1 = mute */ + bool active; }; enum { NID_PATH_VOL_CTL = 0, NID_PATH_MUTE_CTL = 1 }; @@ -2853,16 +2854,6 @@ static int alc_auto_create_shared_input(struct hda_codec *codec) return 0; } -static void alc_set_pin_output(struct hda_codec *codec, hda_nid_t nid, - unsigned int pin_type) -{ - snd_hda_set_pin_ctl(codec, nid, pin_type); - /* unmute pin */ - if (nid_has_mute(codec, nid, HDA_OUTPUT)) - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, - AMP_OUT_UNMUTE); -} - static int get_pin_type(int line_out_type) { if (line_out_type == AUTO_PIN_HP_OUT) @@ -3824,23 +3815,21 @@ static int alc_auto_create_speaker_out(struct hda_codec *codec) "Speaker"); } -/* is a volume or mute control already present? */ -static bool __is_out_ctl_present(struct hda_codec *codec, - struct nid_path *exclude_path, - hda_nid_t nid, int dir, int types) +static bool is_ctl_associated_in_list(struct snd_array *array, hda_nid_t nid, + int dir, int idx, int types) { - struct alc_spec *spec = codec->spec; int i, type; - for (i = 0; i < spec->out_path.used; i++) { - struct nid_path *p = snd_array_elem(&spec->out_path, i); - if (p == exclude_path || p->depth <= 0) + for (i = 0; i < array->used; i++) { + struct nid_path *p = snd_array_elem(array, i); + if (p->depth <= 0) continue; for (type = 0; type < 2; type++) { if (types & (1 << type)) { unsigned int val = p->ctls[type]; if (get_amp_nid_(val) == nid && - get_amp_direction_(val) == dir) + get_amp_direction_(val) == dir && + get_amp_index_(val) == idx) return true; } } @@ -3848,85 +3837,183 @@ static bool __is_out_ctl_present(struct hda_codec *codec, return false; } -#define is_out_ctl_present(codec, path, nid, dir) \ - __is_out_ctl_present(codec, path, nid, dir, 3) /* check both types */ -#define is_out_vol_ctl_present(codec, nid, dir) \ - __is_out_ctl_present(codec, NULL, nid, dir, 1 << NID_PATH_VOL_CTL) -#define is_out_mute_ctl_present(codec, nid, dir) \ - __is_out_ctl_present(codec, NULL, nid, dir, 1 << NID_PATH_MUTE_CTL) +/* check whether a control with the given (nid, dir, idx) was assigned */ +static bool is_ctl_associated(struct hda_codec *codec, hda_nid_t nid, + int dir, int idx) +{ + struct alc_spec *spec = codec->spec; + return is_ctl_associated_in_list(&spec->out_path, nid, dir, idx, 3) || + is_ctl_associated_in_list(&spec->in_path, nid, dir, idx, 3) || + is_ctl_associated_in_list(&spec->loopback_path, nid, dir, idx, 3); +} -static int get_default_amp_val(struct hda_codec *codec, hda_nid_t nid, int dir) +/* can have the amp-in capability? */ +static bool has_amp_in(struct hda_codec *codec, struct nid_path *path, int idx) { - unsigned int caps, offset; + hda_nid_t nid = path->path[idx]; + unsigned int caps = get_wcaps(codec, nid); + unsigned int type = get_wcaps_type(caps); + + if (!(caps & AC_WCAP_IN_AMP)) + return false; + if (type == AC_WID_PIN && idx > 0) /* only for input pins */ + return false; + return true; +} + +/* can have the amp-out capability? */ +static bool has_amp_out(struct hda_codec *codec, struct nid_path *path, int idx) +{ + hda_nid_t nid = path->path[idx]; + unsigned int caps = get_wcaps(codec, nid); + unsigned int type = get_wcaps_type(caps); + + if (!(caps & AC_WCAP_OUT_AMP)) + return false; + if (type == AC_WID_PIN && !idx) /* only for output pins */ + return false; + return true; +} + +static bool is_active_in_list(struct hda_codec *codec, struct snd_array *array, + hda_nid_t nid, int dir, int idx) +{ + int i, n; + + for (n = 0; n < array->used; n++) { + struct nid_path *path = snd_array_elem(array, n); + if (!path->active) + continue; + for (i = 0; i < path->depth; i++) { + if (path->path[i] == nid) { + if (dir == HDA_OUTPUT || path->idx[i] == idx) + return true; + break; + } + } + } + return false; +} + +/* check whether the given (nid,dir,idx) is active */ +static bool is_active_nid(struct hda_codec *codec, hda_nid_t nid, + unsigned int idx, unsigned int dir) +{ + struct alc_spec *spec = codec->spec; + return is_active_in_list(codec, &spec->out_path, nid, idx, dir) || + is_active_in_list(codec, &spec->in_path, nid, idx, dir) || + is_active_in_list(codec, &spec->loopback_path, nid, idx, dir); +} + +/* get the default amp value for the target state */ +static int get_amp_val_to_activate(struct hda_codec *codec, hda_nid_t nid, + int dir, bool enable) +{ + unsigned int caps; unsigned int val = 0; caps = query_amp_caps(codec, nid, dir); if (caps & AC_AMPCAP_NUM_STEPS) { - offset = (caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT; - /* if a volume control is assigned, set the lowest level - * as default; otherwise set to 0dB - */ - if (is_out_vol_ctl_present(codec, nid, dir)) - val = 0; - else - val = offset; + /* set to 0dB */ + if (enable) + val = (caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT; } if (caps & AC_AMPCAP_MUTE) { - /* if a mute control is assigned, mute as default */ - if (is_out_mute_ctl_present(codec, nid, dir)) + if (!enable) val |= HDA_AMP_MUTE; } return val; } -/* configure the path from the given dac to the pin as the proper output */ -static void alc_auto_set_output_and_unmute(struct hda_codec *codec, - hda_nid_t pin, int pin_type, - hda_nid_t dac, bool force) +/* initialize the amp value (only at the first time) */ +static void init_amp(struct hda_codec *codec, hda_nid_t nid, int dir, int idx) +{ + int val = get_amp_val_to_activate(codec, nid, dir, false); + snd_hda_codec_amp_init_stereo(codec, nid, dir, idx, 0xff, val); +} + +static void activate_amp(struct hda_codec *codec, hda_nid_t nid, int dir, + int idx, bool enable) +{ + int val; + if (is_ctl_associated(codec, nid, dir, idx) || + is_active_nid(codec, nid, dir, idx)) + return; + val = get_amp_val_to_activate(codec, nid, dir, enable); + snd_hda_codec_amp_stereo(codec, nid, dir, idx, 0xff, val); +} + +static void activate_amp_out(struct hda_codec *codec, struct nid_path *path, + int i, bool enable) +{ + hda_nid_t nid = path->path[i]; + init_amp(codec, nid, HDA_OUTPUT, 0); + activate_amp(codec, nid, HDA_OUTPUT, 0, enable); +} + +static void activate_amp_in(struct hda_codec *codec, struct nid_path *path, + int i, bool enable) { struct alc_spec *spec = codec->spec; - int i, val; - struct nid_path *path; + hda_nid_t conn[16]; + int n, nums; + hda_nid_t nid = path->path[i]; - alc_set_pin_output(codec, pin, pin_type); - path = get_out_path(codec, pin, dac); - if (!path) + nums = snd_hda_get_connections(codec, nid, conn, ARRAY_SIZE(conn)); + for (n = 0; n < nums; n++) + init_amp(codec, nid, HDA_INPUT, n); + + if (is_ctl_associated(codec, nid, HDA_INPUT, path->idx[i])) return; + /* here is a little bit tricky in comparison with activate_amp_out(); + * when aa-mixer is available, we need to enable the path as well + */ + for (n = 0; n < nums; n++) { + if (n != path->idx[i] && conn[n] != spec->mixer_nid) + continue; + activate_amp(codec, nid, HDA_INPUT, n, enable); + } +} + +static void activate_path(struct hda_codec *codec, struct nid_path *path, + bool enable) +{ + int i; + + if (path->active == enable) + return; + + if (!enable) + path->active = false; + for (i = path->depth - 1; i >= 0; i--) { - hda_nid_t nid = path->path[i]; if (path->multi[i]) - snd_hda_codec_write(codec, nid, 0, + snd_hda_codec_write_cache(codec, path->path[i], 0, AC_VERB_SET_CONNECT_SEL, path->idx[i]); - - if (i != 0 && i != path->depth - 1 && - (get_wcaps(codec, nid) & AC_WCAP_IN_AMP) && - (force || !is_out_ctl_present(codec, path, nid, - HDA_INPUT))) { - hda_nid_t conn[16]; - int n, nums; - nums = snd_hda_get_connections(codec, nid, conn, - ARRAY_SIZE(conn)); - val = get_default_amp_val(codec, nid, HDA_INPUT); - for (n = 0; n < nums; n++) { - if (n != path->idx[i] && - conn[n] != spec->mixer_nid) - continue; - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_AMP_GAIN_MUTE, - AMP_IN_UNMUTE(n) | val); - } - } - if ((get_wcaps(codec, nid) & AC_WCAP_OUT_AMP) && - (force || !is_out_ctl_present(codec, path, nid, - HDA_OUTPUT))) { - val = get_default_amp_val(codec, nid, HDA_OUTPUT); - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_AMP_GAIN_MUTE, - AMP_OUT_UNMUTE | val); - } + if (has_amp_in(codec, path, i)) + activate_amp_in(codec, path, i, enable); + if (has_amp_out(codec, path, i)) + activate_amp_out(codec, path, i, enable); } + + if (enable) + path->active = true; +} + +/* configure the path from the given dac to the pin as the proper output */ +static void alc_auto_set_output_and_unmute(struct hda_codec *codec, + hda_nid_t pin, int pin_type, + hda_nid_t dac) +{ + struct nid_path *path; + + snd_hda_set_pin_ctl_cache(codec, pin, pin_type); + path = get_out_path(codec, pin, dac); + if (!path) + return; + activate_path(codec, path, true); } static void alc_auto_init_multi_out(struct hda_codec *codec) @@ -3939,7 +4026,7 @@ static void alc_auto_init_multi_out(struct hda_codec *codec) hda_nid_t nid = spec->autocfg.line_out_pins[i]; if (nid) alc_auto_set_output_and_unmute(codec, nid, pin_type, - spec->multiout.dac_nids[i], true); + spec->multiout.dac_nids[i]); } } @@ -3963,7 +4050,7 @@ static void alc_auto_init_extra_out(struct hda_codec *codec) else dac = spec->multiout.dac_nids[0]; } - alc_auto_set_output_and_unmute(codec, pin, PIN_HP, dac, true); + alc_auto_set_output_and_unmute(codec, pin, PIN_HP, dac); } for (i = 0; i < spec->autocfg.speaker_outs; i++) { if (spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT) @@ -3978,7 +4065,7 @@ static void alc_auto_init_extra_out(struct hda_codec *codec) else dac = spec->multiout.dac_nids[0]; } - alc_auto_set_output_and_unmute(codec, pin, PIN_OUT, dac, true); + alc_auto_set_output_and_unmute(codec, pin, PIN_OUT, dac); } } @@ -4129,22 +4216,22 @@ static int alc_set_multi_io(struct hda_codec *codec, int idx, bool output) { struct alc_spec *spec = codec->spec; hda_nid_t nid = spec->multi_io[idx].pin; + struct nid_path *path; + + path = get_out_path(codec, nid, spec->multi_io[idx].dac); + if (!path) + return -EINVAL; if (!spec->multi_io[idx].ctl_in) spec->multi_io[idx].ctl_in = - snd_hda_codec_read(codec, nid, 0, + snd_hda_codec_update_cache(codec, nid, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0); + if (output) { snd_hda_set_pin_ctl_cache(codec, nid, PIN_OUT); - if (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP) - snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0, - HDA_AMP_MUTE, 0); - alc_auto_set_output_and_unmute(codec, nid, PIN_OUT, - spec->multi_io[idx].dac, false); + activate_path(codec, path, true); } else { - if (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP) - snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0, - HDA_AMP_MUTE, HDA_AMP_MUTE); + activate_path(codec, path, false); snd_hda_set_pin_ctl_cache(codec, nid, spec->multi_io[idx].ctl_in); } -- cgit v1.2.3 From 9c64076e545771566ebd1b5b6b9f8f5681d83ac4 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 14 Dec 2012 16:15:56 +0100 Subject: ALSA: hda/realtek - Consolidate is_reachable_path() alc_auto_is_dac_reachable() can be replaced fully with is_reachable_path(). The only difference is the order of arguments. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 25 +++++++------------------ 1 file changed, 7 insertions(+), 18 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 269d41a2fea0..04713a8744b1 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -2902,15 +2902,6 @@ static bool alc_is_dac_already_used(struct hda_codec *codec, hda_nid_t nid) return false; } -/* check whether the DAC is reachable from the pin */ -static bool alc_auto_is_dac_reachable(struct hda_codec *codec, - hda_nid_t pin, hda_nid_t dac) -{ - if (!pin || !dac) - return false; - return snd_hda_get_conn_index(codec, pin, dac, true) >= 0; -} - /* look for an empty DAC slot */ static hda_nid_t alc_auto_look_for_dac(struct hda_codec *codec, hda_nid_t pin) { @@ -2921,7 +2912,7 @@ static hda_nid_t alc_auto_look_for_dac(struct hda_codec *codec, hda_nid_t pin) hda_nid_t nid = spec->all_dacs[i]; if (!nid || alc_is_dac_already_used(codec, nid)) continue; - if (alc_auto_is_dac_reachable(codec, pin, nid)) + if (is_reachable_path(codec, nid, pin)) return nid; } return 0; @@ -3013,7 +3004,7 @@ static hda_nid_t get_dac_if_single(struct hda_codec *codec, hda_nid_t pin) hda_nid_t nid = spec->all_dacs[i]; if (!nid || alc_is_dac_already_used(codec, nid)) continue; - if (alc_auto_is_dac_reachable(codec, pin, nid)) { + if (is_reachable_path(codec, nid, pin)) { if (nid_found) return 0; nid_found = nid; @@ -3189,7 +3180,7 @@ static int alc_auto_fill_dacs(struct hda_codec *codec, int num_outs, dacs[i] = alc_auto_look_for_dac(codec, pin); if (!dacs[i] && !i) { for (j = 1; j < num_outs; j++) { - if (alc_auto_is_dac_reachable(codec, pin, dacs[j])) { + if (is_reachable_path(codec, dacs[j], pin)) { dacs[0] = dacs[j]; dacs[j] = 0; break; @@ -3198,11 +3189,10 @@ static int alc_auto_fill_dacs(struct hda_codec *codec, int num_outs, } dac = dacs[i]; if (!dac) { - if (alc_auto_is_dac_reachable(codec, pin, dacs[0])) + if (is_reachable_path(codec, dacs[0], pin)) dac = dacs[0]; else if (cfg->line_outs > i && - alc_auto_is_dac_reachable(codec, pin, - spec->private_dac_nids[i])) + is_reachable_path(codec, spec->private_dac_nids[i], pin)) dac = spec->private_dac_nids[i]; if (dac) { if (!i) @@ -3211,8 +3201,7 @@ static int alc_auto_fill_dacs(struct hda_codec *codec, int num_outs, badness += bad->shared_surr; else badness += bad->shared_clfe; - } else if (alc_auto_is_dac_reachable(codec, pin, - spec->private_dac_nids[0])) { + } else if (is_reachable_path(codec, spec->private_dac_nids[0], pin)) { dac = spec->private_dac_nids[0]; badness += bad->shared_surr_main; } else if (!i) @@ -4141,7 +4130,7 @@ static int alc_auto_fill_multi_ios(struct hda_codec *codec, if (offset && offset + spec->multi_ios < dacs) { dac = spec->private_dac_nids[offset + spec->multi_ios]; - if (!alc_auto_is_dac_reachable(codec, nid, dac)) + if (!is_reachable_path(codec, dac, nid)) dac = 0; } if (hardwired) -- cgit v1.2.3 From c9967f1cbadd3a6af2be54a5baed2cc0dcef50e6 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 14 Dec 2012 16:39:22 +0100 Subject: ALSA: hda/realtek - Consolidate to a single path list We don't have to keep three individual path lists for input, output and loopback. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 94 +++++++++++++++---------------------------- 1 file changed, 33 insertions(+), 61 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 04713a8744b1..9a38107cf0f5 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -192,14 +192,8 @@ struct alc_spec { int num_all_dacs; hda_nid_t all_dacs[16]; - /* output paths */ - struct snd_array out_path; - - /* input paths */ - struct snd_array in_path; - - /* analog loopback paths */ - struct snd_array loopback_path; + /* path list */ + struct snd_array paths; /* hooks */ void (*init_hook)(struct hda_codec *codec); @@ -2412,9 +2406,7 @@ static void alc_free(struct hda_codec *codec) alc_free_kctls(codec); alc_free_bind_ctls(codec); - snd_array_free(&spec->out_path); - snd_array_free(&spec->in_path); - snd_array_free(&spec->loopback_path); + snd_array_free(&spec->paths); snd_hda_gen_free(&spec->gen); kfree(spec); snd_hda_detach_beep_device(codec); @@ -2651,7 +2643,7 @@ static int new_analog_input(struct hda_codec *codec, hda_nid_t pin, !nid_has_mute(codec, mix_nid, HDA_INPUT)) return 0; /* no need for analog loopback */ - path = snd_array_new(&spec->loopback_path); + path = snd_array_new(&spec->paths); if (!path) return -ENOMEM; memset(path, 0, sizeof(*path)); @@ -2684,7 +2676,7 @@ static int new_capture_source(struct hda_codec *codec, int adc_idx, struct hda_input_mux *imux = &spec->private_imux[0]; struct nid_path *path; - path = snd_array_new(&spec->in_path); + path = snd_array_new(&spec->paths); if (!path) return -ENOMEM; memset(path, 0, sizeof(*path)); @@ -2894,8 +2886,8 @@ static bool alc_is_dac_already_used(struct hda_codec *codec, hda_nid_t nid) struct alc_spec *spec = codec->spec; int i; - for (i = 0; i < spec->out_path.used; i++) { - struct nid_path *path = snd_array_elem(&spec->out_path, i); + for (i = 0; i < spec->paths.used; i++) { + struct nid_path *path = snd_array_elem(&spec->paths, i); if (path->path[0] == nid) return true; } @@ -3018,8 +3010,8 @@ static bool is_ctl_used(struct hda_codec *codec, unsigned int val, int type) struct alc_spec *spec = codec->spec; int i; - for (i = 0; i < spec->out_path.used; i++) { - struct nid_path *path = snd_array_elem(&spec->out_path, i); + for (i = 0; i < spec->paths.used; i++) { + struct nid_path *path = snd_array_elem(&spec->paths, i); if (path->ctls[type] == val) return true; } @@ -3059,14 +3051,14 @@ static bool add_new_out_path(struct hda_codec *codec, hda_nid_t pin, struct alc_spec *spec = codec->spec; struct nid_path *path; - path = snd_array_new(&spec->out_path); + path = snd_array_new(&spec->paths); if (!path) return false; memset(path, 0, sizeof(*path)); if (parse_nid_path(codec, dac, pin, 0, path)) return true; /* push back */ - spec->out_path.used--; + spec->paths.used--; return false; } @@ -3079,8 +3071,8 @@ static struct nid_path *get_out_path(struct hda_codec *codec, hda_nid_t pin, struct alc_spec *spec = codec->spec; int i; - for (i = 0; i < spec->out_path.used; i++) { - struct nid_path *path = snd_array_elem(&spec->out_path, i); + for (i = 0; i < spec->paths.used; i++) { + struct nid_path *path = snd_array_elem(&spec->paths, i); if (path->depth <= 0) continue; if ((!dac || path->path[0] == dac) && @@ -3258,7 +3250,7 @@ static int fill_and_eval_dacs(struct hda_codec *codec, memset(spec->multiout.hp_out_nid, 0, sizeof(spec->multiout.hp_out_nid)); memset(spec->multiout.extra_out_nid, 0, sizeof(spec->multiout.extra_out_nid)); spec->multi_ios = 0; - snd_array_free(&spec->out_path); + snd_array_free(&spec->paths); badness = 0; /* fill hard-wired DACs first */ @@ -3804,38 +3796,28 @@ static int alc_auto_create_speaker_out(struct hda_codec *codec) "Speaker"); } -static bool is_ctl_associated_in_list(struct snd_array *array, hda_nid_t nid, - int dir, int idx, int types) +/* check whether a control with the given (nid, dir, idx) was assigned */ +static bool is_ctl_associated(struct hda_codec *codec, hda_nid_t nid, + int dir, int idx) { + struct alc_spec *spec = codec->spec; int i, type; - for (i = 0; i < array->used; i++) { - struct nid_path *p = snd_array_elem(array, i); + for (i = 0; i < spec->paths.used; i++) { + struct nid_path *p = snd_array_elem(&spec->paths, i); if (p->depth <= 0) continue; for (type = 0; type < 2; type++) { - if (types & (1 << type)) { - unsigned int val = p->ctls[type]; - if (get_amp_nid_(val) == nid && - get_amp_direction_(val) == dir && - get_amp_index_(val) == idx) - return true; - } + unsigned int val = p->ctls[type]; + if (get_amp_nid_(val) == nid && + get_amp_direction_(val) == dir && + get_amp_index_(val) == idx) + return true; } } return false; } -/* check whether a control with the given (nid, dir, idx) was assigned */ -static bool is_ctl_associated(struct hda_codec *codec, hda_nid_t nid, - int dir, int idx) -{ - struct alc_spec *spec = codec->spec; - return is_ctl_associated_in_list(&spec->out_path, nid, dir, idx, 3) || - is_ctl_associated_in_list(&spec->in_path, nid, dir, idx, 3) || - is_ctl_associated_in_list(&spec->loopback_path, nid, dir, idx, 3); -} - /* can have the amp-in capability? */ static bool has_amp_in(struct hda_codec *codec, struct nid_path *path, int idx) { @@ -3864,13 +3846,15 @@ static bool has_amp_out(struct hda_codec *codec, struct nid_path *path, int idx) return true; } -static bool is_active_in_list(struct hda_codec *codec, struct snd_array *array, - hda_nid_t nid, int dir, int idx) +/* check whether the given (nid,dir,idx) is active */ +static bool is_active_nid(struct hda_codec *codec, hda_nid_t nid, + unsigned int idx, unsigned int dir) { + struct alc_spec *spec = codec->spec; int i, n; - for (n = 0; n < array->used; n++) { - struct nid_path *path = snd_array_elem(array, n); + for (n = 0; n < spec->paths.used; n++) { + struct nid_path *path = snd_array_elem(&spec->paths, n); if (!path->active) continue; for (i = 0; i < path->depth; i++) { @@ -3884,16 +3868,6 @@ static bool is_active_in_list(struct hda_codec *codec, struct snd_array *array, return false; } -/* check whether the given (nid,dir,idx) is active */ -static bool is_active_nid(struct hda_codec *codec, hda_nid_t nid, - unsigned int idx, unsigned int dir) -{ - struct alc_spec *spec = codec->spec; - return is_active_in_list(codec, &spec->out_path, nid, idx, dir) || - is_active_in_list(codec, &spec->in_path, nid, idx, dir) || - is_active_in_list(codec, &spec->loopback_path, nid, idx, dir); -} - /* get the default amp value for the target state */ static int get_amp_val_to_activate(struct hda_codec *codec, hda_nid_t nid, int dir, bool enable) @@ -4163,7 +4137,7 @@ static int alc_auto_fill_multi_ios(struct hda_codec *codec, } if (!hardwired && spec->multi_ios < 2) { /* cancel newly assigned paths */ - spec->out_path.used -= spec->multi_ios - old_pins; + spec->paths.used -= spec->multi_ios - old_pins; spec->multi_ios = old_pins; return badness; } @@ -4659,9 +4633,7 @@ static int alc_alloc_spec(struct hda_codec *codec, hda_nid_t mixer_nid) snd_hda_gen_init(&spec->gen); snd_array_init(&spec->kctls, sizeof(struct snd_kcontrol_new), 32); snd_array_init(&spec->bind_ctls, sizeof(struct hda_bind_ctls *), 8); - snd_array_init(&spec->out_path, sizeof(struct nid_path), 8); - snd_array_init(&spec->in_path, sizeof(struct nid_path), 8); - snd_array_init(&spec->loopback_path, sizeof(struct nid_path), 8); + snd_array_init(&spec->paths, sizeof(struct nid_path), 8); err = alc_codec_rename_from_preset(codec); if (err < 0) { -- cgit v1.2.3 From fef7fbbc5d6643513b27a706ed02ed4c46a0eef2 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 14 Dec 2012 16:54:44 +0100 Subject: ALSA: hda/realtek - Use path-based parser for digital outputs Similar like analog output paths, use the path list for parsing and initializing digital outputs as well. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 42 ++++++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 20 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 9a38107cf0f5..e25b13a09e58 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -1424,6 +1424,14 @@ static unsigned int alc_get_coef0(struct hda_codec *codec) return spec->coef0; } +static void alc_auto_set_output_and_unmute(struct hda_codec *codec, + hda_nid_t pin, int pin_type, + hda_nid_t dac); +static hda_nid_t alc_auto_look_for_dac(struct hda_codec *codec, hda_nid_t pin, + bool is_digital); +static bool add_new_out_path(struct hda_codec *codec, hda_nid_t pin, + hda_nid_t dac); + /* * Digital I/O handling */ @@ -1433,22 +1441,13 @@ static void alc_auto_init_digital(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; int i; - hda_nid_t pin, dac; + hda_nid_t pin; for (i = 0; i < spec->autocfg.dig_outs; i++) { pin = spec->autocfg.dig_out_pins[i]; if (!pin) continue; - snd_hda_set_pin_ctl(codec, pin, PIN_OUT); - if (!i) - dac = spec->multiout.dig_out_nid; - else - dac = spec->slave_dig_outs[i - 1]; - if (!dac || !(get_wcaps(codec, dac) & AC_WCAP_OUT_AMP)) - continue; - snd_hda_codec_write(codec, dac, 0, - AC_VERB_SET_AMP_GAIN_MUTE, - AMP_OUT_UNMUTE); + alc_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0); } pin = spec->autocfg.dig_in_pin; if (pin) @@ -1465,13 +1464,10 @@ static void alc_auto_parse_digital(struct hda_codec *codec) /* support multiple SPDIFs; the secondary is set up as a slave */ nums = 0; for (i = 0; i < spec->autocfg.dig_outs; i++) { - hda_nid_t conn[4]; - err = snd_hda_get_connections(codec, - spec->autocfg.dig_out_pins[i], - conn, ARRAY_SIZE(conn)); - if (err <= 0) + hda_nid_t pin = spec->autocfg.dig_out_pins[i]; + dig_nid = alc_auto_look_for_dac(codec, pin, true); + if (!dig_nid) continue; - dig_nid = conn[0]; /* assume the first element is audio-out */ if (!nums) { spec->multiout.dig_out_nid = dig_nid; spec->dig_out_type = spec->autocfg.dig_out_type[0]; @@ -1481,6 +1477,7 @@ static void alc_auto_parse_digital(struct hda_codec *codec) break; spec->slave_dig_outs[nums - 1] = dig_nid; } + add_new_out_path(codec, pin, dig_nid); nums++; } @@ -2895,15 +2892,20 @@ static bool alc_is_dac_already_used(struct hda_codec *codec, hda_nid_t nid) } /* look for an empty DAC slot */ -static hda_nid_t alc_auto_look_for_dac(struct hda_codec *codec, hda_nid_t pin) +static hda_nid_t alc_auto_look_for_dac(struct hda_codec *codec, hda_nid_t pin, + bool is_digital) { struct alc_spec *spec = codec->spec; + bool cap_digital; int i; for (i = 0; i < spec->num_all_dacs; i++) { hda_nid_t nid = spec->all_dacs[i]; if (!nid || alc_is_dac_already_used(codec, nid)) continue; + cap_digital = !!(get_wcaps(codec, nid) & AC_WCAP_DIGITAL); + if (is_digital != cap_digital) + continue; if (is_reachable_path(codec, nid, pin)) return nid; } @@ -3169,7 +3171,7 @@ static int alc_auto_fill_dacs(struct hda_codec *codec, int num_outs, for (i = 0; i < num_outs; i++) { hda_nid_t pin = pins[i]; if (!dacs[i]) - dacs[i] = alc_auto_look_for_dac(codec, pin); + dacs[i] = alc_auto_look_for_dac(codec, pin, false); if (!dacs[i] && !i) { for (j = 1; j < num_outs; j++) { if (is_reachable_path(codec, dacs[j], pin)) { @@ -4110,7 +4112,7 @@ static int alc_auto_fill_multi_ios(struct hda_codec *codec, if (hardwired) dac = get_dac_if_single(codec, nid); else if (!dac) - dac = alc_auto_look_for_dac(codec, nid); + dac = alc_auto_look_for_dac(codec, nid, false); if (!dac) { badness++; continue; -- cgit v1.2.3 From 6518f7ac5183ca77805f10323ea716fe86fd7c89 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 14 Dec 2012 17:34:51 +0100 Subject: ALSA: hda/realtek - Rename get_out_path() to get_nid_path() The function can be used not only for output paths but generically. Also swap the argument order. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index e25b13a09e58..76c67407996c 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -3064,11 +3064,11 @@ static bool add_new_out_path(struct hda_codec *codec, hda_nid_t pin, return false; } -/* get the path pointing from the given dac to pin; +/* get the path between the given NIDs; * passing 0 to either @pin or @dac behaves as a wildcard */ -static struct nid_path *get_out_path(struct hda_codec *codec, hda_nid_t pin, - hda_nid_t dac) +static struct nid_path * +get_nid_path(struct hda_codec *codec, hda_nid_t from_nid, hda_nid_t to_nid) { struct alc_spec *spec = codec->spec; int i; @@ -3077,8 +3077,8 @@ static struct nid_path *get_out_path(struct hda_codec *codec, hda_nid_t pin, struct nid_path *path = snd_array_elem(&spec->paths, i); if (path->depth <= 0) continue; - if ((!dac || path->path[0] == dac) && - (!pin || path->path[path->depth - 1] == pin)) + if ((!from_nid || path->path[0] == from_nid) && + (!to_nid || path->path[path->depth - 1] == to_nid)) return path; } return NULL; @@ -3094,7 +3094,7 @@ static struct nid_path *get_out_path(struct hda_codec *codec, hda_nid_t pin, static int assign_out_path_ctls(struct hda_codec *codec, hda_nid_t pin, hda_nid_t dac) { - struct nid_path *path = get_out_path(codec, pin, dac); + struct nid_path *path = get_nid_path(codec, dac, pin); hda_nid_t nid; unsigned int val; int badness = 0; @@ -3495,9 +3495,9 @@ static int alc_auto_fill_dac_nids(struct hda_codec *codec) debug_show_configs(spec, cfg); if (cfg->line_out_pins[0]) { - struct nid_path *path = get_out_path(codec, - cfg->line_out_pins[0], - spec->multiout.dac_nids[0]); + struct nid_path *path = get_nid_path(codec, + spec->multiout.dac_nids[0], + cfg->line_out_pins[0]); if (path) spec->vmaster_nid = alc_look_for_out_vol_nid(codec, path); } @@ -3641,7 +3641,7 @@ static int alc_auto_create_multi_out_ctls(struct hda_codec *codec, name = alc_get_line_out_pfx(spec, i, true, &index); } - path = get_out_path(codec, pin, dac); + path = get_nid_path(codec, dac, pin); if (!path) continue; if (!name || !strcmp(name, "CLFE")) { @@ -3677,7 +3677,7 @@ static int alc_auto_create_extra_out(struct hda_codec *codec, hda_nid_t pin, struct nid_path *path; int err; - path = get_out_path(codec, pin, dac); + path = get_nid_path(codec, dac, pin); if (!path) return 0; /* bind volume control will be created in the case of dac = 0 */ @@ -3763,7 +3763,7 @@ static int alc_auto_create_extra_outs(struct hda_codec *codec, int num_pins, struct nid_path *path; if (!pins[i] || !dacs[i]) continue; - path = get_out_path(codec, pins[i], dacs[i]); + path = get_nid_path(codec, dacs[i], pins[i]); if (!path) continue; vol = alc_look_for_out_vol_nid(codec, path); @@ -3975,7 +3975,7 @@ static void alc_auto_set_output_and_unmute(struct hda_codec *codec, struct nid_path *path; snd_hda_set_pin_ctl_cache(codec, pin, pin_type); - path = get_out_path(codec, pin, dac); + path = get_nid_path(codec, dac, pin); if (!path) return; activate_path(codec, path, true); @@ -4183,7 +4183,7 @@ static int alc_set_multi_io(struct hda_codec *codec, int idx, bool output) hda_nid_t nid = spec->multi_io[idx].pin; struct nid_path *path; - path = get_out_path(codec, nid, spec->multi_io[idx].dac); + path = get_nid_path(codec, spec->multi_io[idx].dac, nid); if (!path) return -EINVAL; -- cgit v1.2.3 From 0250f7cbea95c90564253cae8de80f0caad81fc0 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 14 Dec 2012 17:53:29 +0100 Subject: ALSA: hda/realtek - Fix the initialization of pin amp-in The pin widget has only a single amp value for the input even if it has multiple "sources". Handle the situation in activate_path(). Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 76c67407996c..b6b929ef23e1 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -3921,21 +3921,27 @@ static void activate_amp_in(struct hda_codec *codec, struct nid_path *path, { struct alc_spec *spec = codec->spec; hda_nid_t conn[16]; - int n, nums; + int n, nums, idx; hda_nid_t nid = path->path[i]; nums = snd_hda_get_connections(codec, nid, conn, ARRAY_SIZE(conn)); + if (get_wcaps_type(get_wcaps(codec, nid)) == AC_WID_PIN) { + nums = 1; + idx = 0; + } else + idx = path->idx[i]; + for (n = 0; n < nums; n++) init_amp(codec, nid, HDA_INPUT, n); - if (is_ctl_associated(codec, nid, HDA_INPUT, path->idx[i])) + if (is_ctl_associated(codec, nid, HDA_INPUT, idx)) return; /* here is a little bit tricky in comparison with activate_amp_out(); * when aa-mixer is available, we need to enable the path as well */ for (n = 0; n < nums; n++) { - if (n != path->idx[i] && conn[n] != spec->mixer_nid) + if (n != idx && conn[n] != spec->mixer_nid) continue; activate_amp(codec, nid, HDA_INPUT, n, enable); } -- cgit v1.2.3 From 3ebf1e940a3ffb05b6bd78190d6dc8389d1082ef Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 14 Dec 2012 18:04:37 +0100 Subject: ALSA: hda/realtek - Add missing initialization of multi-io routes The paths used for multi-io haven't been initialized properly, so far. It's usually no big matter because the pins are set to input as default, but it's still cleaner to initialize the paths properly. Now with the path active/inactive check, we can do it easily. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index b6b929ef23e1..74251b950bf3 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -3952,9 +3952,6 @@ static void activate_path(struct hda_codec *codec, struct nid_path *path, { int i; - if (path->active == enable) - return; - if (!enable) path->active = false; @@ -3984,6 +3981,8 @@ static void alc_auto_set_output_and_unmute(struct hda_codec *codec, path = get_nid_path(codec, dac, pin); if (!path) return; + if (path->active) + return; activate_path(codec, path, true); } @@ -4193,10 +4192,8 @@ static int alc_set_multi_io(struct hda_codec *codec, int idx, bool output) if (!path) return -EINVAL; - if (!spec->multi_io[idx].ctl_in) - spec->multi_io[idx].ctl_in = - snd_hda_codec_update_cache(codec, nid, 0, - AC_VERB_GET_PIN_WIDGET_CONTROL, 0); + if (path->active == output) + return 0; if (output) { snd_hda_set_pin_ctl_cache(codec, nid, PIN_OUT); @@ -4251,6 +4248,25 @@ static int alc_auto_add_multi_channel_mode(struct hda_codec *codec) return 0; } +static void alc_auto_init_multi_io(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + int i; + + for (i = 0; i < spec->multi_ios; i++) { + hda_nid_t pin = spec->multi_io[i].pin; + struct nid_path *path; + path = get_nid_path(codec, spec->multi_io[i].dac, pin); + if (!path) + continue; + if (!spec->multi_io[i].ctl_in) + spec->multi_io[i].ctl_in = + snd_hda_codec_update_cache(codec, pin, 0, + AC_VERB_GET_PIN_WIDGET_CONTROL, 0); + activate_path(codec, path, path->active); + } +} + /* filter out invalid adc_nids (and capsrc_nids) that don't give all * active input pins */ @@ -4489,6 +4505,7 @@ static void alc_auto_init_std(struct hda_codec *codec) { alc_auto_init_multi_out(codec); alc_auto_init_extra_out(codec); + alc_auto_init_multi_io(codec); alc_auto_init_analog_input(codec); alc_auto_init_input_src(codec); alc_auto_init_digital(codec); -- cgit v1.2.3 From 8dd48678584c4cf2588335f9ee816a5747980cf6 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 14 Dec 2012 18:19:04 +0100 Subject: ALSA: hda/realtek - Add boost volumes to path list Don't forget to take boost volumes into account in the managed path list. Since it's an additional volume, we need to extend the ctls[] array. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 74251b950bf3..9026b60bdc35 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -95,6 +95,13 @@ struct alc_multi_io { #define MAX_NID_PATH_DEPTH 5 +enum { + NID_PATH_VOL_CTL, + NID_PATH_MUTE_CTL, + NID_PATH_BOOST_CTL, + NID_PATH_NUM_CTLS +}; + /* Widget connection path * * For output, stored in the order of DAC -> ... -> pin, @@ -111,12 +118,10 @@ struct nid_path { hda_nid_t path[MAX_NID_PATH_DEPTH]; unsigned char idx[MAX_NID_PATH_DEPTH]; unsigned char multi[MAX_NID_PATH_DEPTH]; - unsigned int ctls[2]; /* 0 = volume, 1 = mute */ + unsigned int ctls[NID_PATH_NUM_CTLS]; /* NID_PATH_XXX_CTL */ bool active; }; -enum { NID_PATH_VOL_CTL = 0, NID_PATH_MUTE_CTL = 1 }; - struct alc_spec { struct hda_gen_spec gen; @@ -3809,7 +3814,7 @@ static bool is_ctl_associated(struct hda_codec *codec, hda_nid_t nid, struct nid_path *p = snd_array_elem(&spec->paths, i); if (p->depth <= 0) continue; - for (type = 0; type < 2; type++) { + for (type = 0; type < NID_PATH_NUM_CTLS; type++) { unsigned int val = p->ctls[type]; if (get_amp_nid_(val) == nid && get_amp_direction_(val) == dir && @@ -4388,6 +4393,8 @@ static int alc_auto_add_mic_boost(struct hda_codec *codec) if (get_wcaps(codec, nid) & AC_WCAP_IN_AMP) { const char *label; char boost_label[32]; + struct nid_path *path; + unsigned int val; label = hda_get_autocfg_input_label(codec, cfg, i); if (spec->shared_mic_hp && !strcmp(label, "Misc")) @@ -4400,11 +4407,15 @@ static int alc_auto_add_mic_boost(struct hda_codec *codec) snprintf(boost_label, sizeof(boost_label), "%s Boost Volume", label); + val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT); err = add_control(spec, ALC_CTL_WIDGET_VOL, - boost_label, type_idx, - HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT)); + boost_label, type_idx, val); if (err < 0) return err; + + path = get_nid_path(codec, nid, 0); + if (path) + path->ctls[NID_PATH_BOOST_CTL] = val; } } return 0; -- cgit v1.2.3 From 829f69ea590613c7a8b4b053266265947541b3f9 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 14 Dec 2012 18:26:02 +0100 Subject: ALSA: hda/realtek - Initialize loopback paths properly Now we have a complete list of loopback paths, thus we can initialize the paths more completely based on it, instead of assuming a direct connection from pin to mixer. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 9026b60bdc35..d5181b017375 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -2639,6 +2639,7 @@ static int new_analog_input(struct hda_codec *codec, hda_nid_t pin, { struct alc_spec *spec = codec->spec; struct nid_path *path; + unsigned int val; int err, idx; if (!nid_has_volume(codec, mix_nid, HDA_INPUT) && @@ -2654,19 +2655,22 @@ static int new_analog_input(struct hda_codec *codec, hda_nid_t pin, idx = path->idx[path->depth - 1]; if (nid_has_volume(codec, mix_nid, HDA_INPUT)) { - err = __add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, ctlname, ctlidx, - HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT)); + val = HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT); + err = __add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, ctlname, ctlidx, val); if (err < 0) return err; + path->ctls[NID_PATH_VOL_CTL] = val; } if (nid_has_mute(codec, mix_nid, HDA_INPUT)) { - err = __add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, ctlname, ctlidx, - HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT)); + val = HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT); + err = __add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, ctlname, ctlidx, val); if (err < 0) return err; + path->ctls[NID_PATH_MUTE_CTL] = val; } + path->active = true; add_loopback_list(spec, mix_nid, idx); return 0; } @@ -2848,6 +2852,11 @@ static int alc_auto_create_shared_input(struct hda_codec *codec) return 0; } +static struct nid_path * +get_nid_path(struct hda_codec *codec, hda_nid_t from_nid, hda_nid_t to_nid); +static void activate_path(struct hda_codec *codec, struct nid_path *path, + bool enable); + static int get_pin_type(int line_out_type) { if (line_out_type == AUTO_PIN_HP_OUT) @@ -2871,15 +2880,14 @@ static void alc_auto_init_analog_input(struct hda_codec *codec) AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); } - } - /* mute all loopback inputs */ - if (spec->mixer_nid) { - int nums = snd_hda_get_num_conns(codec, spec->mixer_nid); - for (i = 0; i < nums; i++) - snd_hda_codec_write(codec, spec->mixer_nid, 0, - AC_VERB_SET_AMP_GAIN_MUTE, - AMP_IN_MUTE(i)); + /* mute loopback inputs */ + if (spec->mixer_nid) { + struct nid_path *path; + path = get_nid_path(codec, nid, spec->mixer_nid); + if (path) + activate_path(codec, path, path->active); + } } } -- cgit v1.2.3 From 183a444a6d7e601ddfaba4a40304e020ba1f565c Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 17 Dec 2012 18:00:02 +0100 Subject: ALSA: hda/realtek - Don't change connection at path deactivation The widget connection selection must be changed only when the path is enabled. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index d5181b017375..1ec14ac24232 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -3969,7 +3969,7 @@ static void activate_path(struct hda_codec *codec, struct nid_path *path, path->active = false; for (i = path->depth - 1; i >= 0; i--) { - if (path->multi[i]) + if (enable && path->multi[i]) snd_hda_codec_write_cache(codec, path->path[i], 0, AC_VERB_SET_CONNECT_SEL, path->idx[i]); -- cgit v1.2.3 From 666a70d42b0fc04daaba64635741b649ed918fce Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 17 Dec 2012 20:29:29 +0100 Subject: ALSA: hda/realtek - Make input path parser more generic Now we reached to the final big piece of parser rewrite: the input paths. While the old parser code assumes the more-or-less direct and similar connections from input pin to ADC, the new code handles the complete input paths. The capture source is switched by simple calls of activate_path() function. The parsing of capture volume and capture switches is, however, not fully generalized. It assumes that amps are available in the vicinity of ADCs (in three depth). This isn't perfect but it should cover all codecs I know of. Also, this commit removes some NID mapping of capture-related controls temporarily for simplicity. It'll be restored in later commits. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 997 ++++++++++++++++-------------------------- 1 file changed, 374 insertions(+), 623 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 1ec14ac24232..3ee2be544f4d 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -278,6 +278,11 @@ static bool check_amp_caps(struct hda_codec *codec, hda_nid_t nid, #define nid_has_volume(codec, nid, dir) \ check_amp_caps(codec, nid, dir, AC_AMPCAP_NUM_STEPS) +static struct nid_path * +get_nid_path(struct hda_codec *codec, hda_nid_t from_nid, hda_nid_t to_nid); +static void activate_path(struct hda_codec *codec, struct nid_path *path, + bool enable, bool add_aamix); + /* * input MUX handling */ @@ -286,12 +291,7 @@ static int alc_mux_enum_info(struct snd_kcontrol *kcontrol, { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct alc_spec *spec = codec->spec; - unsigned int mux_idx = snd_ctl_get_ioffidx(kcontrol, &uinfo->id); - if (mux_idx >= spec->num_mux_defs) - mux_idx = 0; - if (!spec->input_mux[mux_idx].num_items && mux_idx > 0) - mux_idx = 0; - return snd_hda_input_mux_info(&spec->input_mux[mux_idx], uinfo); + return snd_hda_input_mux_info(&spec->input_mux[0], uinfo); } static int alc_mux_enum_get(struct snd_kcontrol *kcontrol, @@ -305,6 +305,14 @@ static int alc_mux_enum_get(struct snd_kcontrol *kcontrol, return 0; } +static hda_nid_t get_adc_nid(struct hda_codec *codec, int adc_idx, int imux_idx) +{ + struct alc_spec *spec = codec->spec; + if (spec->dyn_adc_switch) + adc_idx = spec->dyn_adc_idx[imux_idx]; + return spec->adc_nids[adc_idx]; +} + static bool alc_dyn_adc_pcm_resetup(struct hda_codec *codec, int cur) { struct alc_spec *spec = codec->spec; @@ -322,14 +330,9 @@ static bool alc_dyn_adc_pcm_resetup(struct hda_codec *codec, int cur) return false; } -static inline hda_nid_t get_capsrc(struct alc_spec *spec, int idx) -{ - return spec->capsrc_nids ? - spec->capsrc_nids[idx] : spec->adc_nids[idx]; -} - static void call_update_outputs(struct hda_codec *codec); static void alc_inv_dmic_sync(struct hda_codec *codec, bool force); +static void alc_inv_dmic_sync_adc(struct hda_codec *codec, int adc_idx); /* for shared I/O, change the pin-control accordingly */ static void update_shared_mic_hp(struct hda_codec *codec, bool set_as_mic) @@ -369,56 +372,39 @@ static int alc_mux_select(struct hda_codec *codec, unsigned int adc_idx, { struct alc_spec *spec = codec->spec; const struct hda_input_mux *imux; - unsigned int mux_idx; - int i, type, num_conns; - hda_nid_t nid; - - if (!spec->input_mux) - return 0; + struct nid_path *path; - mux_idx = adc_idx >= spec->num_mux_defs ? 0 : adc_idx; - imux = &spec->input_mux[mux_idx]; - if (!imux->num_items && mux_idx > 0) - imux = &spec->input_mux[0]; - if (!imux->num_items) + imux = spec->input_mux; + if (!imux || !imux->num_items) return 0; if (idx >= imux->num_items) idx = imux->num_items - 1; if (spec->cur_mux[adc_idx] == idx && !force) return 0; + + path = get_nid_path(codec, spec->imux_pins[spec->cur_mux[adc_idx]], + spec->adc_nids[adc_idx]); + if (!path) + return 0; + if (path->active) + activate_path(codec, path, false, false); + spec->cur_mux[adc_idx] = idx; if (spec->shared_mic_hp) update_shared_mic_hp(codec, spec->cur_mux[adc_idx]); - if (spec->dyn_adc_switch) { + if (spec->dyn_adc_switch) alc_dyn_adc_pcm_resetup(codec, idx); - adc_idx = spec->dyn_adc_idx[idx]; - } - - nid = get_capsrc(spec, adc_idx); - /* no selection? */ - num_conns = snd_hda_get_num_conns(codec, nid); - if (num_conns <= 1) - return 1; - - type = get_wcaps_type(get_wcaps(codec, nid)); - if (type == AC_WID_AUD_MIX) { - /* Matrix-mixer style (e.g. ALC882) */ - int active = imux->items[idx].index; - for (i = 0; i < num_conns; i++) { - unsigned int v = (i == active) ? 0 : HDA_AMP_MUTE; - snd_hda_codec_amp_stereo(codec, nid, HDA_INPUT, i, - HDA_AMP_MUTE, v); - } - } else { - /* MUX style (e.g. ALC880) */ - snd_hda_codec_write_cache(codec, nid, 0, - AC_VERB_SET_CONNECT_SEL, - imux->items[idx].index); - } + path = get_nid_path(codec, spec->imux_pins[idx], + get_adc_nid(codec, adc_idx, idx)); + if (!path) + return 0; + if (path->active) + return 0; + activate_path(codec, path, true, false); alc_inv_dmic_sync(codec, true); return 1; } @@ -1006,65 +992,12 @@ static int find_idx_in_nid_list(hda_nid_t nid, const hda_nid_t *list, int nums) return -1; } -/* check whether dynamic ADC-switching is available */ -static bool alc_check_dyn_adc_switch(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - struct hda_input_mux *imux = &spec->private_imux[0]; - int i, n, idx; - hda_nid_t cap, pin; - - if (imux != spec->input_mux) /* no dynamic imux? */ - return false; - - for (n = 0; n < spec->num_adc_nids; n++) { - cap = spec->private_capsrc_nids[n]; - for (i = 0; i < imux->num_items; i++) { - pin = spec->imux_pins[i]; - if (!pin) - return false; - if (get_connection_index(codec, cap, pin) < 0) - break; - } - if (i >= imux->num_items) - return true; /* no ADC-switch is needed */ - } - - for (i = 0; i < imux->num_items; i++) { - pin = spec->imux_pins[i]; - for (n = 0; n < spec->num_adc_nids; n++) { - cap = spec->private_capsrc_nids[n]; - idx = get_connection_index(codec, cap, pin); - if (idx >= 0) { - imux->items[i].index = idx; - spec->dyn_adc_idx[i] = n; - break; - } - } - } - - snd_printdd("realtek: enabling ADC switching\n"); - spec->dyn_adc_switch = 1; - return true; -} - /* check whether all auto-mic pins are valid; setup indices if OK */ static bool alc_auto_mic_check_imux(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; const struct hda_input_mux *imux; - if (!spec->auto_mic) - return false; - if (spec->auto_mic_valid_imux) - return true; /* already checked */ - - /* fill up imux indices */ - if (!alc_check_dyn_adc_switch(codec)) { - spec->auto_mic = 0; - return false; - } - imux = spec->input_mux; spec->ext_mic_idx = find_idx_in_nid_list(spec->ext_mic_pin, spec->imux_pins, imux->num_items); @@ -1072,10 +1005,8 @@ static bool alc_auto_mic_check_imux(struct hda_codec *codec) spec->imux_pins, imux->num_items); spec->dock_mic_idx = find_idx_in_nid_list(spec->dock_mic_pin, spec->imux_pins, imux->num_items); - if (spec->ext_mic_idx < 0 || spec->int_mic_idx < 0) { - spec->auto_mic = 0; + if (spec->ext_mic_idx < 0 || spec->int_mic_idx < 0) return false; /* no corresponding imux */ - } snd_hda_jack_detect_enable_callback(codec, spec->ext_mic_pin, ALC_MIC_EVENT, alc_mic_automute); @@ -1085,7 +1016,6 @@ static bool alc_auto_mic_check_imux(struct hda_codec *codec) alc_mic_automute); spec->auto_mic_valid_imux = 1; - spec->auto_mic = 1; return true; } @@ -1100,9 +1030,6 @@ static int alc_init_auto_mic(struct hda_codec *codec) hda_nid_t fixed, ext, dock; int i; - if (spec->shared_mic_hp) - return 0; /* no auto-mic for the shared I/O */ - spec->ext_mic_idx = spec->int_mic_idx = spec->dock_mic_idx = -1; fixed = ext = dock = 0; @@ -1152,30 +1079,18 @@ static int alc_init_auto_mic(struct hda_codec *codec) spec->int_mic_pin = fixed; spec->dock_mic_pin = dock; - spec->auto_mic = 1; if (!alc_auto_mic_check_imux(codec)) return 0; + spec->auto_mic = 1; + spec->num_adc_nids = 1; + spec->cur_mux[0] = spec->int_mic_idx; snd_printdd("realtek: Enable auto-mic switch on NID 0x%x/0x%x/0x%x\n", ext, fixed, dock); return 0; } -/* check the availabilities of auto-mute and auto-mic switches */ -static int alc_auto_check_switches(struct hda_codec *codec) -{ - int err; - - err = alc_init_automute(codec); - if (err < 0) - return err; - err = alc_init_auto_mic(codec); - if (err < 0) - return err; - return 0; -} - /* * Realtek SSID verification */ @@ -1509,169 +1424,101 @@ static void alc_auto_parse_digital(struct hda_codec *codec) /* * capture mixer elements */ -static int alc_cap_vol_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct alc_spec *spec = codec->spec; - unsigned long val; - int err; - - mutex_lock(&codec->control_mutex); - if (spec->vol_in_capsrc) - val = HDA_COMPOSE_AMP_VAL(spec->capsrc_nids[0], 3, 0, HDA_OUTPUT); - else - val = HDA_COMPOSE_AMP_VAL(spec->adc_nids[0], 3, 0, HDA_INPUT); - kcontrol->private_value = val; - err = snd_hda_mixer_amp_volume_info(kcontrol, uinfo); - mutex_unlock(&codec->control_mutex); - return err; -} - -static int alc_cap_vol_tlv(struct snd_kcontrol *kcontrol, int op_flag, - unsigned int size, unsigned int __user *tlv) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct alc_spec *spec = codec->spec; - unsigned long val; - int err; - - mutex_lock(&codec->control_mutex); - if (spec->vol_in_capsrc) - val = HDA_COMPOSE_AMP_VAL(spec->capsrc_nids[0], 3, 0, HDA_OUTPUT); - else - val = HDA_COMPOSE_AMP_VAL(spec->adc_nids[0], 3, 0, HDA_INPUT); - kcontrol->private_value = val; - err = snd_hda_mixer_amp_tlv(kcontrol, op_flag, size, tlv); - mutex_unlock(&codec->control_mutex); - return err; -} +#define alc_cap_vol_info snd_hda_mixer_amp_volume_info +#define alc_cap_vol_get snd_hda_mixer_amp_volume_get +#define alc_cap_vol_tlv snd_hda_mixer_amp_tlv -typedef int (*getput_call_t)(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol); +typedef int (*put_call_t)(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); -static int alc_cap_getput_caller(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol, - getput_call_t func, bool is_put) +static int alc_cap_put_caller(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol, + put_call_t func, int type) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct alc_spec *spec = codec->spec; - int i, err = 0; + const struct hda_input_mux *imux; + struct nid_path *path; + int i, adc_idx, err = 0; + imux = spec->input_mux; + adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); mutex_lock(&codec->control_mutex); - if (is_put && spec->dyn_adc_switch) { - for (i = 0; i < spec->num_adc_nids; i++) { - kcontrol->private_value = - HDA_COMPOSE_AMP_VAL(spec->adc_nids[i], - 3, 0, HDA_INPUT); - err = func(kcontrol, ucontrol); - if (err < 0) - goto error; - } - } else { - i = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); - if (spec->vol_in_capsrc) - kcontrol->private_value = - HDA_COMPOSE_AMP_VAL(spec->capsrc_nids[i], - 3, 0, HDA_OUTPUT); - else - kcontrol->private_value = - HDA_COMPOSE_AMP_VAL(spec->adc_nids[i], - 3, 0, HDA_INPUT); + codec->cached_write = 1; + for (i = 0; i < imux->num_items; i++) { + path = get_nid_path(codec, spec->imux_pins[i], + get_adc_nid(codec, adc_idx, i)); + if (!path->ctls[type]) + continue; + kcontrol->private_value = path->ctls[type]; err = func(kcontrol, ucontrol); + if (err < 0) + goto error; } - if (err >= 0 && is_put) - alc_inv_dmic_sync(codec, false); error: + codec->cached_write = 0; mutex_unlock(&codec->control_mutex); + snd_hda_codec_resume_amp(codec); + if (err >= 0 && type == NID_PATH_MUTE_CTL && + spec->inv_dmic_fixup && spec->inv_dmic_muted) + alc_inv_dmic_sync_adc(codec, adc_idx); return err; } -static int alc_cap_vol_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - return alc_cap_getput_caller(kcontrol, ucontrol, - snd_hda_mixer_amp_volume_get, false); -} - static int alc_cap_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - return alc_cap_getput_caller(kcontrol, ucontrol, - snd_hda_mixer_amp_volume_put, true); + return alc_cap_put_caller(kcontrol, ucontrol, + snd_hda_mixer_amp_volume_put, + NID_PATH_VOL_CTL); } /* capture mixer elements */ #define alc_cap_sw_info snd_ctl_boolean_stereo_info +#define alc_cap_sw_get snd_hda_mixer_amp_switch_get -static int alc_cap_sw_get(struct snd_kcontrol *kcontrol, +static int alc_cap_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - return alc_cap_getput_caller(kcontrol, ucontrol, - snd_hda_mixer_amp_switch_get, false); + return alc_cap_put_caller(kcontrol, ucontrol, + snd_hda_mixer_amp_switch_put, + NID_PATH_MUTE_CTL); } -static int alc_cap_sw_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +static void alc_inv_dmic_sync_adc(struct hda_codec *codec, int adc_idx) { - return alc_cap_getput_caller(kcontrol, ucontrol, - snd_hda_mixer_amp_switch_put, true); -} - -#define _DEFINE_CAPMIX(num) \ - { \ - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ - .name = "Capture Switch", \ - .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \ - .count = num, \ - .info = alc_cap_sw_info, \ - .get = alc_cap_sw_get, \ - .put = alc_cap_sw_put, \ - }, \ - { \ - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ - .name = "Capture Volume", \ - .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | \ - SNDRV_CTL_ELEM_ACCESS_TLV_READ | \ - SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK), \ - .count = num, \ - .info = alc_cap_vol_info, \ - .get = alc_cap_vol_get, \ - .put = alc_cap_vol_put, \ - .tlv = { .c = alc_cap_vol_tlv }, \ - } - -#define _DEFINE_CAPSRC(num) \ - { \ - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ - /* .name = "Capture Source", */ \ - .name = "Input Source", \ - .count = num, \ - .info = alc_mux_enum_info, \ - .get = alc_mux_enum_get, \ - .put = alc_mux_enum_put, \ - } - -#define DEFINE_CAPMIX(num) \ -static const struct snd_kcontrol_new alc_capture_mixer ## num[] = { \ - _DEFINE_CAPMIX(num), \ - _DEFINE_CAPSRC(num), \ - { } /* end */ \ -} - -#define DEFINE_CAPMIX_NOSRC(num) \ -static const struct snd_kcontrol_new alc_capture_mixer_nosrc ## num[] = { \ - _DEFINE_CAPMIX(num), \ - { } /* end */ \ -} - -/* up to three ADCs */ -DEFINE_CAPMIX(1); -DEFINE_CAPMIX(2); -DEFINE_CAPMIX(3); -DEFINE_CAPMIX_NOSRC(1); -DEFINE_CAPMIX_NOSRC(2); -DEFINE_CAPMIX_NOSRC(3); + struct alc_spec *spec = codec->spec; + struct hda_input_mux *imux = &spec->private_imux[0]; + struct nid_path *path; + hda_nid_t nid; + int i, dir, parm; + unsigned int val; + + for (i = 0; i < imux->num_items; i++) { + if (spec->imux_pins[i] == spec->inv_dmic_pin) + break; + } + if (i >= imux->num_items) + return; + + path = get_nid_path(codec, spec->inv_dmic_pin, + get_adc_nid(codec, adc_idx, i)); + val = path->ctls[NID_PATH_MUTE_CTL]; + if (!val) + return; + nid = get_amp_nid_(val); + dir = get_amp_direction_(val); + parm = AC_AMP_SET_RIGHT | + (dir == HDA_OUTPUT ? AC_AMP_SET_OUTPUT : AC_AMP_SET_INPUT); + + /* we care only right channel */ + val = snd_hda_codec_amp_read(codec, nid, 1, dir, 0); + if (val & 0x80) /* if already muted, we don't need to touch */ + return; + val |= 0x80; + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, + parm | val); +} /* * Inverted digital-mic handling @@ -1691,40 +1538,22 @@ DEFINE_CAPMIX_NOSRC(3); static void alc_inv_dmic_sync(struct hda_codec *codec, bool force) { struct alc_spec *spec = codec->spec; - int i; + int src, nums; if (!spec->inv_dmic_fixup) return; if (!spec->inv_dmic_muted && !force) return; - for (i = 0; i < spec->num_adc_nids; i++) { - int src = spec->dyn_adc_switch ? 0 : i; + nums = spec->dyn_adc_switch ? 1 : spec->num_adc_nids; + for (src = 0; src < nums; src++) { bool dmic_fixup = false; - hda_nid_t nid; - int parm, dir, v; if (spec->inv_dmic_muted && spec->imux_pins[spec->cur_mux[src]] == spec->inv_dmic_pin) dmic_fixup = true; if (!dmic_fixup && !force) continue; - if (spec->vol_in_capsrc) { - nid = spec->capsrc_nids[i]; - parm = AC_AMP_SET_RIGHT | AC_AMP_SET_OUTPUT; - dir = HDA_OUTPUT; - } else { - nid = spec->adc_nids[i]; - parm = AC_AMP_SET_RIGHT | AC_AMP_SET_INPUT; - dir = HDA_INPUT; - } - /* we care only right channel */ - v = snd_hda_codec_amp_read(codec, nid, 1, dir, 0); - if (v & 0x80) /* if already muted, we don't need to touch */ - continue; - if (dmic_fixup) /* add mute for d-mic */ - v |= 0x80; - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, - parm | v); + alc_inv_dmic_sync_adc(codec, src); } } @@ -1800,13 +1629,6 @@ static const char * const alc_slave_pfxs[] = { #define NID_MAPPING (-1) -#define SUBDEV_SPEAKER_ (0 << 6) -#define SUBDEV_HP_ (1 << 6) -#define SUBDEV_LINE_ (2 << 6) -#define SUBDEV_SPEAKER(x) (SUBDEV_SPEAKER_ | ((x) & 0x3f)) -#define SUBDEV_HP(x) (SUBDEV_HP_ | ((x) & 0x3f)) -#define SUBDEV_LINE(x) (SUBDEV_LINE_ | ((x) & 0x3f)) - static void alc_free_kctls(struct hda_codec *codec); #ifdef CONFIG_SND_HDA_INPUT_BEEP @@ -1821,11 +1643,7 @@ static const struct snd_kcontrol_new alc_beep_mixer[] = { static int __alc_build_controls(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - struct snd_kcontrol *kctl = NULL; - const struct snd_kcontrol_new *knew; - int i, j, err; - unsigned int u; - hda_nid_t nid; + int i, err; for (i = 0; i < spec->num_mixers; i++) { err = snd_hda_add_new_ctls(codec, spec->mixers[i]); @@ -1897,75 +1715,6 @@ static int __alc_build_controls(struct hda_codec *codec) return err; } - /* assign Capture Source enums to NID */ - if (spec->capsrc_nids || spec->adc_nids) { - kctl = snd_hda_find_mixer_ctl(codec, "Capture Source"); - if (!kctl) - kctl = snd_hda_find_mixer_ctl(codec, "Input Source"); - for (i = 0; kctl && i < kctl->count; i++) { - err = snd_hda_add_nid(codec, kctl, i, - get_capsrc(spec, i)); - if (err < 0) - return err; - } - } - if (spec->cap_mixer && spec->adc_nids) { - const char *kname = kctl ? kctl->id.name : NULL; - for (knew = spec->cap_mixer; knew->name; knew++) { - if (kname && strcmp(knew->name, kname) == 0) - continue; - kctl = snd_hda_find_mixer_ctl(codec, knew->name); - for (i = 0; kctl && i < kctl->count; i++) { - err = snd_hda_add_nid(codec, kctl, i, - spec->adc_nids[i]); - if (err < 0) - return err; - } - } - } - - /* other nid->control mapping */ - for (i = 0; i < spec->num_mixers; i++) { - for (knew = spec->mixers[i]; knew->name; knew++) { - if (knew->iface != NID_MAPPING) - continue; - kctl = snd_hda_find_mixer_ctl(codec, knew->name); - if (kctl == NULL) - continue; - u = knew->subdevice; - for (j = 0; j < 4; j++, u >>= 8) { - nid = u & 0x3f; - if (nid == 0) - continue; - switch (u & 0xc0) { - case SUBDEV_SPEAKER_: - nid = spec->autocfg.speaker_pins[nid]; - break; - case SUBDEV_LINE_: - nid = spec->autocfg.line_out_pins[nid]; - break; - case SUBDEV_HP_: - nid = spec->autocfg.hp_pins[nid]; - break; - default: - continue; - } - err = snd_hda_add_nid(codec, kctl, 0, nid); - if (err < 0) - return err; - } - u = knew->private_value; - for (j = 0; j < 4; j++, u >>= 8) { - nid = u & 0xff; - if (nid == 0) - continue; - err = snd_hda_add_nid(codec, kctl, 0, nid); - if (err < 0) - return err; - } - } - } - alc_free_kctls(codec); /* no longer needed */ return 0; @@ -2007,7 +1756,6 @@ static int alc_build_controls(struct hda_codec *codec) * Common callbacks */ -static void alc_init_special_input_src(struct hda_codec *codec); static void alc_auto_init_std(struct hda_codec *codec); static int alc_init(struct hda_codec *codec) @@ -2021,7 +1769,6 @@ static int alc_init(struct hda_codec *codec) alc_auto_init_amp(codec, spec->init_amp); snd_hda_gen_apply_verbs(codec); - alc_init_special_input_src(codec); alc_auto_init_std(codec); alc_apply_fixup(codec, ALC_FIXUP_ACT_INIT); @@ -2675,28 +2422,6 @@ static int new_analog_input(struct hda_codec *codec, hda_nid_t pin, return 0; } -static int new_capture_source(struct hda_codec *codec, int adc_idx, - hda_nid_t pin, int idx, const char *label) -{ - struct alc_spec *spec = codec->spec; - struct hda_input_mux *imux = &spec->private_imux[0]; - struct nid_path *path; - - path = snd_array_new(&spec->paths); - if (!path) - return -ENOMEM; - memset(path, 0, sizeof(*path)); - if (!parse_nid_path(codec, pin, spec->adc_nids[adc_idx], 2, path)) { - snd_printd(KERN_ERR "invalid input path 0x%x -> 0x%x\n", - pin, spec->adc_nids[adc_idx]); - return -EINVAL; - } - - spec->imux_pins[imux->num_items] = pin; - snd_hda_add_imux_item(imux, label, idx, NULL); - return 0; -} - static int alc_is_input_pin(struct hda_codec *codec, hda_nid_t nid) { unsigned int pincap = snd_hda_query_pin_caps(codec, nid); @@ -2712,54 +2437,217 @@ static bool is_reachable_path(struct hda_codec *codec, return snd_hda_get_conn_index(codec, to_nid, from_nid, true) >= 0; } -/* Parse the codec tree and retrieve ADCs and corresponding capsrc MUXs */ -static int alc_auto_fill_adc_caps(struct hda_codec *codec) +/* Parse the codec tree and retrieve ADCs */ +static int alc_auto_fill_adc_nids(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; hda_nid_t nid; hda_nid_t *adc_nids = spec->private_adc_nids; - hda_nid_t *cap_nids = spec->private_capsrc_nids; int max_nums = ARRAY_SIZE(spec->private_adc_nids); int i, nums = 0; nid = codec->start_nid; for (i = 0; i < codec->num_nodes; i++, nid++) { - hda_nid_t src; unsigned int caps = get_wcaps(codec, nid); int type = get_wcaps_type(caps); if (type != AC_WID_AUD_IN || (caps & AC_WCAP_DIGITAL)) continue; adc_nids[nums] = nid; - cap_nids[nums] = nid; - src = nid; - for (;;) { - int n; - type = get_wcaps_type(get_wcaps(codec, src)); - if (type == AC_WID_PIN) - break; - if (type == AC_WID_AUD_SEL) { - cap_nids[nums] = src; - break; - } - n = snd_hda_get_num_conns(codec, src); - if (n > 1) { - cap_nids[nums] = src; - break; - } else if (n != 1) - break; - if (snd_hda_get_connections(codec, src, &src, 1) != 1) - break; - } if (++nums >= max_nums) break; } spec->adc_nids = spec->private_adc_nids; - spec->capsrc_nids = spec->private_capsrc_nids; spec->num_adc_nids = nums; return nums; } +/* filter out invalid adc_nids that don't give all active input pins; + * if needed, check whether dynamic ADC-switching is available + */ +static int check_dyn_adc_switch(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + struct hda_input_mux *imux = &spec->private_imux[0]; + hda_nid_t adc_nids[ARRAY_SIZE(spec->private_adc_nids)]; + int i, n, nums; + hda_nid_t pin, adc; + + again: + nums = 0; + for (n = 0; n < spec->num_adc_nids; n++) { + adc = spec->adc_nids[n]; + for (i = 0; i < imux->num_items; i++) { + pin = spec->imux_pins[i]; + if (!is_reachable_path(codec, pin, adc)) + break; + } + if (i >= imux->num_items) + adc_nids[nums++] = adc; + } + + if (!nums) { + if (spec->shared_mic_hp) { + spec->shared_mic_hp = 0; + spec->private_imux[0].num_items = 1; + goto again; + } + + /* check whether ADC-switch is possible */ + for (i = 0; i < imux->num_items; i++) { + pin = spec->imux_pins[i]; + for (n = 0; n < spec->num_adc_nids; n++) { + adc = spec->adc_nids[n]; + if (is_reachable_path(codec, pin, adc)) { + spec->dyn_adc_idx[i] = n; + break; + } + } + } + + snd_printdd("realtek: enabling ADC switching\n"); + spec->dyn_adc_switch = 1; + } else if (nums != spec->num_adc_nids) { + memcpy(spec->private_adc_nids, adc_nids, + nums * sizeof(hda_nid_t)); + spec->num_adc_nids = nums; + } + + if (spec->input_mux->num_items == 1 || spec->shared_mic_hp) { + snd_printdd("realtek: reducing to a single ADC\n"); + spec->num_adc_nids = 1; /* reduce to a single ADC */ + } + + return 0; +} + +/* templates for capture controls */ +static const struct snd_kcontrol_new cap_src_temp = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Input Source", + .info = alc_mux_enum_info, + .get = alc_mux_enum_get, + .put = alc_mux_enum_put, +}; + +static const struct snd_kcontrol_new cap_vol_temp = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Capture Volume", + .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ | + SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK), + .info = alc_cap_vol_info, + .get = alc_cap_vol_get, + .put = alc_cap_vol_put, + .tlv = { .c = alc_cap_vol_tlv }, +}; + +static const struct snd_kcontrol_new cap_sw_temp = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Capture Switch", + .info = alc_cap_sw_info, + .get = alc_cap_sw_get, + .put = alc_cap_sw_put, +}; + +static int parse_capvol_in_path(struct hda_codec *codec, struct nid_path *path) +{ + hda_nid_t nid; + int i, depth; + + path->ctls[NID_PATH_VOL_CTL] = path->ctls[NID_PATH_MUTE_CTL] = 0; + for (depth = 0; depth < 3; depth++) { + if (depth >= path->depth) + return -EINVAL; + i = path->depth - depth - 1; + nid = path->path[i]; + if (!path->ctls[NID_PATH_VOL_CTL]) { + if (nid_has_volume(codec, nid, HDA_OUTPUT)) + path->ctls[NID_PATH_VOL_CTL] = + HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT); + else if (nid_has_volume(codec, nid, HDA_INPUT)) { + int idx = path->idx[i]; + if (!depth && codec->single_adc_amp) + idx = 0; + path->ctls[NID_PATH_VOL_CTL] = + HDA_COMPOSE_AMP_VAL(nid, 3, idx, HDA_INPUT); + } + } + if (!path->ctls[NID_PATH_MUTE_CTL]) { + if (nid_has_mute(codec, nid, HDA_OUTPUT)) + path->ctls[NID_PATH_MUTE_CTL] = + HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT); + else if (nid_has_mute(codec, nid, HDA_INPUT)) { + int idx = path->idx[i]; + if (!depth && codec->single_adc_amp) + idx = 0; + path->ctls[NID_PATH_MUTE_CTL] = + HDA_COMPOSE_AMP_VAL(nid, 3, idx, HDA_INPUT); + } + } + } + return 0; +} + +static int create_capture_mixers(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + struct hda_input_mux *imux = &spec->private_imux[0]; + struct snd_kcontrol_new *knew; + int i, n, nums; + + if (spec->dyn_adc_switch) + nums = 1; + else + nums = spec->num_adc_nids; + + if (!spec->auto_mic && imux->num_items > 1) { + knew = alc_kcontrol_new(spec, "Input Source", &cap_src_temp); + if (!knew) + return -ENOMEM; + knew->count = nums; + } + + for (n = 0; n < nums; n++) { + int vol, sw; + + vol = sw = 0; + for (i = 0; i < imux->num_items; i++) { + struct nid_path *path; + path = get_nid_path(codec, spec->imux_pins[i], + get_adc_nid(codec, n, i)); + if (!path) + continue; + parse_capvol_in_path(codec, path); + if (!vol) + vol = path->ctls[NID_PATH_VOL_CTL]; + if (!sw) + sw = path->ctls[NID_PATH_MUTE_CTL]; + } + + if (vol) { + knew = alc_kcontrol_new(spec, "Capture Volume", + &cap_vol_temp); + if (!knew) + return -ENOMEM; + knew->index = n; + knew->private_value = vol; + knew->subdevice = HDA_SUBDEV_AMP_FLAG; + } + if (sw) { + knew = alc_kcontrol_new(spec, "Capture Switch", + &cap_sw_temp); + if (!knew) + return -ENOMEM; + knew->index = n; + knew->private_value = sw; + knew->subdevice = HDA_SUBDEV_AMP_FLAG; + } + } + + return 0; +} + /* create playback/capture controls for input pins */ static int alc_auto_create_input_ctls(struct hda_codec *codec) { @@ -2768,16 +2656,17 @@ static int alc_auto_create_input_ctls(struct hda_codec *codec) hda_nid_t mixer = spec->mixer_nid; struct hda_input_mux *imux = &spec->private_imux[0]; int num_adcs; - int i, c, err, idx, type_idx = 0; + int i, c, err, type_idx = 0; const char *prev_label = NULL; - num_adcs = alc_auto_fill_adc_caps(codec); + num_adcs = alc_auto_fill_adc_nids(codec); if (num_adcs < 0) return 0; for (i = 0; i < cfg->num_inputs; i++) { hda_nid_t pin; const char *label; + bool imux_added; pin = cfg->inputs[i].pin; if (!alc_is_input_pin(codec, pin)) @@ -2801,21 +2690,35 @@ static int alc_auto_create_input_ctls(struct hda_codec *codec) } } + imux_added = false; for (c = 0; c < num_adcs; c++) { - hda_nid_t cap = get_capsrc(spec, c); - idx = get_connection_index(codec, cap, pin); - if (idx >= 0) { - err = new_capture_source(codec, c, pin, idx, label); - if (err < 0) - return err; - break; + struct nid_path *path; + hda_nid_t adc = spec->adc_nids[c]; + + if (!is_reachable_path(codec, pin, adc)) + continue; + path = snd_array_new(&spec->paths); + if (!path) + return -ENOMEM; + memset(path, 0, sizeof(*path)); + if (!parse_nid_path(codec, pin, adc, 2, path)) { + snd_printd(KERN_ERR + "invalid input path 0x%x -> 0x%x\n", + pin, adc); + spec->paths.used--; + continue; + } + + if (!imux_added) { + spec->imux_pins[imux->num_items] = pin; + snd_hda_add_imux_item(imux, label, + imux->num_items, NULL); + imux_added = true; } } } - spec->num_mux_defs = 1; spec->input_mux = imux; - return 0; } @@ -2852,11 +2755,6 @@ static int alc_auto_create_shared_input(struct hda_codec *codec) return 0; } -static struct nid_path * -get_nid_path(struct hda_codec *codec, hda_nid_t from_nid, hda_nid_t to_nid); -static void activate_path(struct hda_codec *codec, struct nid_path *path, - bool enable); - static int get_pin_type(int line_out_type) { if (line_out_type == AUTO_PIN_HP_OUT) @@ -2886,7 +2784,7 @@ static void alc_auto_init_analog_input(struct hda_codec *codec) struct nid_path *path; path = get_nid_path(codec, nid, spec->mixer_nid); if (path) - activate_path(codec, path, path->active); + activate_path(codec, path, path->active, false); } } } @@ -3930,15 +3828,18 @@ static void activate_amp_out(struct hda_codec *codec, struct nid_path *path, } static void activate_amp_in(struct hda_codec *codec, struct nid_path *path, - int i, bool enable) + int i, bool enable, bool add_aamix) { struct alc_spec *spec = codec->spec; hda_nid_t conn[16]; int n, nums, idx; + int type; hda_nid_t nid = path->path[i]; nums = snd_hda_get_connections(codec, nid, conn, ARRAY_SIZE(conn)); - if (get_wcaps_type(get_wcaps(codec, nid)) == AC_WID_PIN) { + type = get_wcaps_type(get_wcaps(codec, nid)); + if (type == AC_WID_PIN || + (type == AC_WID_AUD_IN && codec->single_adc_amp)) { nums = 1; idx = 0; } else @@ -3954,14 +3855,14 @@ static void activate_amp_in(struct hda_codec *codec, struct nid_path *path, * when aa-mixer is available, we need to enable the path as well */ for (n = 0; n < nums; n++) { - if (n != idx && conn[n] != spec->mixer_nid) + if (n != idx && (!add_aamix || conn[n] != spec->mixer_nid)) continue; activate_amp(codec, nid, HDA_INPUT, n, enable); } } static void activate_path(struct hda_codec *codec, struct nid_path *path, - bool enable) + bool enable, bool add_aamix) { int i; @@ -3974,7 +3875,7 @@ static void activate_path(struct hda_codec *codec, struct nid_path *path, AC_VERB_SET_CONNECT_SEL, path->idx[i]); if (has_amp_in(codec, path, i)) - activate_amp_in(codec, path, i, enable); + activate_amp_in(codec, path, i, enable, add_aamix); if (has_amp_out(codec, path, i)) activate_amp_out(codec, path, i, enable); } @@ -3996,7 +3897,7 @@ static void alc_auto_set_output_and_unmute(struct hda_codec *codec, return; if (path->active) return; - activate_path(codec, path, true); + activate_path(codec, path, true, true); } static void alc_auto_init_multi_out(struct hda_codec *codec) @@ -4210,9 +4111,9 @@ static int alc_set_multi_io(struct hda_codec *codec, int idx, bool output) if (output) { snd_hda_set_pin_ctl_cache(codec, nid, PIN_OUT); - activate_path(codec, path, true); + activate_path(codec, path, true, true); } else { - activate_path(codec, path, false); + activate_path(codec, path, false, true); snd_hda_set_pin_ctl_cache(codec, nid, spec->multi_io[idx].ctl_in); } @@ -4276,112 +4177,41 @@ static void alc_auto_init_multi_io(struct hda_codec *codec) spec->multi_io[i].ctl_in = snd_hda_codec_update_cache(codec, pin, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0); - activate_path(codec, path, path->active); - } -} - -/* filter out invalid adc_nids (and capsrc_nids) that don't give all - * active input pins - */ -static void alc_remove_invalid_adc_nids(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - const struct hda_input_mux *imux; - hda_nid_t adc_nids[ARRAY_SIZE(spec->private_adc_nids)]; - hda_nid_t capsrc_nids[ARRAY_SIZE(spec->private_adc_nids)]; - int i, n, nums; - - imux = spec->input_mux; - if (!imux) - return; - if (spec->dyn_adc_switch) - return; - - again: - nums = 0; - for (n = 0; n < spec->num_adc_nids; n++) { - hda_nid_t cap = spec->private_capsrc_nids[n]; - int num_conns = snd_hda_get_num_conns(codec, cap); - for (i = 0; i < imux->num_items; i++) { - hda_nid_t pin = spec->imux_pins[i]; - if (pin) { - if (get_connection_index(codec, cap, pin) < 0) - break; - } else if (num_conns <= imux->items[i].index) - break; - } - if (i >= imux->num_items) { - adc_nids[nums] = spec->private_adc_nids[n]; - capsrc_nids[nums++] = cap; - } + activate_path(codec, path, path->active, true); } - if (!nums) { - /* check whether ADC-switch is possible */ - if (!alc_check_dyn_adc_switch(codec)) { - if (spec->shared_mic_hp) { - spec->shared_mic_hp = 0; - spec->private_imux[0].num_items = 1; - goto again; - } - printk(KERN_WARNING "hda_codec: %s: no valid ADC found;" - " using fallback 0x%x\n", - codec->chip_name, spec->private_adc_nids[0]); - spec->num_adc_nids = 1; - spec->auto_mic = 0; - return; - } - } else if (nums != spec->num_adc_nids) { - memcpy(spec->private_adc_nids, adc_nids, - nums * sizeof(hda_nid_t)); - memcpy(spec->private_capsrc_nids, capsrc_nids, - nums * sizeof(hda_nid_t)); - spec->num_adc_nids = nums; - } - - if (spec->auto_mic) - alc_auto_mic_check_imux(codec); /* check auto-mic setups */ - else if (spec->input_mux->num_items == 1 || spec->shared_mic_hp) - spec->num_adc_nids = 1; /* reduce to a single ADC */ } /* * initialize ADC paths */ -static void alc_auto_init_adc(struct hda_codec *codec, int adc_idx) -{ - struct alc_spec *spec = codec->spec; - hda_nid_t nid; - - nid = spec->adc_nids[adc_idx]; - /* mute ADC */ - if (nid_has_mute(codec, nid, HDA_INPUT)) { - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_AMP_GAIN_MUTE, - AMP_IN_MUTE(0)); - return; - } - if (!spec->capsrc_nids) - return; - nid = spec->capsrc_nids[adc_idx]; - if (nid_has_mute(codec, nid, HDA_OUTPUT)) - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_AMP_GAIN_MUTE, - AMP_OUT_MUTE); -} - static void alc_auto_init_input_src(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - int c, nums; + struct hda_input_mux *imux = &spec->private_imux[0]; + struct nid_path *path; + int i, c, nums; - for (c = 0; c < spec->num_adc_nids; c++) - alc_auto_init_adc(codec, c); if (spec->dyn_adc_switch) nums = 1; else nums = spec->num_adc_nids; - for (c = 0; c < nums; c++) - alc_mux_select(codec, c, spec->cur_mux[c], true); + + for (c = 0; c < nums; c++) { + for (i = 0; i < imux->num_items; i++) { + path = get_nid_path(codec, spec->imux_pins[i], + get_adc_nid(codec, c, i)); + if (path) { + bool active = path->active; + if (i == spec->cur_mux[c]) + active = true; + activate_path(codec, path, active, false); + } + } + } + + alc_inv_dmic_sync(codec, true); + if (spec->shared_mic_hp) + update_shared_mic_hp(codec, spec->cur_mux[0]); } /* add mic boosts if needed */ @@ -4429,94 +4259,6 @@ static int alc_auto_add_mic_boost(struct hda_codec *codec) return 0; } -/* select or unmute the given capsrc route */ -static void select_or_unmute_capsrc(struct hda_codec *codec, hda_nid_t cap, - int idx) -{ - if (get_wcaps_type(get_wcaps(codec, cap)) == AC_WID_AUD_MIX) { - snd_hda_codec_amp_stereo(codec, cap, HDA_INPUT, idx, - HDA_AMP_MUTE, 0); - } else if (snd_hda_get_num_conns(codec, cap) > 1) { - snd_hda_codec_write_cache(codec, cap, 0, - AC_VERB_SET_CONNECT_SEL, idx); - } -} - -/* set the default connection to that pin */ -static int init_capsrc_for_pin(struct hda_codec *codec, hda_nid_t pin) -{ - struct alc_spec *spec = codec->spec; - int i; - - if (!pin) - return 0; - for (i = 0; i < spec->num_adc_nids; i++) { - hda_nid_t cap = get_capsrc(spec, i); - int idx; - - idx = get_connection_index(codec, cap, pin); - if (idx < 0) - continue; - select_or_unmute_capsrc(codec, cap, idx); - return i; /* return the found index */ - } - return -1; /* not found */ -} - -/* initialize some special cases for input sources */ -static void alc_init_special_input_src(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - int i; - - for (i = 0; i < spec->autocfg.num_inputs; i++) - init_capsrc_for_pin(codec, spec->autocfg.inputs[i].pin); -} - -/* assign appropriate capture mixers */ -static void set_capture_mixer(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - static const struct snd_kcontrol_new *caps[2][3] = { - { alc_capture_mixer_nosrc1, - alc_capture_mixer_nosrc2, - alc_capture_mixer_nosrc3 }, - { alc_capture_mixer1, - alc_capture_mixer2, - alc_capture_mixer3 }, - }; - - /* check whether either of ADC or MUX has a volume control */ - if (!nid_has_volume(codec, spec->adc_nids[0], HDA_INPUT)) { - if (!spec->capsrc_nids) - return; /* no volume */ - if (!nid_has_volume(codec, spec->capsrc_nids[0], HDA_OUTPUT)) - return; /* no volume in capsrc, too */ - spec->vol_in_capsrc = 1; - } - - if (spec->num_adc_nids > 0) { - int mux = 0; - int num_adcs = 0; - - if (spec->input_mux && spec->input_mux->num_items > 1) - mux = 1; - if (spec->auto_mic) { - num_adcs = 1; - mux = 0; - } else if (spec->dyn_adc_switch) - num_adcs = 1; - if (!num_adcs) { - if (spec->num_adc_nids > 3) - spec->num_adc_nids = 3; - else if (!spec->num_adc_nids) - return; - num_adcs = spec->num_adc_nids; - } - spec->cap_mixer = caps[mux][num_adcs - 1]; - } -} - /* * standard auto-parser initializations */ @@ -4639,16 +4381,28 @@ static int alc_parse_auto_config(struct hda_codec *codec, dig_only: alc_auto_parse_digital(codec); - if (!spec->no_analog) - alc_remove_invalid_adc_nids(codec); - if (ssid_nids) alc_ssid_check(codec, ssid_nids); if (!spec->no_analog) { - err = alc_auto_check_switches(codec); + err = alc_init_automute(codec); if (err < 0) return err; + + err = check_dyn_adc_switch(codec); + if (err < 0) + return err; + + if (!spec->shared_mic_hp) { + err = alc_init_auto_mic(codec); + if (err < 0) + return err; + } + + err = create_capture_mixers(codec); + if (err < 0) + return err; + err = alc_auto_add_mic_boost(codec); if (err < 0) return err; @@ -4657,9 +4411,6 @@ static int alc_parse_auto_config(struct hda_codec *codec, if (spec->kctls.list) add_mixer(spec, spec->kctls.list); - if (!spec->no_analog && !spec->cap_mixer) - set_capture_mixer(codec); - return 1; } -- cgit v1.2.3 From 27d31536517aa3609c592b68f1a69b6b687061f8 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 18 Dec 2012 08:57:05 +0100 Subject: ALSA: hda/realtek - Clean up some spec fields Remove some fields from struct alc_spec, and clean up the usage. Namely, - spec->input_mux becomes a single element, private_imux[] is removed - spec->adc_nids becomes an array by itself, and private_adc_nids[] gets removed, too Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 56 ++++++++++++++++--------------------------- 1 file changed, 20 insertions(+), 36 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 3ee2be544f4d..4fb815c7351d 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -128,7 +128,6 @@ struct alc_spec { /* codec parameterization */ const struct snd_kcontrol_new *mixers[5]; /* mixer arrays */ unsigned int num_mixers; - const struct snd_kcontrol_new *cap_mixer; /* capture mixer */ unsigned int beep_amp; /* beep amp value, set via set_beep_amp() */ char stream_name_analog[32]; /* analog PCM stream */ @@ -152,8 +151,7 @@ struct alc_spec { /* capture */ unsigned int num_adc_nids; - const hda_nid_t *adc_nids; - const hda_nid_t *capsrc_nids; + hda_nid_t adc_nids[AUTO_CFG_MAX_OUTS]; hda_nid_t dig_in_nid; /* digital-in NID; optional */ hda_nid_t mixer_nid; /* analog-mixer NID */ @@ -164,7 +162,7 @@ struct alc_spec { /* capture source */ unsigned int num_mux_defs; - const struct hda_input_mux *input_mux; + struct hda_input_mux input_mux; unsigned int cur_mux[3]; hda_nid_t ext_mic_pin; hda_nid_t dock_mic_pin; @@ -184,10 +182,7 @@ struct alc_spec { struct auto_pin_cfg autocfg; struct alc_customize_define cdefine; struct snd_array kctls; - struct hda_input_mux private_imux[3]; hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS]; - hda_nid_t private_adc_nids[AUTO_CFG_MAX_OUTS]; - hda_nid_t private_capsrc_nids[AUTO_CFG_MAX_OUTS]; hda_nid_t imux_pins[HDA_MAX_NUM_INPUTS]; unsigned int dyn_adc_idx[HDA_MAX_NUM_INPUTS]; int int_mic_idx, ext_mic_idx, dock_mic_idx; /* for auto-mic */ @@ -291,7 +286,7 @@ static int alc_mux_enum_info(struct snd_kcontrol *kcontrol, { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct alc_spec *spec = codec->spec; - return snd_hda_input_mux_info(&spec->input_mux[0], uinfo); + return snd_hda_input_mux_info(&spec->input_mux, uinfo); } static int alc_mux_enum_get(struct snd_kcontrol *kcontrol, @@ -374,8 +369,8 @@ static int alc_mux_select(struct hda_codec *codec, unsigned int adc_idx, const struct hda_input_mux *imux; struct nid_path *path; - imux = spec->input_mux; - if (!imux || !imux->num_items) + imux = &spec->input_mux; + if (!imux->num_items) return 0; if (idx >= imux->num_items) @@ -632,8 +627,6 @@ static void alc_mic_automute(struct hda_codec *codec, struct hda_jack_tbl *jack) if (!spec->auto_mic || !spec->auto_mic_valid_imux) return; - if (snd_BUG_ON(!spec->adc_nids)) - return; if (snd_BUG_ON(spec->int_mic_idx < 0 || spec->ext_mic_idx < 0)) return; @@ -998,7 +991,7 @@ static bool alc_auto_mic_check_imux(struct hda_codec *codec) struct alc_spec *spec = codec->spec; const struct hda_input_mux *imux; - imux = spec->input_mux; + imux = &spec->input_mux; spec->ext_mic_idx = find_idx_in_nid_list(spec->ext_mic_pin, spec->imux_pins, imux->num_items); spec->int_mic_idx = find_idx_in_nid_list(spec->int_mic_pin, @@ -1441,7 +1434,7 @@ static int alc_cap_put_caller(struct snd_kcontrol *kcontrol, struct nid_path *path; int i, adc_idx, err = 0; - imux = spec->input_mux; + imux = &spec->input_mux; adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); mutex_lock(&codec->control_mutex); codec->cached_write = 1; @@ -1488,7 +1481,7 @@ static int alc_cap_sw_put(struct snd_kcontrol *kcontrol, static void alc_inv_dmic_sync_adc(struct hda_codec *codec, int adc_idx) { struct alc_spec *spec = codec->spec; - struct hda_input_mux *imux = &spec->private_imux[0]; + struct hda_input_mux *imux = &spec->input_mux; struct nid_path *path; hda_nid_t nid; int i, dir, parm; @@ -1650,11 +1643,6 @@ static int __alc_build_controls(struct hda_codec *codec) if (err < 0) return err; } - if (spec->cap_mixer) { - err = snd_hda_add_new_ctls(codec, spec->cap_mixer); - if (err < 0) - return err; - } if (spec->multiout.dig_out_nid) { err = snd_hda_create_dig_out_ctls(codec, spec->multiout.dig_out_nid, @@ -2014,7 +2002,7 @@ static int alc_build_pcms(struct hda_codec *codec) info->stream[SNDRV_PCM_STREAM_PLAYBACK].chmap = snd_pcm_2_1_chmaps; } - if (spec->adc_nids) { + if (spec->num_adc_nids) { p = spec->stream_analog_capture; if (!p) { if (spec->dyn_adc_switch) @@ -2074,8 +2062,7 @@ static int alc_build_pcms(struct hda_codec *codec) * model, configure a second analog capture-only PCM. */ have_multi_adcs = (spec->num_adc_nids > 1) && - !spec->dyn_adc_switch && !spec->auto_mic && - (!spec->input_mux || spec->input_mux->num_items > 1); + !spec->dyn_adc_switch && !spec->auto_mic; /* Additional Analaog capture for index #2 */ if (spec->alt_dac_nid || have_multi_adcs) { codec->num_pcms = 3; @@ -2442,8 +2429,8 @@ static int alc_auto_fill_adc_nids(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; hda_nid_t nid; - hda_nid_t *adc_nids = spec->private_adc_nids; - int max_nums = ARRAY_SIZE(spec->private_adc_nids); + hda_nid_t *adc_nids = spec->adc_nids; + int max_nums = ARRAY_SIZE(spec->adc_nids); int i, nums = 0; nid = codec->start_nid; @@ -2457,7 +2444,6 @@ static int alc_auto_fill_adc_nids(struct hda_codec *codec) if (++nums >= max_nums) break; } - spec->adc_nids = spec->private_adc_nids; spec->num_adc_nids = nums; return nums; } @@ -2468,8 +2454,8 @@ static int alc_auto_fill_adc_nids(struct hda_codec *codec) static int check_dyn_adc_switch(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - struct hda_input_mux *imux = &spec->private_imux[0]; - hda_nid_t adc_nids[ARRAY_SIZE(spec->private_adc_nids)]; + struct hda_input_mux *imux = &spec->input_mux; + hda_nid_t adc_nids[ARRAY_SIZE(spec->adc_nids)]; int i, n, nums; hda_nid_t pin, adc; @@ -2489,7 +2475,7 @@ static int check_dyn_adc_switch(struct hda_codec *codec) if (!nums) { if (spec->shared_mic_hp) { spec->shared_mic_hp = 0; - spec->private_imux[0].num_items = 1; + imux->num_items = 1; goto again; } @@ -2508,12 +2494,11 @@ static int check_dyn_adc_switch(struct hda_codec *codec) snd_printdd("realtek: enabling ADC switching\n"); spec->dyn_adc_switch = 1; } else if (nums != spec->num_adc_nids) { - memcpy(spec->private_adc_nids, adc_nids, - nums * sizeof(hda_nid_t)); + memcpy(spec->adc_nids, adc_nids, nums * sizeof(hda_nid_t)); spec->num_adc_nids = nums; } - if (spec->input_mux->num_items == 1 || spec->shared_mic_hp) { + if (imux->num_items == 1 || spec->shared_mic_hp) { snd_printdd("realtek: reducing to a single ADC\n"); spec->num_adc_nids = 1; /* reduce to a single ADC */ } @@ -2592,7 +2577,7 @@ static int parse_capvol_in_path(struct hda_codec *codec, struct nid_path *path) static int create_capture_mixers(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - struct hda_input_mux *imux = &spec->private_imux[0]; + struct hda_input_mux *imux = &spec->input_mux; struct snd_kcontrol_new *knew; int i, n, nums; @@ -2654,7 +2639,7 @@ static int alc_auto_create_input_ctls(struct hda_codec *codec) struct alc_spec *spec = codec->spec; const struct auto_pin_cfg *cfg = &spec->autocfg; hda_nid_t mixer = spec->mixer_nid; - struct hda_input_mux *imux = &spec->private_imux[0]; + struct hda_input_mux *imux = &spec->input_mux; int num_adcs; int i, c, err, type_idx = 0; const char *prev_label = NULL; @@ -2718,7 +2703,6 @@ static int alc_auto_create_input_ctls(struct hda_codec *codec) } } - spec->input_mux = imux; return 0; } @@ -4187,7 +4171,7 @@ static void alc_auto_init_multi_io(struct hda_codec *codec) static void alc_auto_init_input_src(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - struct hda_input_mux *imux = &spec->private_imux[0]; + struct hda_input_mux *imux = &spec->input_mux; struct nid_path *path; int i, c, nums; -- cgit v1.2.3 From 62343997e475b8c33fef0d8549eef49f6855ae0a Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 18 Dec 2012 09:06:01 +0100 Subject: ALSA: hda/realtek - Remove superfluous input amp init The amps will be initialized via activate_path(), thus it's superfluous to set in alc_auto_init_analog_input(). Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 4fb815c7351d..954f4a8db004 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -2755,13 +2755,8 @@ static void alc_auto_init_analog_input(struct hda_codec *codec) for (i = 0; i < cfg->num_inputs; i++) { hda_nid_t nid = cfg->inputs[i].pin; - if (alc_is_input_pin(codec, nid)) { + if (alc_is_input_pin(codec, nid)) alc_set_input_pin(codec, nid, cfg->inputs[i].type); - if (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP) - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_AMP_GAIN_MUTE, - AMP_OUT_MUTE); - } /* mute loopback inputs */ if (spec->mixer_nid) { -- cgit v1.2.3 From 965ccebccdf7199bef866414e56817b18ea6268e Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 18 Dec 2012 11:46:37 +0100 Subject: ALSA: hda/realtek - Rename add_new_out_path() with add_new_nid_path() Make the function more generic for both input and output directions, and returns the assigned path pointer. The argument order is changed to follow the standard (from, to) way. Now this new function is used for analog input and loopback path parser codes, too. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 46 +++++++++++++++++++++---------------------- 1 file changed, 23 insertions(+), 23 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 954f4a8db004..d9c3b4af41d4 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -1342,8 +1342,12 @@ static void alc_auto_set_output_and_unmute(struct hda_codec *codec, hda_nid_t dac); static hda_nid_t alc_auto_look_for_dac(struct hda_codec *codec, hda_nid_t pin, bool is_digital); -static bool add_new_out_path(struct hda_codec *codec, hda_nid_t pin, - hda_nid_t dac); +static bool parse_nid_path(struct hda_codec *codec, hda_nid_t from_nid, + hda_nid_t to_nid, int with_aa_mix, + struct nid_path *path); +static struct nid_path *add_new_nid_path(struct hda_codec *codec, + hda_nid_t from_nid, hda_nid_t to_nid, + int with_aa_mix); /* * Digital I/O handling @@ -1371,7 +1375,7 @@ static void alc_auto_init_digital(struct hda_codec *codec) static void alc_auto_parse_digital(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - int i, err, nums; + int i, nums; hda_nid_t dig_nid; /* support multiple SPDIFs; the secondary is set up as a slave */ @@ -1381,6 +1385,8 @@ static void alc_auto_parse_digital(struct hda_codec *codec) dig_nid = alc_auto_look_for_dac(codec, pin, true); if (!dig_nid) continue; + if (!add_new_nid_path(codec, dig_nid, pin, 2)) + continue; if (!nums) { spec->multiout.dig_out_nid = dig_nid; spec->dig_out_type = spec->autocfg.dig_out_type[0]; @@ -1390,7 +1396,6 @@ static void alc_auto_parse_digital(struct hda_codec *codec) break; spec->slave_dig_outs[nums - 1] = dig_nid; } - add_new_out_path(codec, pin, dig_nid); nums++; } @@ -1404,8 +1409,6 @@ static void alc_auto_parse_digital(struct hda_codec *codec) continue; if (!(wcaps & AC_WCAP_CONN_LIST)) continue; - err = get_connection_index(codec, dig_nid, - spec->autocfg.dig_in_pin); if (err >= 0) { spec->dig_in_nid = dig_nid; break; @@ -2343,10 +2346,6 @@ static const char *alc_get_line_out_pfx(struct alc_spec *spec, int ch, return channel_name[ch]; } -static bool parse_nid_path(struct hda_codec *codec, hda_nid_t from_nid, - hda_nid_t to_nid, int with_aa_mix, - struct nid_path *path); - #ifdef CONFIG_PM /* add the powersave loopback-list entry */ static void add_loopback_list(struct alc_spec *spec, hda_nid_t mix, int idx) @@ -2380,11 +2379,8 @@ static int new_analog_input(struct hda_codec *codec, hda_nid_t pin, !nid_has_mute(codec, mix_nid, HDA_INPUT)) return 0; /* no need for analog loopback */ - path = snd_array_new(&spec->paths); + path = add_new_nid_path(codec, pin, mix_nid, 2); if (!path) - return -ENOMEM; - memset(path, 0, sizeof(*path)); - if (!parse_nid_path(codec, pin, mix_nid, 2, path)) return -EINVAL; idx = path->idx[path->depth - 1]; @@ -2937,21 +2933,25 @@ static hda_nid_t alc_look_for_out_mute_nid(struct hda_codec *codec, static hda_nid_t alc_look_for_out_vol_nid(struct hda_codec *codec, struct nid_path *path); -static bool add_new_out_path(struct hda_codec *codec, hda_nid_t pin, - hda_nid_t dac) +static struct nid_path *add_new_nid_path(struct hda_codec *codec, + hda_nid_t from_nid, hda_nid_t to_nid, + int with_aa_mix) { struct alc_spec *spec = codec->spec; struct nid_path *path; + if (from_nid && to_nid && !is_reachable_path(codec, from_nid, to_nid)) + return NULL; + path = snd_array_new(&spec->paths); if (!path) - return false; + return NULL; memset(path, 0, sizeof(*path)); - if (parse_nid_path(codec, dac, pin, 0, path)) - return true; + if (parse_nid_path(codec, from_nid, to_nid, with_aa_mix, path)) + return path; /* push back */ spec->paths.used--; - return false; + return NULL; } /* get the path between the given NIDs; @@ -3093,7 +3093,7 @@ static int alc_auto_fill_dacs(struct hda_codec *codec, int num_outs, else badness += bad->no_dac; } - if (!add_new_out_path(codec, pin, dac)) + if (!add_new_nid_path(codec, dac, pin, 0)) dac = dacs[i] = 0; if (dac) badness += assign_out_path_ctls(codec, pin, dac); @@ -3118,7 +3118,7 @@ static bool alc_map_singles(struct hda_codec *codec, int outs, dac = get_dac_if_single(codec, pins[i]); if (!dac) continue; - if (add_new_out_path(codec, pins[i], dac)) { + if (add_new_nid_path(codec, dac, pins[i], 0)) { dacs[i] = dac; found = true; } @@ -4015,7 +4015,7 @@ static int alc_auto_fill_multi_ios(struct hda_codec *codec, badness++; continue; } - if (!add_new_out_path(codec, nid, dac)) { + if (!add_new_nid_path(codec, dac, nid, 0)) { badness++; continue; } -- cgit v1.2.3 From df1d1fb09aed7aae0f94a5ba1a39d0477dee6f1d Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 18 Dec 2012 11:55:53 +0100 Subject: ALSA: hda/realtek - Parse digital input path This was the last forgotten path. Now it's parsed via the same path parser. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index d9c3b4af41d4..0d6e9d884594 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -1402,14 +1402,16 @@ static void alc_auto_parse_digital(struct hda_codec *codec) if (spec->autocfg.dig_in_pin) { dig_nid = codec->start_nid; for (i = 0; i < codec->num_nodes; i++, dig_nid++) { + struct nid_path *path; unsigned int wcaps = get_wcaps(codec, dig_nid); if (get_wcaps_type(wcaps) != AC_WID_AUD_IN) continue; if (!(wcaps & AC_WCAP_DIGITAL)) continue; - if (!(wcaps & AC_WCAP_CONN_LIST)) - continue; - if (err >= 0) { + path = add_new_nid_path(codec, spec->autocfg.dig_in_pin, + dig_nid, 2); + if (path) { + path->active = true; spec->dig_in_nid = dig_nid; break; } -- cgit v1.2.3 From 37c042076521a854239241eaac94788076046231 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 18 Dec 2012 14:22:45 +0100 Subject: ALSA: hda/realtek - Allow different pins for shared hp/mic vref check Add a new field to indicate the possible pin NID for alternative vref setup for the shared hp/mic. Although 0x18 is valid for all Realtek codecs, it'll be different on other vendor's codecs. Also, drop the sanity check in update_shared_mic_hp() since the reference pin is set explicitly in the caller side. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 0d6e9d884594..c6cc833f4d2a 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -187,6 +187,7 @@ struct alc_spec { unsigned int dyn_adc_idx[HDA_MAX_NUM_INPUTS]; int int_mic_idx, ext_mic_idx, dock_mic_idx; /* for auto-mic */ hda_nid_t inv_dmic_pin; + hda_nid_t shared_mic_vref_pin; /* DAC list */ int num_all_dacs; @@ -343,15 +344,11 @@ static void update_shared_mic_hp(struct hda_codec *codec, bool set_as_mic) /* This pin does not have vref caps - let's enable vref on pin 0x18 instead, as suggested by Realtek */ - if (val == AC_PINCTL_VREF_HIZ) { - const hda_nid_t vref_pin = 0x18; - /* Sanity check pin 0x18 */ - if (get_wcaps_type(get_wcaps(codec, vref_pin)) == AC_WID_PIN && - get_defcfg_connect(snd_hda_codec_get_pincfg(codec, vref_pin)) == AC_JACK_PORT_NONE) { - unsigned int vref_val = snd_hda_get_default_vref(codec, vref_pin); - if (vref_val != AC_PINCTL_VREF_HIZ) - snd_hda_set_pin_ctl(codec, vref_pin, PIN_IN | (set_as_mic ? vref_val : 0)); - } + if (val == AC_PINCTL_VREF_HIZ && spec->shared_mic_vref_pin) { + const hda_nid_t vref_pin = spec->shared_mic_vref_pin; + unsigned int vref_val = snd_hda_get_default_vref(codec, vref_pin); + if (vref_val != AC_PINCTL_VREF_HIZ) + snd_hda_set_pin_ctl(codec, vref_pin, PIN_IN | (set_as_mic ? vref_val : 0)); } val = set_as_mic ? val | PIN_IN : PIN_HP; @@ -5642,6 +5639,7 @@ static int patch_alc262(struct hda_codec *codec) return err; spec = codec->spec; + spec->shared_mic_vref_pin = 0x18; #if 0 /* pshou 07/11/05 set a zero PCM sample to DAC when FIFO is @@ -6431,6 +6429,7 @@ static int patch_alc269(struct hda_codec *codec) return err; spec = codec->spec; + spec->shared_mic_vref_pin = 0x18; alc_pick_fixup(codec, alc269_fixup_models, alc269_fixup_tbl, alc269_fixups); -- cgit v1.2.3 From 480967db6c5ac0c2cd4582a73ee8aaaffda66d51 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 18 Dec 2012 14:29:52 +0100 Subject: ALSA: hda/realtek - Drop auto_mic_valid_imux flag This flag is superfluous now and it's always as same as spec->auto_mic. Let's drop. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index c6cc833f4d2a..e3fe7350b359 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -209,7 +209,6 @@ struct alc_spec { unsigned int line_jack_present:1; unsigned int master_mute:1; unsigned int auto_mic:1; - unsigned int auto_mic_valid_imux:1; /* valid imux for auto-mic */ unsigned int automute_speaker:1; /* automute speaker outputs */ unsigned int automute_lo:1; /* automute LO outputs */ unsigned int detect_hp:1; /* Headphone detection enabled */ @@ -622,7 +621,7 @@ static void alc_mic_automute(struct hda_codec *codec, struct hda_jack_tbl *jack) struct alc_spec *spec = codec->spec; hda_nid_t *pins = spec->imux_pins; - if (!spec->auto_mic || !spec->auto_mic_valid_imux) + if (!spec->auto_mic) return; if (snd_BUG_ON(spec->int_mic_idx < 0 || spec->ext_mic_idx < 0)) return; @@ -1004,8 +1003,6 @@ static bool alc_auto_mic_check_imux(struct hda_codec *codec) snd_hda_jack_detect_enable_callback(codec, spec->dock_mic_pin, ALC_MIC_EVENT, alc_mic_automute); - - spec->auto_mic_valid_imux = 1; return true; } -- cgit v1.2.3 From 20c18f562a4d0494dd2b99e19cfeb33f55c9c50e Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 18 Dec 2012 14:33:21 +0100 Subject: ALSA: hda/realtek - Remove unused fields and macro definitions Also arranged alc_spec definitions to optimize bit fields. Use a bit field for spec->need_dac_fix, too. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index e3fe7350b359..f10018b732f3 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -73,8 +73,6 @@ struct alc_multi_io { unsigned int ctl_in; /* cached input-pin control value */ }; -#define MAX_VOL_NIDS 0x40 - /* make compatible with old code */ #define alc_apply_pincfgs snd_hda_apply_pincfgs #define alc_apply_fixup snd_hda_apply_fixup @@ -161,7 +159,6 @@ struct alc_spec { unsigned int cur_adc_format; /* capture source */ - unsigned int num_mux_defs; struct hda_input_mux input_mux; unsigned int cur_mux[3]; hda_nid_t ext_mic_pin; @@ -171,7 +168,6 @@ struct alc_spec { /* channel model */ const struct hda_channel_mode *channel_mode; int num_channel_mode; - int need_dac_fix; int const_channel_count; /* min. channel count (for speakers) */ int ext_channel_count; /* current channel count for multi-io */ @@ -218,19 +214,15 @@ struct alc_spec { unsigned int keep_vref_in_automute:1; /* Don't clear VREF in automute */ /* other flags */ + unsigned int need_dac_fix:1; /* need to limit DACs for multi channels */ unsigned int no_analog :1; /* digital I/O only */ unsigned int dyn_adc_switch:1; /* switch ADCs (for ALC275) */ - unsigned int single_input_src:1; - unsigned int vol_in_capsrc:1; /* use capsrc volume (ADC has no vol) */ - unsigned int parse_flags; /* passed to snd_hda_parse_pin_defcfg() */ unsigned int shared_mic_hp:1; /* HP/Mic-in sharing */ unsigned int inv_dmic_fixup:1; /* has inverted digital-mic workaround */ unsigned int inv_dmic_muted:1; /* R-ch of inv d-mic is muted? */ unsigned int no_primary_hp:1; /* Don't prefer HP pins to speaker pins */ - /* auto-mute control */ - int automute_mode; - hda_nid_t automute_mixer_nid[AUTO_CFG_MAX_OUTS]; + unsigned int parse_flags; /* passed to snd_hda_parse_pin_defcfg() */ int init_amp; int codec_variant; /* flag for other variants */ @@ -612,9 +604,6 @@ static void alc_line_automute(struct hda_codec *codec, struct hda_jack_tbl *jack call_update_outputs(codec); } -#define get_connection_index(codec, mux, nid) \ - snd_hda_get_conn_index(codec, mux, nid, 0) - /* standard mic auto-switch helper */ static void alc_mic_automute(struct hda_codec *codec, struct hda_jack_tbl *jack) { @@ -1619,8 +1608,6 @@ static const char * const alc_slave_pfxs[] = { * build control elements */ -#define NID_MAPPING (-1) - static void alc_free_kctls(struct hda_codec *codec); #ifdef CONFIG_SND_HDA_INPUT_BEEP -- cgit v1.2.3 From 3bd7b644d049feb8df4225492689a324963f42f4 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 18 Dec 2012 14:57:09 +0100 Subject: ALSA: hda/realtek - Handle vmaster hook in the parser side ... so that the fixup just needs to set the hook function in FIXUP_ACT_PROBE. This will make easier to port for other codecs, too. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index f10018b732f3..a3046144f149 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -1687,6 +1687,8 @@ static int __alc_build_controls(struct hda_codec *codec) true, &spec->vmaster_mute.sw_kctl); if (err < 0) return err; + if (spec->vmaster_mute.hook) + snd_hda_add_vmaster_hook(codec, &spec->vmaster_mute, true); } alc_free_kctls(codec); /* no longer needed */ @@ -1745,6 +1747,9 @@ static int alc_init(struct hda_codec *codec) snd_hda_gen_apply_verbs(codec); alc_auto_init_std(codec); + if (spec->vmaster_mute.sw_kctl && spec->vmaster_mute.hook) + snd_hda_sync_vmaster_hook(&spec->vmaster_mute); + alc_apply_fixup(codec, ALC_FIXUP_ACT_INIT); hda_call_check_power_status(codec, 0x01); @@ -6025,15 +6030,8 @@ static void alc269_fixup_mic1_mute(struct hda_codec *codec, const struct alc_fixup *fix, int action) { struct alc_spec *spec = codec->spec; - switch (action) { - case ALC_FIXUP_ACT_BUILD: + if (action == ALC_FIXUP_ACT_PROBE) spec->vmaster_mute.hook = alc269_fixup_mic1_mute_hook; - snd_hda_add_vmaster_hook(codec, &spec->vmaster_mute, true); - /* fallthru */ - case ALC_FIXUP_ACT_INIT: - snd_hda_sync_vmaster_hook(&spec->vmaster_mute); - break; - } } /* update mute-LED according to the speaker mute state via mic2 VREF pin */ @@ -6048,15 +6046,8 @@ static void alc269_fixup_mic2_mute(struct hda_codec *codec, const struct alc_fixup *fix, int action) { struct alc_spec *spec = codec->spec; - switch (action) { - case ALC_FIXUP_ACT_BUILD: + if (action == ALC_FIXUP_ACT_PROBE) spec->vmaster_mute.hook = alc269_fixup_mic2_mute_hook; - snd_hda_add_vmaster_hook(codec, &spec->vmaster_mute, true); - /* fallthru */ - case ALC_FIXUP_ACT_INIT: - snd_hda_sync_vmaster_hook(&spec->vmaster_mute); - break; - } } static void alc271_hp_gate_mic_jack(struct hda_codec *codec, -- cgit v1.2.3 From 52a8efab10637ae5f58123be3ab3b9cb6a9ff2b0 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 18 Dec 2012 17:33:04 +0100 Subject: ALSA: hda/realtek - Assign Master mixer when possible There are a few more cases where we can assign "Master" mixer element safely, e.g. when a single DAC is used in the whole output paths. Also, when vmaster hook is present, avoid "Master" but assign "PCM" instead. Otherwise vmaster hook won't work properly. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index a3046144f149..f21c53d437db 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -2306,7 +2306,14 @@ static const char *alc_get_line_out_pfx(struct alc_spec *spec, int ch, *index = 0; if (cfg->line_outs == 1 && !spec->multi_ios && !cfg->hp_outs && !cfg->speaker_outs && can_be_master) - return "Master"; + return spec->vmaster_mute.hook ? "PCM" : "Master"; + + /* if there is really a single DAC used in the whole output paths, + * use it master (or "PCM" if a vmaster hook is present) + */ + if (spec->multiout.num_dacs == 1 && !spec->mixer_nid && + !spec->multiout.hp_out_nid[0] && !spec->multiout.extra_out_nid[0]) + return spec->vmaster_mute.hook ? "PCM" : "Master"; switch (cfg->line_out_type) { case AUTO_PIN_SPEAKER_OUT: -- cgit v1.2.3 From 2eab694a6c85499710d050f880c6f8ae705e7a19 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 18 Dec 2012 15:30:41 +0100 Subject: ALSA: hda/realtek - Merge a few split functions Merge a few functions that have been split due to historical reasons to single functions. Splitting too much (and placing too far away) actually worsens the readability. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 32 ++++++-------------------------- 1 file changed, 6 insertions(+), 26 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index f21c53d437db..25c0fc911666 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -653,14 +653,6 @@ static void alc880_unsol_event(struct hda_codec *codec, unsigned int res) snd_hda_jack_unsol_event(codec, res >> 2); } -/* call init functions of standard auto-mute helpers */ -static void alc_inithook(struct hda_codec *codec) -{ - alc_hp_automute(codec, NULL); - alc_line_automute(codec, NULL); - alc_mic_automute(codec, NULL); -} - /* additional initialization for ALC888 variants */ static void alc888_coef_init(struct hda_codec *codec) { @@ -1619,7 +1611,7 @@ static const struct snd_kcontrol_new alc_beep_mixer[] = { }; #endif -static int __alc_build_controls(struct hda_codec *codec) +static int alc_build_controls(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; int i, err; @@ -1693,13 +1685,6 @@ static int __alc_build_controls(struct hda_codec *codec) alc_free_kctls(codec); /* no longer needed */ - return 0; -} - -static int alc_build_jacks(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - if (spec->shared_mic_hp) { int err; int nid = spec->autocfg.inputs[1].pin; @@ -1711,18 +1696,10 @@ static int alc_build_jacks(struct hda_codec *codec) return err; } - return snd_hda_jack_add_kctls(codec, &spec->autocfg); -} - -static int alc_build_controls(struct hda_codec *codec) -{ - int err = __alc_build_controls(codec); + err = snd_hda_jack_add_kctls(codec, &spec->autocfg); if (err < 0) return err; - err = alc_build_jacks(codec); - if (err < 0) - return err; alc_apply_fixup(codec, ALC_FIXUP_ACT_BUILD); return 0; } @@ -4244,7 +4221,10 @@ static void alc_auto_init_std(struct hda_codec *codec) alc_auto_init_analog_input(codec); alc_auto_init_input_src(codec); alc_auto_init_digital(codec); - alc_inithook(codec); + /* call init functions of standard auto-mute helpers */ + alc_hp_automute(codec, NULL); + alc_line_automute(codec, NULL); + alc_mic_automute(codec, NULL); } /* -- cgit v1.2.3 From bc54976721d30f5ec51e90dcd1aca56494e0b0cf Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 18 Dec 2012 15:35:11 +0100 Subject: ALSA: hda/realtek - Allow passing name=NULL to alc_kcontrol_new() This prevents stupid typos. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 25c0fc911666..13d4548a5381 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -862,7 +862,10 @@ alc_kcontrol_new(struct alc_spec *spec, const char *name, if (!knew) return NULL; *knew = *temp; - knew->name = kstrdup(name, GFP_KERNEL); + if (name) + knew->name = kstrdup(name, GFP_KERNEL); + else if (knew->name) + knew->name = kstrdup(knew->name, GFP_KERNEL); if (!knew->name) return NULL; return knew; @@ -872,7 +875,7 @@ static int alc_add_automute_mode_enum(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - if (!alc_kcontrol_new(spec, "Auto-Mute Mode", &alc_automute_mode_enum)) + if (!alc_kcontrol_new(spec, NULL, &alc_automute_mode_enum)) return -ENOMEM; return 0; } @@ -1556,6 +1559,7 @@ static int alc_inv_dmic_sw_put(struct snd_kcontrol *kcontrol, static const struct snd_kcontrol_new alc_inv_dmic_sw = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Inverted Internal Mic Capture Switch", .info = snd_ctl_boolean_mono_info, .get = alc_inv_dmic_sw_get, .put = alc_inv_dmic_sw_put, @@ -1565,8 +1569,7 @@ static int alc_add_inv_dmic_mixer(struct hda_codec *codec, hda_nid_t nid) { struct alc_spec *spec = codec->spec; - if (!alc_kcontrol_new(spec, "Inverted Internal Mic Capture Switch", - &alc_inv_dmic_sw)) + if (!alc_kcontrol_new(spec, NULL, &alc_inv_dmic_sw)) return -ENOMEM; spec->inv_dmic_fixup = 1; spec->inv_dmic_muted = 0; @@ -2555,7 +2558,7 @@ static int create_capture_mixers(struct hda_codec *codec) nums = spec->num_adc_nids; if (!spec->auto_mic && imux->num_items > 1) { - knew = alc_kcontrol_new(spec, "Input Source", &cap_src_temp); + knew = alc_kcontrol_new(spec, NULL, &cap_src_temp); if (!knew) return -ENOMEM; knew->count = nums; @@ -2579,8 +2582,7 @@ static int create_capture_mixers(struct hda_codec *codec) } if (vol) { - knew = alc_kcontrol_new(spec, "Capture Volume", - &cap_vol_temp); + knew = alc_kcontrol_new(spec, NULL, &cap_vol_temp); if (!knew) return -ENOMEM; knew->index = n; @@ -2588,8 +2590,7 @@ static int create_capture_mixers(struct hda_codec *codec) knew->subdevice = HDA_SUBDEV_AMP_FLAG; } if (sw) { - knew = alc_kcontrol_new(spec, "Capture Switch", - &cap_sw_temp); + knew = alc_kcontrol_new(spec, NULL, &cap_sw_temp); if (!knew) return -ENOMEM; knew->index = n; @@ -4106,8 +4107,7 @@ static int alc_auto_add_multi_channel_mode(struct hda_codec *codec) struct alc_spec *spec = codec->spec; if (spec->multi_ios > 0) { - if (!alc_kcontrol_new(spec, "Channel Mode", - &alc_auto_channel_mode_enum)) + if (!alc_kcontrol_new(spec, NULL, &alc_auto_channel_mode_enum)) return -ENOMEM; } return 0; -- cgit v1.2.3 From 9bf387b6121bc446f275b0de8196d4dea8a3c876 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 18 Dec 2012 17:18:21 +0100 Subject: ALSA: hda/realtek - Allow multiple individual capture volume/switch controls So far we create only "Capture Volume" and "Capture Switch" controls for binding all possible amps, but we'd prefer creating individual capture volume and switch controls per input in some cases (e.g. conexant parser does it). Add a new flag, spec->multi_cap_vol, to follow that policy. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 149 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 131 insertions(+), 18 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 13d4548a5381..6fb39221aac4 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -221,6 +221,7 @@ struct alc_spec { unsigned int inv_dmic_fixup:1; /* has inverted digital-mic workaround */ unsigned int inv_dmic_muted:1; /* R-ch of inv d-mic is muted? */ unsigned int no_primary_hp:1; /* Don't prefer HP pins to speaker pins */ + unsigned int multi_cap_vol:1; /* allow multiple capture xxx volumes */ unsigned int parse_flags; /* passed to snd_hda_parse_pin_defcfg() */ @@ -2474,6 +2475,10 @@ static int check_dyn_adc_switch(struct hda_codec *codec) spec->num_adc_nids = 1; /* reduce to a single ADC */ } + /* single index for individual volumes ctls */ + if (!spec->dyn_adc_switch && spec->multi_cap_vol) + spec->num_adc_nids = 1; + return 0; } @@ -2545,12 +2550,122 @@ static int parse_capvol_in_path(struct hda_codec *codec, struct nid_path *path) return 0; } +static int add_single_cap_ctl(struct hda_codec *codec, const char *label, + int idx, bool is_switch, unsigned int ctl) +{ + struct alc_spec *spec = codec->spec; + char tmpname[44]; + int type = is_switch ? ALC_CTL_WIDGET_MUTE : ALC_CTL_WIDGET_VOL; + const char *sfx = is_switch ? "Switch" : "Volume"; + + if (!ctl) + return 0; + + if (label) + snprintf(tmpname, sizeof(tmpname), + "%s Capture %s", label, sfx); + else + snprintf(tmpname, sizeof(tmpname), + "Capture %s", sfx); + return add_control(spec, type, tmpname, idx, ctl); +} + +/* create single (and simple) capture volume and switch controls */ +static int create_single_cap_vol_ctl(struct hda_codec *codec, int idx, + unsigned int vol_ctl, unsigned int sw_ctl) +{ + int err; + err = add_single_cap_ctl(codec, NULL, idx, false, vol_ctl); + if (err < 0) + return err; + err = add_single_cap_ctl(codec, NULL, idx, true, sw_ctl); + if (err < 0) + return err; + return 0; +} + +/* create bound capture volume and switch controls */ +static int create_bind_cap_vol_ctl(struct hda_codec *codec, int idx, + unsigned int vol_ctl, unsigned int sw_ctl) +{ + struct alc_spec *spec = codec->spec; + struct snd_kcontrol_new *knew; + + if (vol_ctl) { + knew = alc_kcontrol_new(spec, NULL, &cap_vol_temp); + if (!knew) + return -ENOMEM; + knew->index = idx; + knew->private_value = vol_ctl; + knew->subdevice = HDA_SUBDEV_AMP_FLAG; + } + if (sw_ctl) { + knew = alc_kcontrol_new(spec, NULL, &cap_sw_temp); + if (!knew) + return -ENOMEM; + knew->index = idx; + knew->private_value = sw_ctl; + knew->subdevice = HDA_SUBDEV_AMP_FLAG; + } + return 0; +} + +/* return the vol ctl when used first in the imux list */ +static unsigned int get_first_cap_ctl(struct hda_codec *codec, int idx, int type) +{ + struct alc_spec *spec = codec->spec; + struct nid_path *path; + unsigned int ctl; + int i; + + path = get_nid_path(codec, spec->imux_pins[idx], + get_adc_nid(codec, 0, idx)); + if (!path) + return 0; + ctl = path->ctls[type]; + if (!ctl) + return 0; + for (i = 0; i < idx - 1; i++) { + path = get_nid_path(codec, spec->imux_pins[i], + get_adc_nid(codec, 0, i)); + if (path && path->ctls[type] == ctl) + return 0; + } + return ctl; +} + +/* create individual capture volume and switch controls per input */ +static int create_multi_cap_vol_ctl(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + struct hda_input_mux *imux = &spec->input_mux; + int i, err, type, type_idx = 0; + const char *prev_label = NULL; + + for (i = 0; i < imux->num_items; i++) { + const char *label; + label = hda_get_autocfg_input_label(codec, &spec->autocfg, i); + if (prev_label && !strcmp(label, prev_label)) + type_idx++; + else + type_idx = 0; + prev_label = label; + + for (type = 0; type < 2; type++) { + err = add_single_cap_ctl(codec, label, type_idx, type, + get_first_cap_ctl(codec, i, type)); + if (err < 0) + return err; + } + } + return 0; +} + static int create_capture_mixers(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; struct hda_input_mux *imux = &spec->input_mux; - struct snd_kcontrol_new *knew; - int i, n, nums; + int i, n, nums, err; if (spec->dyn_adc_switch) nums = 1; @@ -2558,6 +2673,7 @@ static int create_capture_mixers(struct hda_codec *codec) nums = spec->num_adc_nids; if (!spec->auto_mic && imux->num_items > 1) { + struct snd_kcontrol_new *knew; knew = alc_kcontrol_new(spec, NULL, &cap_src_temp); if (!knew) return -ENOMEM; @@ -2565,6 +2681,7 @@ static int create_capture_mixers(struct hda_codec *codec) } for (n = 0; n < nums; n++) { + bool multi = false; int vol, sw; vol = sw = 0; @@ -2577,26 +2694,22 @@ static int create_capture_mixers(struct hda_codec *codec) parse_capvol_in_path(codec, path); if (!vol) vol = path->ctls[NID_PATH_VOL_CTL]; + else if (vol != path->ctls[NID_PATH_VOL_CTL]) + multi = true; if (!sw) sw = path->ctls[NID_PATH_MUTE_CTL]; + else if (sw != path->ctls[NID_PATH_MUTE_CTL]) + multi = true; } - if (vol) { - knew = alc_kcontrol_new(spec, NULL, &cap_vol_temp); - if (!knew) - return -ENOMEM; - knew->index = n; - knew->private_value = vol; - knew->subdevice = HDA_SUBDEV_AMP_FLAG; - } - if (sw) { - knew = alc_kcontrol_new(spec, NULL, &cap_sw_temp); - if (!knew) - return -ENOMEM; - knew->index = n; - knew->private_value = sw; - knew->subdevice = HDA_SUBDEV_AMP_FLAG; - } + if (!multi) + err = create_single_cap_vol_ctl(codec, n, vol, sw); + else if (!spec->multi_cap_vol) + err = create_bind_cap_vol_ctl(codec, n, vol, sw); + else + err = create_multi_cap_vol_ctl(codec); + if (err < 0) + return err; } return 0; -- cgit v1.2.3 From 81fede89eda16a597c2d814113b74677754b0058 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 18 Dec 2012 17:24:25 +0100 Subject: ALSA: hda/realtek - Add conexant-style inverted dmic handling To make the parser more generic, a few codes to handle the inverted stereo dmic in a way Conexant parser does is added in this patch. The caller should set spec->inv_dmic_split flag appropriately. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 6fb39221aac4..fbdcbded3417 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -184,6 +184,7 @@ struct alc_spec { int int_mic_idx, ext_mic_idx, dock_mic_idx; /* for auto-mic */ hda_nid_t inv_dmic_pin; hda_nid_t shared_mic_vref_pin; + int inv_dmic_split_idx; /* used internally for inv_dmic_split */ /* DAC list */ int num_all_dacs; @@ -222,6 +223,7 @@ struct alc_spec { unsigned int inv_dmic_muted:1; /* R-ch of inv d-mic is muted? */ unsigned int no_primary_hp:1; /* Don't prefer HP pins to speaker pins */ unsigned int multi_cap_vol:1; /* allow multiple capture xxx volumes */ + unsigned int inv_dmic_split:1; /* inverted dmic w/a for conexant */ unsigned int parse_flags; /* passed to snd_hda_parse_pin_defcfg() */ @@ -2550,6 +2552,8 @@ static int parse_capvol_in_path(struct hda_codec *codec, struct nid_path *path) return 0; } +static unsigned int amp_val_replace_channels(unsigned int val, unsigned int chs); + static int add_single_cap_ctl(struct hda_codec *codec, const char *label, int idx, bool is_switch, unsigned int ctl) { @@ -2557,17 +2561,37 @@ static int add_single_cap_ctl(struct hda_codec *codec, const char *label, char tmpname[44]; int type = is_switch ? ALC_CTL_WIDGET_MUTE : ALC_CTL_WIDGET_VOL; const char *sfx = is_switch ? "Switch" : "Volume"; + unsigned int chs; + int err; if (!ctl) return 0; + if (idx == spec->inv_dmic_split_idx) + chs = 1; + else + chs = 3; + if (label) snprintf(tmpname, sizeof(tmpname), "%s Capture %s", label, sfx); else snprintf(tmpname, sizeof(tmpname), "Capture %s", sfx); - return add_control(spec, type, tmpname, idx, ctl); + err = add_control(spec, type, tmpname, idx, + amp_val_replace_channels(ctl, chs)); + if (err < 0 || chs == 3) + return err; + + /* Make independent right kcontrol */ + if (label) + snprintf(tmpname, sizeof(tmpname), + "Inverted %s Capture %s", label, sfx); + else + snprintf(tmpname, sizeof(tmpname), + "Inverted Capture %s", sfx); + return add_control(spec, type, tmpname, idx, + amp_val_replace_channels(ctl, 2)); } /* create single (and simple) capture volume and switch controls */ @@ -2730,6 +2754,7 @@ static int alc_auto_create_input_ctls(struct hda_codec *codec) if (num_adcs < 0) return 0; + spec->inv_dmic_split_idx = -1; for (i = 0; i < cfg->num_inputs; i++) { hda_nid_t pin; const char *label; @@ -2783,6 +2808,14 @@ static int alc_auto_create_input_ctls(struct hda_codec *codec) imux_added = true; } } + + if (spec->inv_dmic_split) { + if (cfg->inputs[i].type == AUTO_PIN_MIC) { + unsigned int def_conf = snd_hda_codec_get_pincfg(codec, pin); + if (snd_hda_get_input_pin_attr(def_conf) == INPUT_PIN_ATTR_INT) + spec->inv_dmic_split_idx = i; + } + } } return 0; -- cgit v1.2.3 From c9ce6b260b039392b24ad65954788047d13d4c9a Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 18 Dec 2012 18:12:44 +0100 Subject: ALSA: hda - Move fixup code into struct hda_codec Since the fixup code is used commonly, it's worth to move it to the common place, struct hda_codec, instead of keeping in hda_gen_spec. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_auto_parser.c | 39 ++++++++++----------- sound/pci/hda/hda_auto_parser.h | 78 ----------------------------------------- sound/pci/hda/hda_codec.c | 2 ++ sound/pci/hda/hda_codec.h | 8 +++++ sound/pci/hda/hda_local.h | 53 ++++++++++++++++++++++++++++ sound/pci/hda/patch_cirrus.c | 8 +---- sound/pci/hda/patch_conexant.c | 6 +--- sound/pci/hda/patch_realtek.c | 10 ++---- sound/pci/hda/patch_via.c | 4 --- 9 files changed, 86 insertions(+), 122 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_auto_parser.c b/sound/pci/hda/hda_auto_parser.c index 7da883a464e3..d4606884f6a3 100644 --- a/sound/pci/hda/hda_auto_parser.c +++ b/sound/pci/hda/hda_auto_parser.c @@ -622,28 +622,27 @@ int snd_hda_get_pin_label(struct hda_codec *codec, hda_nid_t nid, } EXPORT_SYMBOL_HDA(snd_hda_get_pin_label); -int snd_hda_gen_add_verbs(struct hda_gen_spec *spec, - const struct hda_verb *list) +int snd_hda_add_verbs(struct hda_codec *codec, + const struct hda_verb *list) { const struct hda_verb **v; - v = snd_array_new(&spec->verbs); + v = snd_array_new(&codec->verbs); if (!v) return -ENOMEM; *v = list; return 0; } -EXPORT_SYMBOL_HDA(snd_hda_gen_add_verbs); +EXPORT_SYMBOL_HDA(snd_hda_add_verbs); -void snd_hda_gen_apply_verbs(struct hda_codec *codec) +void snd_hda_apply_verbs(struct hda_codec *codec) { - struct hda_gen_spec *spec = codec->spec; int i; - for (i = 0; i < spec->verbs.used; i++) { - struct hda_verb **v = snd_array_elem(&spec->verbs, i); + for (i = 0; i < codec->verbs.used; i++) { + struct hda_verb **v = snd_array_elem(&codec->verbs, i); snd_hda_sequence_write(codec, *v); } } -EXPORT_SYMBOL_HDA(snd_hda_gen_apply_verbs); +EXPORT_SYMBOL_HDA(snd_hda_apply_verbs); void snd_hda_apply_pincfgs(struct hda_codec *codec, const struct hda_pintbl *cfg) @@ -655,18 +654,17 @@ EXPORT_SYMBOL_HDA(snd_hda_apply_pincfgs); void snd_hda_apply_fixup(struct hda_codec *codec, int action) { - struct hda_gen_spec *spec = codec->spec; - int id = spec->fixup_id; + int id = codec->fixup_id; #ifdef CONFIG_SND_DEBUG_VERBOSE - const char *modelname = spec->fixup_name; + const char *modelname = codec->fixup_name; #endif int depth = 0; - if (!spec->fixup_list) + if (!codec->fixup_list) return; while (id >= 0) { - const struct hda_fixup *fix = spec->fixup_list + id; + const struct hda_fixup *fix = codec->fixup_list + id; switch (fix->type) { case HDA_FIXUP_PINS: @@ -683,7 +681,7 @@ void snd_hda_apply_fixup(struct hda_codec *codec, int action) snd_printdd(KERN_INFO SFX "%s: Apply fix-verbs for %s\n", codec->chip_name, modelname); - snd_hda_gen_add_verbs(codec->spec, fix->v.verbs); + snd_hda_add_verbs(codec, fix->v.verbs); break; case HDA_FIXUP_FUNC: if (!fix->v.func) @@ -713,15 +711,14 @@ void snd_hda_pick_fixup(struct hda_codec *codec, const struct snd_pci_quirk *quirk, const struct hda_fixup *fixlist) { - struct hda_gen_spec *spec = codec->spec; const struct snd_pci_quirk *q; int id = -1; const char *name = NULL; /* when model=nofixup is given, don't pick up any fixups */ if (codec->modelname && !strcmp(codec->modelname, "nofixup")) { - spec->fixup_list = NULL; - spec->fixup_id = -1; + codec->fixup_list = NULL; + codec->fixup_id = -1; return; } @@ -759,10 +756,10 @@ void snd_hda_pick_fixup(struct hda_codec *codec, } } - spec->fixup_id = id; + codec->fixup_id = id; if (id >= 0) { - spec->fixup_list = fixlist; - spec->fixup_name = name; + codec->fixup_list = fixlist; + codec->fixup_name = name; } } EXPORT_SYMBOL_HDA(snd_hda_pick_fixup); diff --git a/sound/pci/hda/hda_auto_parser.h b/sound/pci/hda/hda_auto_parser.h index 632ad0ad3007..ff11074b6c8b 100644 --- a/sound/pci/hda/hda_auto_parser.h +++ b/sound/pci/hda/hda_auto_parser.h @@ -89,82 +89,4 @@ int snd_hda_parse_pin_defcfg(struct hda_codec *codec, #define snd_hda_parse_pin_def_config(codec, cfg, ignore) \ snd_hda_parse_pin_defcfg(codec, cfg, ignore, 0) -/* - */ - -struct hda_gen_spec { - /* fix-up list */ - int fixup_id; - const struct hda_fixup *fixup_list; - const char *fixup_name; - - /* additional init verbs */ - struct snd_array verbs; -}; - - -/* - * Fix-up pin default configurations and add default verbs - */ - -struct hda_pintbl { - hda_nid_t nid; - u32 val; -}; - -struct hda_model_fixup { - const int id; - const char *name; -}; - -struct hda_fixup { - int type; - bool chained; - int chain_id; - union { - const struct hda_pintbl *pins; - const struct hda_verb *verbs; - void (*func)(struct hda_codec *codec, - const struct hda_fixup *fix, - int action); - } v; -}; - -/* fixup types */ -enum { - HDA_FIXUP_INVALID, - HDA_FIXUP_PINS, - HDA_FIXUP_VERBS, - HDA_FIXUP_FUNC, -}; - -/* fixup action definitions */ -enum { - HDA_FIXUP_ACT_PRE_PROBE, - HDA_FIXUP_ACT_PROBE, - HDA_FIXUP_ACT_INIT, - HDA_FIXUP_ACT_BUILD, -}; - -int snd_hda_gen_add_verbs(struct hda_gen_spec *spec, - const struct hda_verb *list); -void snd_hda_gen_apply_verbs(struct hda_codec *codec); -void snd_hda_apply_pincfgs(struct hda_codec *codec, - const struct hda_pintbl *cfg); -void snd_hda_apply_fixup(struct hda_codec *codec, int action); -void snd_hda_pick_fixup(struct hda_codec *codec, - const struct hda_model_fixup *models, - const struct snd_pci_quirk *quirk, - const struct hda_fixup *fixlist); - -static inline void snd_hda_gen_init(struct hda_gen_spec *spec) -{ - snd_array_init(&spec->verbs, sizeof(struct hda_verb *), 8); -} - -static inline void snd_hda_gen_free(struct hda_gen_spec *spec) -{ - snd_array_free(&spec->verbs); -} - #endif /* __SOUND_HDA_AUTO_PARSER_H */ diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 0037147dcd54..e7749dee7c40 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -1253,6 +1253,7 @@ int snd_hda_codec_new(struct hda_bus *bus, snd_array_init(&codec->conn_lists, sizeof(hda_nid_t), 64); snd_array_init(&codec->spdif_out, sizeof(struct hda_spdif_out), 16); snd_array_init(&codec->jacktbl, sizeof(struct hda_jack_tbl), 16); + snd_array_init(&codec->verbs, sizeof(struct hda_verb *), 8); INIT_DELAYED_WORK(&codec->jackpoll_work, hda_jackpoll_work); #ifdef CONFIG_PM @@ -2407,6 +2408,7 @@ int snd_hda_codec_reset(struct hda_codec *codec) snd_array_free(&codec->driver_pins); snd_array_free(&codec->cvt_setups); snd_array_free(&codec->spdif_out); + snd_array_free(&codec->verbs); codec->num_pcms = 0; codec->pcm_info = NULL; codec->preset = NULL; diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index cab39b23d37c..a1cb28ff3968 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -896,6 +896,14 @@ struct hda_codec { /* jack detection */ struct snd_array jacks; #endif + + /* fix-up list */ + int fixup_id; + const struct hda_fixup *fixup_list; + const char *fixup_name; + + /* additional init verbs */ + struct snd_array verbs; }; /* direction */ diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index ff56da82b7a0..de12dcc5accc 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -385,6 +385,59 @@ int snd_hda_check_board_codec_sid_config(struct hda_codec *codec, int snd_hda_add_new_ctls(struct hda_codec *codec, const struct snd_kcontrol_new *knew); +/* + * Fix-up pin default configurations and add default verbs + */ + +struct hda_pintbl { + hda_nid_t nid; + u32 val; +}; + +struct hda_model_fixup { + const int id; + const char *name; +}; + +struct hda_fixup { + int type; + bool chained; + int chain_id; + union { + const struct hda_pintbl *pins; + const struct hda_verb *verbs; + void (*func)(struct hda_codec *codec, + const struct hda_fixup *fix, + int action); + } v; +}; + +/* fixup types */ +enum { + HDA_FIXUP_INVALID, + HDA_FIXUP_PINS, + HDA_FIXUP_VERBS, + HDA_FIXUP_FUNC, +}; + +/* fixup action definitions */ +enum { + HDA_FIXUP_ACT_PRE_PROBE, + HDA_FIXUP_ACT_PROBE, + HDA_FIXUP_ACT_INIT, + HDA_FIXUP_ACT_BUILD, +}; + +int snd_hda_add_verbs(struct hda_codec *codec, const struct hda_verb *list); +void snd_hda_apply_verbs(struct hda_codec *codec); +void snd_hda_apply_pincfgs(struct hda_codec *codec, + const struct hda_pintbl *cfg); +void snd_hda_apply_fixup(struct hda_codec *codec, int action); +void snd_hda_pick_fixup(struct hda_codec *codec, + const struct hda_model_fixup *models, + const struct snd_pci_quirk *quirk, + const struct hda_fixup *fixlist); + /* * unsolicited event handler */ diff --git a/sound/pci/hda/patch_cirrus.c b/sound/pci/hda/patch_cirrus.c index a2537b2f8724..7b0b8c305737 100644 --- a/sound/pci/hda/patch_cirrus.c +++ b/sound/pci/hda/patch_cirrus.c @@ -34,8 +34,6 @@ */ struct cs_spec { - struct hda_gen_spec gen; - struct auto_pin_cfg autocfg; struct hda_multi_out multiout; struct snd_kcontrol *vmaster_sw; @@ -1201,7 +1199,7 @@ static int cs_init(struct hda_codec *codec) snd_hda_sequence_write(codec, cs_coef_init_verbs); - snd_hda_gen_apply_verbs(codec); + snd_hda_apply_verbs(codec); if (spec->gpio_mask) { snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_MASK, @@ -1252,7 +1250,6 @@ static void cs_free(struct hda_codec *codec) struct cs_spec *spec = codec->spec; kfree(spec->capture_bind[0]); kfree(spec->capture_bind[1]); - snd_hda_gen_free(&spec->gen); kfree(codec->spec); } @@ -1443,7 +1440,6 @@ static int patch_cs420x(struct hda_codec *codec) if (!spec) return -ENOMEM; codec->spec = spec; - snd_hda_gen_init(&spec->gen); spec->vendor_nid = CS420X_VENDOR_NID; @@ -1981,7 +1977,6 @@ static int patch_cs4210(struct hda_codec *codec) if (!spec) return -ENOMEM; codec->spec = spec; - snd_hda_gen_init(&spec->gen); spec->vendor_nid = CS4210_VENDOR_NID; @@ -2021,7 +2016,6 @@ static int patch_cs4213(struct hda_codec *codec) if (!spec) return -ENOMEM; codec->spec = spec; - snd_hda_gen_init(&spec->gen); spec->vendor_nid = CS4213_VENDOR_NID; diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index dd798c3196ff..a52f5662f69c 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -67,8 +67,6 @@ struct imux_info { }; struct conexant_spec { - struct hda_gen_spec gen; - const struct snd_kcontrol_new *mixers[5]; int num_mixers; hda_nid_t vmaster_nid; @@ -451,7 +449,6 @@ static int conexant_init(struct hda_codec *codec) static void conexant_free(struct hda_codec *codec) { struct conexant_spec *spec = codec->spec; - snd_hda_gen_free(&spec->gen); snd_hda_detach_beep_device(codec); kfree(spec); } @@ -4033,7 +4030,7 @@ static void cx_auto_init_digital(struct hda_codec *codec) static int cx_auto_init(struct hda_codec *codec) { struct conexant_spec *spec = codec->spec; - snd_hda_gen_apply_verbs(codec); + snd_hda_apply_verbs(codec); cx_auto_init_output(codec); cx_auto_init_input(codec); cx_auto_init_digital(codec); @@ -4533,7 +4530,6 @@ static int patch_conexant_auto(struct hda_codec *codec) if (!spec) return -ENOMEM; codec->spec = spec; - snd_hda_gen_init(&spec->gen); switch (codec->vendor_id) { case 0x14f15045: diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index fbdcbded3417..567d93f6c698 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -121,8 +121,6 @@ struct nid_path { }; struct alc_spec { - struct hda_gen_spec gen; - /* codec parameterization */ const struct snd_kcontrol_new *mixers[5]; /* mixer arrays */ unsigned int num_mixers; @@ -1727,7 +1725,7 @@ static int alc_init(struct hda_codec *codec) alc_fix_pll(codec); alc_auto_init_amp(codec, spec->init_amp); - snd_hda_gen_apply_verbs(codec); + snd_hda_apply_verbs(codec); alc_auto_init_std(codec); if (spec->vmaster_mute.sw_kctl && spec->vmaster_mute.hook) @@ -2117,7 +2115,6 @@ static void alc_free(struct hda_codec *codec) alc_free_kctls(codec); alc_free_bind_ctls(codec); snd_array_free(&spec->paths); - snd_hda_gen_free(&spec->gen); kfree(spec); snd_hda_detach_beep_device(codec); } @@ -4525,7 +4522,6 @@ static int alc_alloc_spec(struct hda_codec *codec, hda_nid_t mixer_nid) codec->spec = spec; codec->single_adc_amp = 1; spec->mixer_nid = mixer_nid; - snd_hda_gen_init(&spec->gen); snd_array_init(&spec->kctls, sizeof(struct snd_kcontrol_new), 32); snd_array_init(&spec->bind_ctls, sizeof(struct hda_bind_ctls *), 8); snd_array_init(&spec->paths, sizeof(struct nid_path), 8); @@ -5001,7 +4997,7 @@ static void alc260_fixup_gpio1_toggle(struct hda_codec *codec, spec->autocfg.hp_pins[0] = 0x0f; /* copy it for automute */ snd_hda_jack_detect_enable_callback(codec, 0x0f, ALC_HP_EVENT, alc_hp_automute); - snd_hda_gen_add_verbs(&spec->gen, alc_gpio1_init_verbs); + snd_hda_add_verbs(codec, alc_gpio1_init_verbs); } } @@ -5878,7 +5874,7 @@ static int alc268_parse_auto_config(struct hda_codec *codec) if (err > 0) { if (!spec->no_analog && spec->autocfg.speaker_pins[0] != 0x1d) { add_mixer(spec, alc268_beep_mixer); - snd_hda_gen_add_verbs(&spec->gen, alc268_beep_init_verbs); + snd_hda_add_verbs(codec, alc268_beep_init_verbs); } } return err; diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 09bb64996d72..b224b3d68184 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -120,8 +120,6 @@ enum { }; struct via_spec { - struct hda_gen_spec gen; - /* codec parameterization */ const struct snd_kcontrol_new *mixers[6]; unsigned int num_mixers; @@ -252,7 +250,6 @@ static struct via_spec * via_new_spec(struct hda_codec *codec) /* VT1708BCE & VT1708S are almost same */ if (spec->codec_type == VT1708BCE) spec->codec_type = VT1708S; - snd_hda_gen_init(&spec->gen); return spec; } @@ -1657,7 +1654,6 @@ static void via_free(struct hda_codec *codec) vt1708_stop_hp_work(spec); kfree(spec->bind_cap_vol); kfree(spec->bind_cap_sw); - snd_hda_gen_free(&spec->gen); kfree(spec); } -- cgit v1.2.3 From 7e35dd3d6b7eeeb46f8b82a552fefb7cce3f7580 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 19 Dec 2012 11:39:00 +0100 Subject: ALSA: hda/realtek - Fix split stereo dmic code The previous commit passed an utterly wrong value for checking the split inv dmic pin. This patch fixes it and also tries to remove inv_dmic_split_idx field. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 58 +++++++++++++++++++++++++++---------------- 1 file changed, 36 insertions(+), 22 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 567d93f6c698..9218d3086f67 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -182,7 +182,6 @@ struct alc_spec { int int_mic_idx, ext_mic_idx, dock_mic_idx; /* for auto-mic */ hda_nid_t inv_dmic_pin; hda_nid_t shared_mic_vref_pin; - int inv_dmic_split_idx; /* used internally for inv_dmic_split */ /* DAC list */ int num_all_dacs; @@ -2552,23 +2551,19 @@ static int parse_capvol_in_path(struct hda_codec *codec, struct nid_path *path) static unsigned int amp_val_replace_channels(unsigned int val, unsigned int chs); static int add_single_cap_ctl(struct hda_codec *codec, const char *label, - int idx, bool is_switch, unsigned int ctl) + int idx, bool is_switch, unsigned int ctl, + bool inv_dmic) { struct alc_spec *spec = codec->spec; char tmpname[44]; int type = is_switch ? ALC_CTL_WIDGET_MUTE : ALC_CTL_WIDGET_VOL; const char *sfx = is_switch ? "Switch" : "Volume"; - unsigned int chs; + unsigned int chs = inv_dmic ? 1 : 3; int err; if (!ctl) return 0; - if (idx == spec->inv_dmic_split_idx) - chs = 1; - else - chs = 3; - if (label) snprintf(tmpname, sizeof(tmpname), "%s Capture %s", label, sfx); @@ -2591,15 +2586,36 @@ static int add_single_cap_ctl(struct hda_codec *codec, const char *label, amp_val_replace_channels(ctl, 2)); } +static bool is_inv_dmic_pin(struct hda_codec *codec, hda_nid_t nid) +{ + struct alc_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + unsigned int val; + int i; + + if (!spec->inv_dmic_split) + return false; + for (i = 0; i < cfg->num_inputs; i++) { + if (cfg->inputs[i].pin != nid) + continue; + if (cfg->inputs[i].type != AUTO_PIN_MIC) + return false; + val = snd_hda_codec_get_pincfg(codec, nid); + return snd_hda_get_input_pin_attr(val) == INPUT_PIN_ATTR_INT; + } + return false; +} + /* create single (and simple) capture volume and switch controls */ static int create_single_cap_vol_ctl(struct hda_codec *codec, int idx, - unsigned int vol_ctl, unsigned int sw_ctl) + unsigned int vol_ctl, unsigned int sw_ctl, + bool inv_dmic) { int err; - err = add_single_cap_ctl(codec, NULL, idx, false, vol_ctl); + err = add_single_cap_ctl(codec, NULL, idx, false, vol_ctl, inv_dmic); if (err < 0) return err; - err = add_single_cap_ctl(codec, NULL, idx, true, sw_ctl); + err = add_single_cap_ctl(codec, NULL, idx, true, sw_ctl, inv_dmic); if (err < 0) return err; return 0; @@ -2665,16 +2681,19 @@ static int create_multi_cap_vol_ctl(struct hda_codec *codec) for (i = 0; i < imux->num_items; i++) { const char *label; + bool inv_dmic; label = hda_get_autocfg_input_label(codec, &spec->autocfg, i); if (prev_label && !strcmp(label, prev_label)) type_idx++; else type_idx = 0; prev_label = label; + inv_dmic = is_inv_dmic_pin(codec, spec->imux_pins[i]); for (type = 0; type < 2; type++) { err = add_single_cap_ctl(codec, label, type_idx, type, - get_first_cap_ctl(codec, i, type)); + get_first_cap_ctl(codec, i, type), + inv_dmic); if (err < 0) return err; } @@ -2703,6 +2722,7 @@ static int create_capture_mixers(struct hda_codec *codec) for (n = 0; n < nums; n++) { bool multi = false; + bool inv_dmic = false; int vol, sw; vol = sw = 0; @@ -2721,10 +2741,13 @@ static int create_capture_mixers(struct hda_codec *codec) sw = path->ctls[NID_PATH_MUTE_CTL]; else if (sw != path->ctls[NID_PATH_MUTE_CTL]) multi = true; + if (is_inv_dmic_pin(codec, spec->imux_pins[i])) + inv_dmic = true; } if (!multi) - err = create_single_cap_vol_ctl(codec, n, vol, sw); + err = create_single_cap_vol_ctl(codec, n, vol, sw, + inv_dmic); else if (!spec->multi_cap_vol) err = create_bind_cap_vol_ctl(codec, n, vol, sw); else @@ -2751,7 +2774,6 @@ static int alc_auto_create_input_ctls(struct hda_codec *codec) if (num_adcs < 0) return 0; - spec->inv_dmic_split_idx = -1; for (i = 0; i < cfg->num_inputs; i++) { hda_nid_t pin; const char *label; @@ -2805,14 +2827,6 @@ static int alc_auto_create_input_ctls(struct hda_codec *codec) imux_added = true; } } - - if (spec->inv_dmic_split) { - if (cfg->inputs[i].type == AUTO_PIN_MIC) { - unsigned int def_conf = snd_hda_codec_get_pincfg(codec, pin); - if (snd_hda_get_input_pin_attr(def_conf) == INPUT_PIN_ATTR_INT) - spec->inv_dmic_split_idx = i; - } - } } return 0; -- cgit v1.2.3 From 5ec16d12c896b6ea710ac74e68e2f431c80d1c62 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 28 Nov 2012 18:11:59 +0100 Subject: ALSA: hda - Rearrange INPUT_PIN_ATTR_* Put INPUT_PIN_ATTR_FRONT after INPUT_PIN_ATTR_REAR, and define INPUT_PIN_ATTR_LAST to point to the last element. This is a preliminary work for cleaning up Realtek auto-mic parser. In the auto-mic implementation, the front panel is preferred over the rear panel. By arranging the attr definitions like in this commit, we can simply use sort() for figuring out the priority order. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_auto_parser.c | 2 +- sound/pci/hda/hda_auto_parser.h | 3 ++- sound/pci/hda/patch_via.c | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_auto_parser.c b/sound/pci/hda/hda_auto_parser.c index d4606884f6a3..44c81d34544d 100644 --- a/sound/pci/hda/hda_auto_parser.c +++ b/sound/pci/hda/hda_auto_parser.c @@ -363,7 +363,7 @@ static const char *hda_get_input_pin_label(struct hda_codec *codec, { unsigned int def_conf; static const char * const mic_names[] = { - "Internal Mic", "Dock Mic", "Mic", "Front Mic", "Rear Mic", + "Internal Mic", "Dock Mic", "Mic", "Rear Mic", "Front Mic" }; int attr; diff --git a/sound/pci/hda/hda_auto_parser.h b/sound/pci/hda/hda_auto_parser.h index ff11074b6c8b..f74807138b49 100644 --- a/sound/pci/hda/hda_auto_parser.h +++ b/sound/pci/hda/hda_auto_parser.h @@ -51,8 +51,9 @@ enum { INPUT_PIN_ATTR_INT, /* internal mic/line-in */ INPUT_PIN_ATTR_DOCK, /* docking mic/line-in */ INPUT_PIN_ATTR_NORMAL, /* mic/line-in jack */ - INPUT_PIN_ATTR_FRONT, /* mic/line-in jack in front */ INPUT_PIN_ATTR_REAR, /* mic/line-in jack in rear */ + INPUT_PIN_ATTR_FRONT, /* mic/line-in jack in front */ + INPUT_PIN_ATTR_LAST = INPUT_PIN_ATTR_FRONT, }; int snd_hda_get_input_pin_attr(unsigned int def_conf); diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index b224b3d68184..d3c852ab105e 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -1913,7 +1913,7 @@ static void mangle_smart51(struct hda_codec *codec) int i, j, nums, attr; int pins[AUTO_CFG_MAX_INS]; - for (attr = INPUT_PIN_ATTR_REAR; attr >= INPUT_PIN_ATTR_NORMAL; attr--) { + for (attr = INPUT_PIN_ATTR_LAST; attr >= INPUT_PIN_ATTR_NORMAL; attr--) { nums = 0; for (i = 0; i < cfg->num_inputs; i++) { unsigned int def; -- cgit v1.2.3 From ab16c6dd79389761eca1366d809a002b44b7f960 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 29 Nov 2012 09:56:50 +0100 Subject: ALSA: hda - More generic auto-mic switching for Realtek codecs This patch extends the capability of the auto-mic feature. Instead of limiting the automatic input-source selection only to the mics (internal, external and dock mics), allow it for generic inputs, e.g. switching between the rear line-in and the front mic. The logic is to check the attribute and location of input pins, and enable the automatic selection feature only if all such pins are in different locations (e.g. internal, front, rear, etc) and line-in or mic pins. That is, if multiple input pins are assigned to a single location, the feature isn't enabled because we don't know the priority. (You may wonder why this restriction doesn't exist for the headphone automute. The reason is that the output case is different from the input: the input source is an exclusive selection while the output can be multiplexed.) Note that, for avoiding regressions, the line-in auto switching feature isn't activated as default. It has to be set explicitly via spec->line_in_auto_switch flag in a fixup code. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 143 +++++++++++++++++++++++------------------- 1 file changed, 78 insertions(+), 65 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 9218d3086f67..f210ca7575b2 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include "hda_codec.h" @@ -90,6 +91,13 @@ struct alc_multi_io { #define ALC_FIXUP_ACT_INIT HDA_FIXUP_ACT_INIT #define ALC_FIXUP_ACT_BUILD HDA_FIXUP_ACT_BUILD +#define MAX_AUTO_MIC_PINS 3 + +struct alc_automic_entry { + hda_nid_t pin; /* pin */ + int idx; /* imux index, -1 = invalid */ + unsigned int attr; /* pin attribute (INPUT_PIN_ATTR_*) */ +}; #define MAX_NID_PATH_DEPTH 5 @@ -159,9 +167,6 @@ struct alc_spec { /* capture source */ struct hda_input_mux input_mux; unsigned int cur_mux[3]; - hda_nid_t ext_mic_pin; - hda_nid_t dock_mic_pin; - hda_nid_t int_mic_pin; /* channel model */ const struct hda_channel_mode *channel_mode; @@ -179,7 +184,6 @@ struct alc_spec { hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS]; hda_nid_t imux_pins[HDA_MAX_NUM_INPUTS]; unsigned int dyn_adc_idx[HDA_MAX_NUM_INPUTS]; - int int_mic_idx, ext_mic_idx, dock_mic_idx; /* for auto-mic */ hda_nid_t inv_dmic_pin; hda_nid_t shared_mic_vref_pin; @@ -190,6 +194,10 @@ struct alc_spec { /* path list */ struct snd_array paths; + /* auto-mic stuff */ + int am_num_entries; + struct alc_automic_entry am_entry[MAX_AUTO_MIC_PINS]; + /* hooks */ void (*init_hook)(struct hda_codec *codec); #ifdef CONFIG_PM @@ -210,6 +218,7 @@ struct alc_spec { unsigned int automute_speaker_possible:1; /* there are speakers and either LO or HP */ unsigned int automute_lo_possible:1; /* there are line outs and HP */ unsigned int keep_vref_in_automute:1; /* Don't clear VREF in automute */ + unsigned int line_in_auto_switch:1; /* allow line-in auto switch */ /* other flags */ unsigned int need_dac_fix:1; /* need to limit DACs for multi channels */ @@ -608,20 +617,18 @@ static void alc_line_automute(struct hda_codec *codec, struct hda_jack_tbl *jack static void alc_mic_automute(struct hda_codec *codec, struct hda_jack_tbl *jack) { struct alc_spec *spec = codec->spec; - hda_nid_t *pins = spec->imux_pins; + int i; if (!spec->auto_mic) return; - if (snd_BUG_ON(spec->int_mic_idx < 0 || spec->ext_mic_idx < 0)) - return; - if (snd_hda_jack_detect(codec, pins[spec->ext_mic_idx])) - alc_mux_select(codec, 0, spec->ext_mic_idx, false); - else if (spec->dock_mic_idx >= 0 && - snd_hda_jack_detect(codec, pins[spec->dock_mic_idx])) - alc_mux_select(codec, 0, spec->dock_mic_idx, false); - else - alc_mux_select(codec, 0, spec->int_mic_idx, false); + for (i = spec->am_num_entries - 1; i > 0; i--) { + if (snd_hda_jack_detect(codec, spec->am_entry[i].pin)) { + alc_mux_select(codec, 0, spec->am_entry[i].idx, false); + return; + } + } + alc_mux_select(codec, 0, spec->am_entry[0].idx, false); } /* update the master volume per volume-knob's unsol event */ @@ -970,26 +977,33 @@ static bool alc_auto_mic_check_imux(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; const struct hda_input_mux *imux; + int i; imux = &spec->input_mux; - spec->ext_mic_idx = find_idx_in_nid_list(spec->ext_mic_pin, - spec->imux_pins, imux->num_items); - spec->int_mic_idx = find_idx_in_nid_list(spec->int_mic_pin, - spec->imux_pins, imux->num_items); - spec->dock_mic_idx = find_idx_in_nid_list(spec->dock_mic_pin, - spec->imux_pins, imux->num_items); - if (spec->ext_mic_idx < 0 || spec->int_mic_idx < 0) - return false; /* no corresponding imux */ - - snd_hda_jack_detect_enable_callback(codec, spec->ext_mic_pin, - ALC_MIC_EVENT, alc_mic_automute); - if (spec->dock_mic_pin) - snd_hda_jack_detect_enable_callback(codec, spec->dock_mic_pin, + for (i = 0; i < spec->am_num_entries; i++) { + spec->am_entry[i].idx = + find_idx_in_nid_list(spec->am_entry[i].pin, + spec->imux_pins, imux->num_items); + if (spec->am_entry[i].idx < 0) + return false; /* no corresponding imux */ + } + + /* we don't need the jack detection for the first pin */ + for (i = 1; i < spec->am_num_entries; i++) + snd_hda_jack_detect_enable_callback(codec, + spec->am_entry[i].pin, ALC_MIC_EVENT, alc_mic_automute); return true; } +static int compare_attr(const void *ap, const void *bp) +{ + const struct alc_automic_entry *a = ap; + const struct alc_automic_entry *b = bp; + return (int)(a->attr - b->attr); +} + /* * Check the availability of auto-mic switch; * Set up if really supported @@ -998,66 +1012,63 @@ static int alc_init_auto_mic(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; struct auto_pin_cfg *cfg = &spec->autocfg; - hda_nid_t fixed, ext, dock; - int i; + unsigned int types; + int i, num_pins; - spec->ext_mic_idx = spec->int_mic_idx = spec->dock_mic_idx = -1; - - fixed = ext = dock = 0; + types = 0; + num_pins = 0; for (i = 0; i < cfg->num_inputs; i++) { hda_nid_t nid = cfg->inputs[i].pin; - unsigned int defcfg; - defcfg = snd_hda_codec_get_pincfg(codec, nid); - switch (snd_hda_get_input_pin_attr(defcfg)) { + unsigned int attr; + attr = snd_hda_codec_get_pincfg(codec, nid); + attr = snd_hda_get_input_pin_attr(attr); + if (types & (1 << attr)) + return 0; /* already occupied */ + switch (attr) { case INPUT_PIN_ATTR_INT: - if (fixed) - return 0; /* already occupied */ if (cfg->inputs[i].type != AUTO_PIN_MIC) return 0; /* invalid type */ - fixed = nid; break; case INPUT_PIN_ATTR_UNUSED: return 0; /* invalid entry */ - case INPUT_PIN_ATTR_DOCK: - if (dock) - return 0; /* already occupied */ - if (cfg->inputs[i].type > AUTO_PIN_LINE_IN) - return 0; /* invalid type */ - dock = nid; - break; default: - if (ext) - return 0; /* already occupied */ - if (cfg->inputs[i].type != AUTO_PIN_MIC) + if (cfg->inputs[i].type > AUTO_PIN_LINE_IN) return 0; /* invalid type */ - ext = nid; + if (!spec->line_in_auto_switch && + cfg->inputs[i].type != AUTO_PIN_MIC) + return 0; /* only mic is allowed */ + if (!is_jack_detectable(codec, nid)) + return 0; /* no unsol support */ break; } + if (num_pins >= MAX_AUTO_MIC_PINS) + return 0; + types |= (1 << attr); + spec->am_entry[num_pins].pin = nid; + spec->am_entry[num_pins].attr = attr; + num_pins++; } - if (!ext && dock) { - ext = dock; - dock = 0; - } - if (!ext || !fixed) + + if (num_pins < 2) return 0; - if (!is_jack_detectable(codec, ext)) - return 0; /* no unsol support */ - if (dock && !is_jack_detectable(codec, dock)) - return 0; /* no unsol support */ - /* check imux indices */ - spec->ext_mic_pin = ext; - spec->int_mic_pin = fixed; - spec->dock_mic_pin = dock; + spec->am_num_entries = num_pins; + /* sort the am_entry in the order of attr so that the pin with a + * higher attr will be selected when the jack is plugged. + */ + sort(spec->am_entry, num_pins, sizeof(spec->am_entry[0]), + compare_attr, NULL); if (!alc_auto_mic_check_imux(codec)) return 0; spec->auto_mic = 1; spec->num_adc_nids = 1; - spec->cur_mux[0] = spec->int_mic_idx; + spec->cur_mux[0] = spec->am_entry[0].idx; snd_printdd("realtek: Enable auto-mic switch on NID 0x%x/0x%x/0x%x\n", - ext, fixed, dock); + spec->am_entry[0].pin, + spec->am_entry[1].pin, + spec->am_entry[2].pin); return 0; } @@ -6199,8 +6210,10 @@ static void alc271_hp_gate_mic_jack(struct hda_codec *codec, { struct alc_spec *spec = codec->spec; + if (snd_BUG_ON(!spec->am_entry[1].pin || !spec->autocfg.hp_pins[0])) + return; if (action == ALC_FIXUP_ACT_PROBE) - snd_hda_jack_set_gating_jack(codec, spec->ext_mic_pin, + snd_hda_jack_set_gating_jack(codec, spec->am_entry[1].pin, spec->autocfg.hp_pins[0]); } -- cgit v1.2.3 From fdf52cab88bc76d0826c42d0f7e014d31e4a7445 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 19 Dec 2012 12:24:03 +0100 Subject: ALSA: hda/realtek - Remove redundant argument from alc_mux_select() The argument "force" is always false in the recent code. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index f210ca7575b2..c85899d50404 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -360,7 +360,7 @@ static void update_shared_mic_hp(struct hda_codec *codec, bool set_as_mic) /* select the given imux item; either unmute exclusively or select the route */ static int alc_mux_select(struct hda_codec *codec, unsigned int adc_idx, - unsigned int idx, bool force) + unsigned int idx) { struct alc_spec *spec = codec->spec; const struct hda_input_mux *imux; @@ -372,7 +372,7 @@ static int alc_mux_select(struct hda_codec *codec, unsigned int adc_idx, if (idx >= imux->num_items) idx = imux->num_items - 1; - if (spec->cur_mux[adc_idx] == idx && !force) + if (spec->cur_mux[adc_idx] == idx) return 0; path = get_nid_path(codec, spec->imux_pins[spec->cur_mux[adc_idx]], @@ -407,7 +407,7 @@ static int alc_mux_enum_put(struct snd_kcontrol *kcontrol, struct hda_codec *codec = snd_kcontrol_chip(kcontrol); unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); return alc_mux_select(codec, adc_idx, - ucontrol->value.enumerated.item[0], false); + ucontrol->value.enumerated.item[0]); } /* @@ -624,11 +624,11 @@ static void alc_mic_automute(struct hda_codec *codec, struct hda_jack_tbl *jack) for (i = spec->am_num_entries - 1; i > 0; i--) { if (snd_hda_jack_detect(codec, spec->am_entry[i].pin)) { - alc_mux_select(codec, 0, spec->am_entry[i].idx, false); + alc_mux_select(codec, 0, spec->am_entry[i].idx); return; } } - alc_mux_select(codec, 0, spec->am_entry[0].idx, false); + alc_mux_select(codec, 0, spec->am_entry[0].idx); } /* update the master volume per volume-knob's unsol event */ -- cgit v1.2.3 From 352f7f914ebb8fe19f9b3f03e7767b04eedf5be3 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 19 Dec 2012 12:52:06 +0100 Subject: ALSA: hda - Merge Realtek parser code to generic parser Finally the whole generic parser code in Realtek driver is moved into hda_generic.c so that it can be used for generic codec driver. The old dumb generic driver is replaced. Yay. The future plan is to adapt this generic parser for other codecs, i.e. the codec driver calls the exported functions in generic driver but adds some codec-specific fixes and setups. As of this commit, the complete driver code is still duplicated in Realtek codec driver. The big code reduction will come from now on. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 4130 ++++++++++++++++++++++++++++++++++--------- sound/pci/hda/hda_generic.h | 199 +++ 2 files changed, 3518 insertions(+), 811 deletions(-) create mode 100644 sound/pci/hda/hda_generic.h (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index b81d3d0b952d..2d19b915dbf1 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -23,1063 +23,3571 @@ #include #include #include +#include #include +#include #include "hda_codec.h" #include "hda_local.h" +#include "hda_auto_parser.h" +#include "hda_jack.h" +#include "hda_generic.h" -/* widget node for parsing */ -struct hda_gnode { - hda_nid_t nid; /* NID of this widget */ - unsigned short nconns; /* number of input connections */ - hda_nid_t *conn_list; - hda_nid_t slist[2]; /* temporay list */ - unsigned int wid_caps; /* widget capabilities */ - unsigned char type; /* widget type */ - unsigned char pin_ctl; /* pin controls */ - unsigned char checked; /* the flag indicates that the node is already parsed */ - unsigned int pin_caps; /* pin widget capabilities */ - unsigned int def_cfg; /* default configuration */ - unsigned int amp_out_caps; /* AMP out capabilities */ - unsigned int amp_in_caps; /* AMP in capabilities */ - struct list_head list; -}; - -/* patch-specific record */ - -#define MAX_PCM_VOLS 2 -struct pcm_vol { - struct hda_gnode *node; /* Node for PCM volume */ - unsigned int index; /* connection of PCM volume */ -}; -struct hda_gspec { - struct hda_gnode *dac_node[2]; /* DAC node */ - struct hda_gnode *out_pin_node[2]; /* Output pin (Line-Out) node */ - struct pcm_vol pcm_vol[MAX_PCM_VOLS]; /* PCM volumes */ - unsigned int pcm_vol_nodes; /* number of PCM volumes */ +/* initialize hda_gen_spec struct */ +int snd_hda_gen_spec_init(struct hda_gen_spec *spec) +{ + snd_array_init(&spec->kctls, sizeof(struct snd_kcontrol_new), 32); + snd_array_init(&spec->bind_ctls, sizeof(struct hda_bind_ctls *), 8); + snd_array_init(&spec->paths, sizeof(struct nid_path), 8); + return 0; +} +EXPORT_SYMBOL_HDA(snd_hda_gen_spec_init); - struct hda_gnode *adc_node; /* ADC node */ - struct hda_gnode *cap_vol_node; /* Node for capture volume */ - unsigned int cur_cap_src; /* current capture source */ - struct hda_input_mux input_mux; +static struct snd_kcontrol_new * +add_kctl(struct hda_gen_spec *spec, const char *name, + const struct snd_kcontrol_new *temp) +{ + struct snd_kcontrol_new *knew = snd_array_new(&spec->kctls); + if (!knew) + return NULL; + *knew = *temp; + if (name) + knew->name = kstrdup(name, GFP_KERNEL); + else if (knew->name) + knew->name = kstrdup(knew->name, GFP_KERNEL); + if (!knew->name) + return NULL; + return knew; +} - unsigned int def_amp_in_caps; - unsigned int def_amp_out_caps; +static void free_kctls(struct hda_gen_spec *spec) +{ + if (spec->kctls.list) { + struct snd_kcontrol_new *kctl = spec->kctls.list; + int i; + for (i = 0; i < spec->kctls.used; i++) + kfree(kctl[i].name); + } + snd_array_free(&spec->kctls); +} - struct hda_pcm pcm_rec; /* PCM information */ +static struct hda_bind_ctls *new_bind_ctl(struct hda_codec *codec, + unsigned int nums, + struct hda_ctl_ops *ops) +{ + struct hda_gen_spec *spec = codec->spec; + struct hda_bind_ctls **ctlp, *ctl; + ctlp = snd_array_new(&spec->bind_ctls); + if (!ctlp) + return NULL; + ctl = kzalloc(sizeof(*ctl) + sizeof(long) * (nums + 1), GFP_KERNEL); + *ctlp = ctl; + if (ctl) + ctl->ops = ops; + return ctl; +} - struct list_head nid_list; /* list of widgets */ +static void free_bind_ctls(struct hda_gen_spec *spec) +{ + if (spec->bind_ctls.list) { + struct hda_bind_ctls **ctl = spec->bind_ctls.list; + int i; + for (i = 0; i < spec->bind_ctls.used; i++) + kfree(ctl[i]); + } + snd_array_free(&spec->bind_ctls); +} -#ifdef CONFIG_PM -#define MAX_LOOPBACK_AMPS 7 - struct hda_loopback_check loopback; - int num_loopbacks; - struct hda_amp_list loopback_list[MAX_LOOPBACK_AMPS + 1]; -#endif -}; +void snd_hda_gen_spec_free(struct hda_gen_spec *spec) +{ + if (!spec) + return; + free_kctls(spec); + free_bind_ctls(spec); + snd_array_free(&spec->paths); +} +EXPORT_SYMBOL_HDA(snd_hda_gen_spec_free); /* - * retrieve the default device type from the default config value + * parsing paths */ -#define defcfg_type(node) (((node)->def_cfg & AC_DEFCFG_DEVICE) >> \ - AC_DEFCFG_DEVICE_SHIFT) -#define defcfg_location(node) (((node)->def_cfg & AC_DEFCFG_LOCATION) >> \ - AC_DEFCFG_LOCATION_SHIFT) -#define defcfg_port_conn(node) (((node)->def_cfg & AC_DEFCFG_PORT_CONN) >> \ - AC_DEFCFG_PORT_CONN_SHIFT) -/* - * destructor +/* get the path between the given NIDs; + * passing 0 to either @pin or @dac behaves as a wildcard */ -static void snd_hda_generic_free(struct hda_codec *codec) +struct nid_path *snd_hda_get_nid_path(struct hda_codec *codec, + hda_nid_t from_nid, hda_nid_t to_nid) { - struct hda_gspec *spec = codec->spec; - struct hda_gnode *node, *n; + struct hda_gen_spec *spec = codec->spec; + int i; - if (! spec) - return; - /* free all widgets */ - list_for_each_entry_safe(node, n, &spec->nid_list, list) { - if (node->conn_list != node->slist) - kfree(node->conn_list); - kfree(node); + for (i = 0; i < spec->paths.used; i++) { + struct nid_path *path = snd_array_elem(&spec->paths, i); + if (path->depth <= 0) + continue; + if ((!from_nid || path->path[0] == from_nid) && + (!to_nid || path->path[path->depth - 1] == to_nid)) + return path; } - kfree(spec); + return NULL; } +EXPORT_SYMBOL_HDA(snd_hda_get_nid_path); - -/* - * add a new widget node and read its attributes - */ -static int add_new_node(struct hda_codec *codec, struct hda_gspec *spec, hda_nid_t nid) +/* check whether the given DAC is already found in any existing paths */ +static bool is_dac_already_used(struct hda_codec *codec, hda_nid_t nid) { - struct hda_gnode *node; - int nconns; - hda_nid_t conn_list[HDA_MAX_CONNECTIONS]; + struct hda_gen_spec *spec = codec->spec; + int i; - node = kzalloc(sizeof(*node), GFP_KERNEL); - if (node == NULL) - return -ENOMEM; - node->nid = nid; - node->wid_caps = get_wcaps(codec, nid); - node->type = get_wcaps_type(node->wid_caps); - if (node->wid_caps & AC_WCAP_CONN_LIST) { - nconns = snd_hda_get_connections(codec, nid, conn_list, - HDA_MAX_CONNECTIONS); - if (nconns < 0) { - kfree(node); - return nconns; - } - } else { - nconns = 0; - } - if (nconns <= ARRAY_SIZE(node->slist)) - node->conn_list = node->slist; - else { - node->conn_list = kmalloc(sizeof(hda_nid_t) * nconns, - GFP_KERNEL); - if (! node->conn_list) { - snd_printk(KERN_ERR "hda-generic: cannot malloc\n"); - kfree(node); - return -ENOMEM; - } + for (i = 0; i < spec->paths.used; i++) { + struct nid_path *path = snd_array_elem(&spec->paths, i); + if (path->path[0] == nid) + return true; } - memcpy(node->conn_list, conn_list, nconns * sizeof(hda_nid_t)); - node->nconns = nconns; + return false; +} - if (node->type == AC_WID_PIN) { - node->pin_caps = snd_hda_query_pin_caps(codec, node->nid); - node->pin_ctl = snd_hda_codec_read(codec, node->nid, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0); - node->def_cfg = snd_hda_codec_get_pincfg(codec, node->nid); - } +/* check whether the given two widgets can be connected */ +static bool is_reachable_path(struct hda_codec *codec, + hda_nid_t from_nid, hda_nid_t to_nid) +{ + if (!from_nid || !to_nid) + return false; + return snd_hda_get_conn_index(codec, to_nid, from_nid, true) >= 0; +} - if (node->wid_caps & AC_WCAP_OUT_AMP) { - if (node->wid_caps & AC_WCAP_AMP_OVRD) - node->amp_out_caps = snd_hda_param_read(codec, node->nid, AC_PAR_AMP_OUT_CAP); - if (! node->amp_out_caps) - node->amp_out_caps = spec->def_amp_out_caps; - } - if (node->wid_caps & AC_WCAP_IN_AMP) { - if (node->wid_caps & AC_WCAP_AMP_OVRD) - node->amp_in_caps = snd_hda_param_read(codec, node->nid, AC_PAR_AMP_IN_CAP); - if (! node->amp_in_caps) - node->amp_in_caps = spec->def_amp_in_caps; +/* nid, dir and idx */ +#define AMP_VAL_COMPARE_MASK (0xffff | (1U << 18) | (0x0f << 19)) + +/* check whether the given ctl is already assigned in any path elements */ +static bool is_ctl_used(struct hda_codec *codec, unsigned int val, int type) +{ + struct hda_gen_spec *spec = codec->spec; + int i; + + val &= AMP_VAL_COMPARE_MASK; + for (i = 0; i < spec->paths.used; i++) { + struct nid_path *path = snd_array_elem(&spec->paths, i); + if ((path->ctls[type] & AMP_VAL_COMPARE_MASK) == val) + return true; } - list_add_tail(&node->list, &spec->nid_list); - return 0; + return false; } -/* - * build the AFG subtree - */ -static int build_afg_tree(struct hda_codec *codec) +/* check whether a control with the given (nid, dir, idx) was assigned */ +static bool is_ctl_associated(struct hda_codec *codec, hda_nid_t nid, + int dir, int idx) { - struct hda_gspec *spec = codec->spec; - int i, nodes, err; - hda_nid_t nid; - - if (snd_BUG_ON(!spec)) - return -EINVAL; - - spec->def_amp_out_caps = snd_hda_param_read(codec, codec->afg, AC_PAR_AMP_OUT_CAP); - spec->def_amp_in_caps = snd_hda_param_read(codec, codec->afg, AC_PAR_AMP_IN_CAP); + unsigned int val = HDA_COMPOSE_AMP_VAL(nid, 3, idx, dir); + return is_ctl_used(codec, val, NID_PATH_VOL_CTL) || + is_ctl_used(codec, val, NID_PATH_MUTE_CTL); +} - nodes = snd_hda_get_sub_nodes(codec, codec->afg, &nid); - if (! nid || nodes < 0) { - printk(KERN_ERR "Invalid AFG subtree\n"); - return -EINVAL; +/* called recursively */ +static bool __parse_nid_path(struct hda_codec *codec, + hda_nid_t from_nid, hda_nid_t to_nid, + int with_aa_mix, struct nid_path *path, int depth) +{ + struct hda_gen_spec *spec = codec->spec; + hda_nid_t conn[16]; + int i, nums; + + if (to_nid == spec->mixer_nid) { + if (!with_aa_mix) + return false; + with_aa_mix = 2; /* mark aa-mix is included */ } - /* parse all nodes belonging to the AFG */ - for (i = 0; i < nodes; i++, nid++) { - if ((err = add_new_node(codec, spec, nid)) < 0) - return err; + nums = snd_hda_get_connections(codec, to_nid, conn, ARRAY_SIZE(conn)); + for (i = 0; i < nums; i++) { + if (conn[i] != from_nid) { + /* special case: when from_nid is 0, + * try to find an empty DAC + */ + if (from_nid || + get_wcaps_type(get_wcaps(codec, conn[i])) != AC_WID_AUD_OUT || + is_dac_already_used(codec, conn[i])) + continue; + } + /* aa-mix is requested but not included? */ + if (!(spec->mixer_nid && with_aa_mix == 1)) + goto found; } - - return 0; + if (depth >= MAX_NID_PATH_DEPTH) + return false; + for (i = 0; i < nums; i++) { + unsigned int type; + type = get_wcaps_type(get_wcaps(codec, conn[i])); + if (type == AC_WID_AUD_OUT || type == AC_WID_AUD_IN || + type == AC_WID_PIN) + continue; + if (__parse_nid_path(codec, from_nid, conn[i], + with_aa_mix, path, depth + 1)) + goto found; + } + return false; + + found: + path->path[path->depth] = conn[i]; + path->idx[path->depth + 1] = i; + if (nums > 1 && get_wcaps_type(get_wcaps(codec, to_nid)) != AC_WID_AUD_MIX) + path->multi[path->depth + 1] = 1; + path->depth++; + return true; } - -/* - * look for the node record for the given NID +/* parse the widget path from the given nid to the target nid; + * when @from_nid is 0, try to find an empty DAC; + * when @with_aa_mix is 0, paths with spec->mixer_nid are excluded. + * when @with_aa_mix is 1, paths without spec->mixer_nid are excluded. + * when @with_aa_mix is 2, no special handling about spec->mixer_nid. */ -/* FIXME: should avoid the braindead linear search */ -static struct hda_gnode *hda_get_node(struct hda_gspec *spec, hda_nid_t nid) +bool snd_hda_parse_nid_path(struct hda_codec *codec, hda_nid_t from_nid, + hda_nid_t to_nid, int with_aa_mix, + struct nid_path *path) { - struct hda_gnode *node; - - list_for_each_entry(node, &spec->nid_list, list) { - if (node->nid == nid) - return node; + if (__parse_nid_path(codec, from_nid, to_nid, with_aa_mix, path, 1)) { + path->path[path->depth] = to_nid; + path->depth++; +#if 0 + snd_printdd("path: depth=%d, %02x/%02x/%02x/%02x/%02x\n", + path->depth, path->path[0], path->path[1], + path->path[2], path->path[3], path->path[4]); +#endif + return true; } - return NULL; + return false; } +EXPORT_SYMBOL_HDA(snd_hda_parse_nid_path); /* - * unmute (and set max vol) the output amplifier + * parse the path between the given NIDs and add to the path list. + * if no valid path is found, return NULL */ -static int unmute_output(struct hda_codec *codec, struct hda_gnode *node) -{ - unsigned int val, ofs; - snd_printdd("UNMUTE OUT: NID=0x%x\n", node->nid); - val = (node->amp_out_caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT; - ofs = (node->amp_out_caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT; - if (val >= ofs) - val -= ofs; - snd_hda_codec_amp_stereo(codec, node->nid, HDA_OUTPUT, 0, 0xff, val); - return 0; +struct nid_path * +snd_hda_add_new_path(struct hda_codec *codec, hda_nid_t from_nid, + hda_nid_t to_nid, int with_aa_mix) +{ + struct hda_gen_spec *spec = codec->spec; + struct nid_path *path; + + if (from_nid && to_nid && !is_reachable_path(codec, from_nid, to_nid)) + return NULL; + + path = snd_array_new(&spec->paths); + if (!path) + return NULL; + memset(path, 0, sizeof(*path)); + if (snd_hda_parse_nid_path(codec, from_nid, to_nid, with_aa_mix, path)) + return path; + /* push back */ + spec->paths.used--; + return NULL; } +EXPORT_SYMBOL_HDA(snd_hda_add_new_path); -/* - * unmute (and set max vol) the input amplifier - */ -static int unmute_input(struct hda_codec *codec, struct hda_gnode *node, unsigned int index) -{ - unsigned int val, ofs; - snd_printdd("UNMUTE IN: NID=0x%x IDX=0x%x\n", node->nid, index); - val = (node->amp_in_caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT; - ofs = (node->amp_in_caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT; - if (val >= ofs) - val -= ofs; - snd_hda_codec_amp_stereo(codec, node->nid, HDA_INPUT, index, 0xff, val); +/* look for an empty DAC slot */ +static hda_nid_t look_for_dac(struct hda_codec *codec, hda_nid_t pin, + bool is_digital) +{ + struct hda_gen_spec *spec = codec->spec; + bool cap_digital; + int i; + + for (i = 0; i < spec->num_all_dacs; i++) { + hda_nid_t nid = spec->all_dacs[i]; + if (!nid || is_dac_already_used(codec, nid)) + continue; + cap_digital = !!(get_wcaps(codec, nid) & AC_WCAP_DIGITAL); + if (is_digital != cap_digital) + continue; + if (is_reachable_path(codec, nid, pin)) + return nid; + } return 0; } -/* - * select the input connection of the given node. - */ -static int select_input_connection(struct hda_codec *codec, struct hda_gnode *node, - unsigned int index) +/* replace the channels in the composed amp value with the given number */ +static unsigned int amp_val_replace_channels(unsigned int val, unsigned int chs) { - snd_printdd("CONNECT: NID=0x%x IDX=0x%x\n", node->nid, index); - return snd_hda_codec_write_cache(codec, node->nid, 0, - AC_VERB_SET_CONNECT_SEL, index); + val &= ~(0x3U << 16); + val |= chs << 16; + return val; } -/* - * clear checked flag of each node in the node list - */ -static void clear_check_flags(struct hda_gspec *spec) +/* check whether the widget has the given amp capability for the direction */ +static bool check_amp_caps(struct hda_codec *codec, hda_nid_t nid, + int dir, unsigned int bits) { - struct hda_gnode *node; + if (!nid) + return false; + if (get_wcaps(codec, nid) & (1 << (dir + 1))) + if (query_amp_caps(codec, nid, dir) & bits) + return true; + return false; +} + +#define nid_has_mute(codec, nid, dir) \ + check_amp_caps(codec, nid, dir, AC_AMPCAP_MUTE) +#define nid_has_volume(codec, nid, dir) \ + check_amp_caps(codec, nid, dir, AC_AMPCAP_NUM_STEPS) + +/* look for a widget suitable for assigning a mute switch in the path */ +static hda_nid_t look_for_out_mute_nid(struct hda_codec *codec, + struct nid_path *path) +{ + int i; + + for (i = path->depth - 1; i >= 0; i--) { + if (nid_has_mute(codec, path->path[i], HDA_OUTPUT)) + return path->path[i]; + if (i != path->depth - 1 && i != 0 && + nid_has_mute(codec, path->path[i], HDA_INPUT)) + return path->path[i]; + } + return 0; +} + +/* look for a widget suitable for assigning a volume ctl in the path */ +static hda_nid_t look_for_out_vol_nid(struct hda_codec *codec, + struct nid_path *path) +{ + int i; - list_for_each_entry(node, &spec->nid_list, list) { - node->checked = 0; + for (i = path->depth - 1; i >= 0; i--) { + if (nid_has_volume(codec, path->path[i], HDA_OUTPUT)) + return path->path[i]; } + return 0; } /* - * parse the output path recursively until reach to an audio output widget - * - * returns 0 if not found, 1 if found, or a negative error code. + * path activation / deactivation */ -static int parse_output_path(struct hda_codec *codec, struct hda_gspec *spec, - struct hda_gnode *node, int dac_idx) + +/* can have the amp-in capability? */ +static bool has_amp_in(struct hda_codec *codec, struct nid_path *path, int idx) { - int i, err; - struct hda_gnode *child; + hda_nid_t nid = path->path[idx]; + unsigned int caps = get_wcaps(codec, nid); + unsigned int type = get_wcaps_type(caps); + + if (!(caps & AC_WCAP_IN_AMP)) + return false; + if (type == AC_WID_PIN && idx > 0) /* only for input pins */ + return false; + return true; +} - if (node->checked) - return 0; +/* can have the amp-out capability? */ +static bool has_amp_out(struct hda_codec *codec, struct nid_path *path, int idx) +{ + hda_nid_t nid = path->path[idx]; + unsigned int caps = get_wcaps(codec, nid); + unsigned int type = get_wcaps_type(caps); + + if (!(caps & AC_WCAP_OUT_AMP)) + return false; + if (type == AC_WID_PIN && !idx) /* only for output pins */ + return false; + return true; +} - node->checked = 1; - if (node->type == AC_WID_AUD_OUT) { - if (node->wid_caps & AC_WCAP_DIGITAL) { - snd_printdd("Skip Digital OUT node %x\n", node->nid); - return 0; - } - snd_printdd("AUD_OUT found %x\n", node->nid); - if (spec->dac_node[dac_idx]) { - /* already DAC node is assigned, just unmute & connect */ - return node == spec->dac_node[dac_idx]; - } - spec->dac_node[dac_idx] = node; - if ((node->wid_caps & AC_WCAP_OUT_AMP) && - spec->pcm_vol_nodes < MAX_PCM_VOLS) { - spec->pcm_vol[spec->pcm_vol_nodes].node = node; - spec->pcm_vol[spec->pcm_vol_nodes].index = 0; - spec->pcm_vol_nodes++; - } - return 1; /* found */ - } +/* check whether the given (nid,dir,idx) is active */ +static bool is_active_nid(struct hda_codec *codec, hda_nid_t nid, + unsigned int idx, unsigned int dir) +{ + struct hda_gen_spec *spec = codec->spec; + int i, n; - for (i = 0; i < node->nconns; i++) { - child = hda_get_node(spec, node->conn_list[i]); - if (! child) + for (n = 0; n < spec->paths.used; n++) { + struct nid_path *path = snd_array_elem(&spec->paths, n); + if (!path->active) continue; - err = parse_output_path(codec, spec, child, dac_idx); - if (err < 0) - return err; - else if (err > 0) { - /* found one, - * select the path, unmute both input and output - */ - if (node->nconns > 1) - select_input_connection(codec, node, i); - unmute_input(codec, node, i); - unmute_output(codec, node); - if (spec->dac_node[dac_idx] && - spec->pcm_vol_nodes < MAX_PCM_VOLS && - !(spec->dac_node[dac_idx]->wid_caps & - AC_WCAP_OUT_AMP)) { - if ((node->wid_caps & AC_WCAP_IN_AMP) || - (node->wid_caps & AC_WCAP_OUT_AMP)) { - int n = spec->pcm_vol_nodes; - spec->pcm_vol[n].node = node; - spec->pcm_vol[n].index = i; - spec->pcm_vol_nodes++; - } + for (i = 0; i < path->depth; i++) { + if (path->path[i] == nid) { + if (dir == HDA_OUTPUT || path->idx[i] == idx) + return true; + break; } - return 1; } } - return 0; + return false; } -/* - * Look for the output PIN widget with the given jack type - * and parse the output path to that PIN. - * - * Returns the PIN node when the path to DAC is established. - */ -static struct hda_gnode *parse_output_jack(struct hda_codec *codec, - struct hda_gspec *spec, - int jack_type) +/* get the default amp value for the target state */ +static int get_amp_val_to_activate(struct hda_codec *codec, hda_nid_t nid, + int dir, bool enable) { - struct hda_gnode *node; - int err; - - list_for_each_entry(node, &spec->nid_list, list) { - if (node->type != AC_WID_PIN) - continue; - /* output capable? */ - if (! (node->pin_caps & AC_PINCAP_OUT)) - continue; - if (defcfg_port_conn(node) == AC_JACK_PORT_NONE) - continue; /* unconnected */ - if (jack_type >= 0) { - if (jack_type != defcfg_type(node)) - continue; - if (node->wid_caps & AC_WCAP_DIGITAL) - continue; /* skip SPDIF */ - } else { - /* output as default? */ - if (! (node->pin_ctl & AC_PINCTL_OUT_EN)) - continue; - } - clear_check_flags(spec); - err = parse_output_path(codec, spec, node, 0); - if (err < 0) - return NULL; - if (! err && spec->out_pin_node[0]) { - err = parse_output_path(codec, spec, node, 1); - if (err < 0) - return NULL; - } - if (err > 0) { - /* unmute the PIN output */ - unmute_output(codec, node); - /* set PIN-Out enable */ - snd_hda_codec_write_cache(codec, node->nid, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, - AC_PINCTL_OUT_EN | - ((node->pin_caps & AC_PINCAP_HP_DRV) ? - AC_PINCTL_HP_EN : 0)); - return node; - } + unsigned int caps; + unsigned int val = 0; + + caps = query_amp_caps(codec, nid, dir); + if (caps & AC_AMPCAP_NUM_STEPS) { + /* set to 0dB */ + if (enable) + val = (caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT; } - return NULL; + if (caps & AC_AMPCAP_MUTE) { + if (!enable) + val |= HDA_AMP_MUTE; + } + return val; } +/* initialize the amp value (only at the first time) */ +static void init_amp(struct hda_codec *codec, hda_nid_t nid, int dir, int idx) +{ + int val = get_amp_val_to_activate(codec, nid, dir, false); + snd_hda_codec_amp_init_stereo(codec, nid, dir, idx, 0xff, val); +} -/* - * parse outputs - */ -static int parse_output(struct hda_codec *codec) +static void activate_amp(struct hda_codec *codec, hda_nid_t nid, int dir, + int idx, bool enable) +{ + int val; + if (is_ctl_associated(codec, nid, dir, idx) || + is_active_nid(codec, nid, dir, idx)) + return; + val = get_amp_val_to_activate(codec, nid, dir, enable); + snd_hda_codec_amp_stereo(codec, nid, dir, idx, 0xff, val); +} + +static void activate_amp_out(struct hda_codec *codec, struct nid_path *path, + int i, bool enable) +{ + hda_nid_t nid = path->path[i]; + init_amp(codec, nid, HDA_OUTPUT, 0); + activate_amp(codec, nid, HDA_OUTPUT, 0, enable); +} + +static void activate_amp_in(struct hda_codec *codec, struct nid_path *path, + int i, bool enable, bool add_aamix) { - struct hda_gspec *spec = codec->spec; - struct hda_gnode *node; + struct hda_gen_spec *spec = codec->spec; + hda_nid_t conn[16]; + int n, nums, idx; + int type; + hda_nid_t nid = path->path[i]; + + nums = snd_hda_get_connections(codec, nid, conn, ARRAY_SIZE(conn)); + type = get_wcaps_type(get_wcaps(codec, nid)); + if (type == AC_WID_PIN || + (type == AC_WID_AUD_IN && codec->single_adc_amp)) { + nums = 1; + idx = 0; + } else + idx = path->idx[i]; + + for (n = 0; n < nums; n++) + init_amp(codec, nid, HDA_INPUT, n); + + if (is_ctl_associated(codec, nid, HDA_INPUT, idx)) + return; - /* - * Look for the output PIN widget + /* here is a little bit tricky in comparison with activate_amp_out(); + * when aa-mixer is available, we need to enable the path as well */ - /* first, look for the line-out pin */ - node = parse_output_jack(codec, spec, AC_JACK_LINE_OUT); - if (node) /* found, remember the PIN node */ - spec->out_pin_node[0] = node; - else { - /* if no line-out is found, try speaker out */ - node = parse_output_jack(codec, spec, AC_JACK_SPEAKER); - if (node) - spec->out_pin_node[0] = node; - } - /* look for the HP-out pin */ - node = parse_output_jack(codec, spec, AC_JACK_HP_OUT); - if (node) { - if (! spec->out_pin_node[0]) - spec->out_pin_node[0] = node; - else - spec->out_pin_node[1] = node; + for (n = 0; n < nums; n++) { + if (n != idx && (!add_aamix || conn[n] != spec->mixer_nid)) + continue; + activate_amp(codec, nid, HDA_INPUT, n, enable); } +} - if (! spec->out_pin_node[0]) { - /* no line-out or HP pins found, - * then choose for the first output pin - */ - spec->out_pin_node[0] = parse_output_jack(codec, spec, -1); - if (! spec->out_pin_node[0]) - snd_printd("hda_generic: no proper output path found\n"); +/* activate or deactivate the given path + * if @add_aamix is set, enable the input from aa-mix NID as well (if any) + */ +void snd_hda_activate_path(struct hda_codec *codec, struct nid_path *path, + bool enable, bool add_aamix) +{ + int i; + + if (!enable) + path->active = false; + + for (i = path->depth - 1; i >= 0; i--) { + if (enable && path->multi[i]) + snd_hda_codec_write_cache(codec, path->path[i], 0, + AC_VERB_SET_CONNECT_SEL, + path->idx[i]); + if (has_amp_in(codec, path, i)) + activate_amp_in(codec, path, i, enable, add_aamix); + if (has_amp_out(codec, path, i)) + activate_amp_out(codec, path, i, enable); } - return 0; + if (enable) + path->active = true; } +EXPORT_SYMBOL_HDA(snd_hda_activate_path); + /* - * input MUX + * Helper functions for creating mixer ctl elements */ -/* control callbacks */ -static int capture_source_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct hda_gspec *spec = codec->spec; - return snd_hda_input_mux_info(&spec->input_mux, uinfo); -} +enum { + HDA_CTL_WIDGET_VOL, + HDA_CTL_WIDGET_MUTE, + HDA_CTL_BIND_MUTE, + HDA_CTL_BIND_VOL, + HDA_CTL_BIND_SW, +}; +static const struct snd_kcontrol_new control_templates[] = { + HDA_CODEC_VOLUME(NULL, 0, 0, 0), + HDA_CODEC_MUTE(NULL, 0, 0, 0), + HDA_BIND_MUTE(NULL, 0, 0, 0), + HDA_BIND_VOL(NULL, 0), + HDA_BIND_SW(NULL, 0), +}; -static int capture_source_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +/* add dynamic controls from template */ +static int add_control(struct hda_gen_spec *spec, int type, const char *name, + int cidx, unsigned long val) { - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct hda_gspec *spec = codec->spec; + struct snd_kcontrol_new *knew; - ucontrol->value.enumerated.item[0] = spec->cur_cap_src; + knew = add_kctl(spec, name, &control_templates[type]); + if (!knew) + return -ENOMEM; + knew->index = cidx; + if (get_amp_nid_(val)) + knew->subdevice = HDA_SUBDEV_AMP_FLAG; + knew->private_value = val; return 0; } -static int capture_source_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +static int add_control_with_pfx(struct hda_gen_spec *spec, int type, + const char *pfx, const char *dir, + const char *sfx, int cidx, unsigned long val) { - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct hda_gspec *spec = codec->spec; - return snd_hda_input_mux_put(codec, &spec->input_mux, ucontrol, - spec->adc_node->nid, &spec->cur_cap_src); + char name[32]; + snprintf(name, sizeof(name), "%s %s %s", pfx, dir, sfx); + return add_control(spec, type, name, cidx, val); } -/* - * return the string name of the given input PIN widget - */ -static const char *get_input_type(struct hda_gnode *node, unsigned int *pinctl) -{ - unsigned int location = defcfg_location(node); - switch (defcfg_type(node)) { - case AC_JACK_LINE_IN: - if ((location & 0x0f) == AC_JACK_LOC_FRONT) - return "Front Line"; - return "Line"; - case AC_JACK_CD: -#if 0 - if (pinctl) - *pinctl |= AC_PINCTL_VREF_GRD; -#endif - return "CD"; - case AC_JACK_AUX: - if ((location & 0x0f) == AC_JACK_LOC_FRONT) - return "Front Aux"; - return "Aux"; - case AC_JACK_MIC_IN: - if (pinctl && - (node->pin_caps & - (AC_PINCAP_VREF_80 << AC_PINCAP_VREF_SHIFT))) - *pinctl |= AC_PINCTL_VREF_80; - if ((location & 0x0f) == AC_JACK_LOC_FRONT) - return "Front Mic"; - return "Mic"; - case AC_JACK_SPDIF_IN: - return "SPDIF"; - case AC_JACK_DIG_OTHER_IN: - return "Digital"; +#define add_pb_vol_ctrl(spec, type, pfx, val) \ + add_control_with_pfx(spec, type, pfx, "Playback", "Volume", 0, val) +#define add_pb_sw_ctrl(spec, type, pfx, val) \ + add_control_with_pfx(spec, type, pfx, "Playback", "Switch", 0, val) +#define __add_pb_vol_ctrl(spec, type, pfx, cidx, val) \ + add_control_with_pfx(spec, type, pfx, "Playback", "Volume", cidx, val) +#define __add_pb_sw_ctrl(spec, type, pfx, cidx, val) \ + add_control_with_pfx(spec, type, pfx, "Playback", "Switch", cidx, val) + +static int add_vol_ctl(struct hda_codec *codec, const char *pfx, int cidx, + unsigned int chs, struct nid_path *path) +{ + unsigned int val; + if (!path) + return 0; + val = path->ctls[NID_PATH_VOL_CTL]; + if (!val) + return 0; + val = amp_val_replace_channels(val, chs); + return __add_pb_vol_ctrl(codec->spec, HDA_CTL_WIDGET_VOL, pfx, cidx, val); +} + +/* return the channel bits suitable for the given path->ctls[] */ +static int get_default_ch_nums(struct hda_codec *codec, struct nid_path *path, + int type) +{ + int chs = 1; /* mono (left only) */ + if (path) { + hda_nid_t nid = get_amp_nid_(path->ctls[type]); + if (nid && (get_wcaps(codec, nid) & AC_WCAP_STEREO)) + chs = 3; /* stereo */ } - return NULL; + return chs; } -/* - * parse the nodes recursively until reach to the input PIN - * - * returns 0 if not found, 1 if found, or a negative error code. +static int add_stereo_vol(struct hda_codec *codec, const char *pfx, int cidx, + struct nid_path *path) +{ + int chs = get_default_ch_nums(codec, path, NID_PATH_VOL_CTL); + return add_vol_ctl(codec, pfx, cidx, chs, path); +} + +/* create a mute-switch for the given mixer widget; + * if it has multiple sources (e.g. DAC and loopback), create a bind-mute */ -static int parse_adc_sub_nodes(struct hda_codec *codec, struct hda_gspec *spec, - struct hda_gnode *node, int idx) +static int add_sw_ctl(struct hda_codec *codec, const char *pfx, int cidx, + unsigned int chs, struct nid_path *path) { - int i, err; - unsigned int pinctl; - const char *type; + unsigned int val; + int type = HDA_CTL_WIDGET_MUTE; - if (node->checked) + if (!path) return 0; - - node->checked = 1; - if (node->type != AC_WID_PIN) { - for (i = 0; i < node->nconns; i++) { - struct hda_gnode *child; - child = hda_get_node(spec, node->conn_list[i]); - if (! child) - continue; - err = parse_adc_sub_nodes(codec, spec, child, idx); - if (err < 0) - return err; - if (err > 0) { - /* found one, - * select the path, unmute both input and output - */ - if (node->nconns > 1) - select_input_connection(codec, node, i); - unmute_input(codec, node, i); - unmute_output(codec, node); - return err; - } - } + val = path->ctls[NID_PATH_MUTE_CTL]; + if (!val) return 0; + val = amp_val_replace_channels(val, chs); + if (get_amp_direction_(val) == HDA_INPUT) { + hda_nid_t nid = get_amp_nid_(val); + int nums = snd_hda_get_num_conns(codec, nid); + if (nums > 1) { + type = HDA_CTL_BIND_MUTE; + val |= nums << 19; + } } + return __add_pb_sw_ctrl(codec->spec, type, pfx, cidx, val); +} - /* input capable? */ - if (! (node->pin_caps & AC_PINCAP_IN)) - return 0; +static int add_stereo_sw(struct hda_codec *codec, const char *pfx, + int cidx, struct nid_path *path) +{ + int chs = get_default_ch_nums(codec, path, NID_PATH_MUTE_CTL); + return add_sw_ctl(codec, pfx, cidx, chs, path); +} - if (defcfg_port_conn(node) == AC_JACK_PORT_NONE) - return 0; /* unconnected */ +static const char * const channel_name[4] = { + "Front", "Surround", "CLFE", "Side" +}; - if (node->wid_caps & AC_WCAP_DIGITAL) - return 0; /* skip SPDIF */ +/* give some appropriate ctl name prefix for the given line out channel */ +static const char *get_line_out_pfx(struct hda_gen_spec *spec, int ch, + bool can_be_master, int *index) +{ + struct auto_pin_cfg *cfg = &spec->autocfg; - if (spec->input_mux.num_items >= HDA_MAX_NUM_INPUTS) { - snd_printk(KERN_ERR "hda_generic: Too many items for capture\n"); - return -EINVAL; - } + *index = 0; + if (cfg->line_outs == 1 && !spec->multi_ios && + !cfg->hp_outs && !cfg->speaker_outs && can_be_master) + return spec->vmaster_mute.hook ? "PCM" : "Master"; - pinctl = AC_PINCTL_IN_EN; - /* create a proper capture source label */ - type = get_input_type(node, &pinctl); - if (! type) { - /* input as default? */ - if (! (node->pin_ctl & AC_PINCTL_IN_EN)) - return 0; - type = "Input"; + /* if there is really a single DAC used in the whole output paths, + * use it master (or "PCM" if a vmaster hook is present) + */ + if (spec->multiout.num_dacs == 1 && !spec->mixer_nid && + !spec->multiout.hp_out_nid[0] && !spec->multiout.extra_out_nid[0]) + return spec->vmaster_mute.hook ? "PCM" : "Master"; + + switch (cfg->line_out_type) { + case AUTO_PIN_SPEAKER_OUT: + if (cfg->line_outs == 1) + return "Speaker"; + if (cfg->line_outs == 2) + return ch ? "Bass Speaker" : "Speaker"; + break; + case AUTO_PIN_HP_OUT: + /* for multi-io case, only the primary out */ + if (ch && spec->multi_ios) + break; + *index = ch; + return "Headphone"; + default: + if (cfg->line_outs == 1 && !spec->multi_ios) + return "PCM"; + break; + } + if (ch >= ARRAY_SIZE(channel_name)) { + snd_BUG(); + return "PCM"; } - snd_hda_add_imux_item(&spec->input_mux, type, idx, NULL); - - /* unmute the PIN external input */ - unmute_input(codec, node, 0); /* index = 0? */ - /* set PIN-In enable */ - snd_hda_codec_write_cache(codec, node->nid, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, pinctl); - return 1; /* found */ + return channel_name[ch]; } /* - * parse input + * Parse output paths */ -static int parse_input_path(struct hda_codec *codec, struct hda_gnode *adc_node) + +/* badness definition */ +enum { + /* No primary DAC is found for the main output */ + BAD_NO_PRIMARY_DAC = 0x10000, + /* No DAC is found for the extra output */ + BAD_NO_DAC = 0x4000, + /* No possible multi-ios */ + BAD_MULTI_IO = 0x103, + /* No individual DAC for extra output */ + BAD_NO_EXTRA_DAC = 0x102, + /* No individual DAC for extra surrounds */ + BAD_NO_EXTRA_SURR_DAC = 0x101, + /* Primary DAC shared with main surrounds */ + BAD_SHARED_SURROUND = 0x100, + /* Primary DAC shared with main CLFE */ + BAD_SHARED_CLFE = 0x10, + /* Primary DAC shared with extra surrounds */ + BAD_SHARED_EXTRA_SURROUND = 0x10, + /* Volume widget is shared */ + BAD_SHARED_VOL = 0x10, +}; + +/* look for widgets in the path between the given NIDs appropriate for + * volume and mute controls, and assign the values to ctls[]. + * + * When no appropriate widget is found in the path, the badness value + * is incremented depending on the situation. The function returns the + * total badness for both volume and mute controls. + */ +static int assign_out_path_ctls(struct hda_codec *codec, hda_nid_t pin, + hda_nid_t dac) { - struct hda_gspec *spec = codec->spec; - struct hda_gnode *node; - int i, err; + struct nid_path *path = snd_hda_get_nid_path(codec, dac, pin); + hda_nid_t nid; + unsigned int val; + int badness = 0; + + if (!path) + return BAD_SHARED_VOL * 2; + nid = look_for_out_vol_nid(codec, path); + if (nid) { + val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT); + if (is_ctl_used(codec, val, NID_PATH_VOL_CTL)) + badness += BAD_SHARED_VOL; + else + path->ctls[NID_PATH_VOL_CTL] = val; + } else + badness += BAD_SHARED_VOL; + nid = look_for_out_mute_nid(codec, path); + if (nid) { + unsigned int wid_type = get_wcaps_type(get_wcaps(codec, nid)); + if (wid_type == AC_WID_PIN || wid_type == AC_WID_AUD_OUT || + nid_has_mute(codec, nid, HDA_OUTPUT)) + val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT); + else + val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT); + if (is_ctl_used(codec, val, NID_PATH_MUTE_CTL)) + badness += BAD_SHARED_VOL; + else + path->ctls[NID_PATH_MUTE_CTL] = val; + } else + badness += BAD_SHARED_VOL; + return badness; +} - snd_printdd("AUD_IN = %x\n", adc_node->nid); - clear_check_flags(spec); +struct badness_table { + int no_primary_dac; /* no primary DAC */ + int no_dac; /* no secondary DACs */ + int shared_primary; /* primary DAC is shared with main output */ + int shared_surr; /* secondary DAC shared with main or primary */ + int shared_clfe; /* third DAC shared with main or primary */ + int shared_surr_main; /* secondary DAC sahred with main/DAC0 */ +}; - // awk added - fixed no recording due to muted widget - unmute_input(codec, adc_node, 0); - - /* - * check each connection of the ADC - * if it reaches to a proper input PIN, add the path as the - * input path. - */ - /* first, check the direct connections to PIN widgets */ - for (i = 0; i < adc_node->nconns; i++) { - node = hda_get_node(spec, adc_node->conn_list[i]); - if (node && node->type == AC_WID_PIN) { - err = parse_adc_sub_nodes(codec, spec, node, i); - if (err < 0) - return err; +static struct badness_table main_out_badness = { + .no_primary_dac = BAD_NO_PRIMARY_DAC, + .no_dac = BAD_NO_DAC, + .shared_primary = BAD_NO_PRIMARY_DAC, + .shared_surr = BAD_SHARED_SURROUND, + .shared_clfe = BAD_SHARED_CLFE, + .shared_surr_main = BAD_SHARED_SURROUND, +}; + +static struct badness_table extra_out_badness = { + .no_primary_dac = BAD_NO_DAC, + .no_dac = BAD_NO_DAC, + .shared_primary = BAD_NO_EXTRA_DAC, + .shared_surr = BAD_SHARED_EXTRA_SURROUND, + .shared_clfe = BAD_SHARED_EXTRA_SURROUND, + .shared_surr_main = BAD_NO_EXTRA_SURR_DAC, +}; + +/* try to assign DACs to pins and return the resultant badness */ +static int try_assign_dacs(struct hda_codec *codec, int num_outs, + const hda_nid_t *pins, hda_nid_t *dacs, + const struct badness_table *bad) +{ + struct hda_gen_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + int i, j; + int badness = 0; + hda_nid_t dac; + + if (!num_outs) + return 0; + + for (i = 0; i < num_outs; i++) { + hda_nid_t pin = pins[i]; + if (!dacs[i]) + dacs[i] = look_for_dac(codec, pin, false); + if (!dacs[i] && !i) { + for (j = 1; j < num_outs; j++) { + if (is_reachable_path(codec, dacs[j], pin)) { + dacs[0] = dacs[j]; + dacs[j] = 0; + break; + } + } } - } - /* ... then check the rests, more complicated connections */ - for (i = 0; i < adc_node->nconns; i++) { - node = hda_get_node(spec, adc_node->conn_list[i]); - if (node && node->type != AC_WID_PIN) { - err = parse_adc_sub_nodes(codec, spec, node, i); - if (err < 0) - return err; + dac = dacs[i]; + if (!dac) { + if (is_reachable_path(codec, dacs[0], pin)) + dac = dacs[0]; + else if (cfg->line_outs > i && + is_reachable_path(codec, spec->private_dac_nids[i], pin)) + dac = spec->private_dac_nids[i]; + if (dac) { + if (!i) + badness += bad->shared_primary; + else if (i == 1) + badness += bad->shared_surr; + else + badness += bad->shared_clfe; + } else if (is_reachable_path(codec, spec->private_dac_nids[0], pin)) { + dac = spec->private_dac_nids[0]; + badness += bad->shared_surr_main; + } else if (!i) + badness += bad->no_primary_dac; + else + badness += bad->no_dac; } + if (!snd_hda_add_new_path(codec, dac, pin, 0)) + dac = dacs[i] = 0; + if (dac) + badness += assign_out_path_ctls(codec, pin, dac); } - if (! spec->input_mux.num_items) - return 0; /* no input path found... */ - - snd_printdd("[Capture Source] NID=0x%x, #SRC=%d\n", adc_node->nid, spec->input_mux.num_items); - for (i = 0; i < spec->input_mux.num_items; i++) - snd_printdd(" [%s] IDX=0x%x\n", spec->input_mux.items[i].label, - spec->input_mux.items[i].index); - - spec->adc_node = adc_node; - return 1; + return badness; } -/* - * parse input - */ -static int parse_input(struct hda_codec *codec) +/* return NID if the given pin has only a single connection to a certain DAC */ +static hda_nid_t get_dac_if_single(struct hda_codec *codec, hda_nid_t pin) { - struct hda_gspec *spec = codec->spec; - struct hda_gnode *node; - int err; + struct hda_gen_spec *spec = codec->spec; + int i; + hda_nid_t nid_found = 0; - /* - * At first we look for an audio input widget. - * If it reaches to certain input PINs, we take it as the - * input path. - */ - list_for_each_entry(node, &spec->nid_list, list) { - if (node->wid_caps & AC_WCAP_DIGITAL) - continue; /* skip SPDIF */ - if (node->type == AC_WID_AUD_IN) { - err = parse_input_path(codec, node); - if (err < 0) - return err; - else if (err > 0) + for (i = 0; i < spec->num_all_dacs; i++) { + hda_nid_t nid = spec->all_dacs[i]; + if (!nid || is_dac_already_used(codec, nid)) + continue; + if (is_reachable_path(codec, nid, pin)) { + if (nid_found) return 0; + nid_found = nid; } } - snd_printd("hda_generic: no proper input path found\n"); - return 0; + return nid_found; } -#ifdef CONFIG_PM -static void add_input_loopback(struct hda_codec *codec, hda_nid_t nid, - int dir, int idx) +/* check whether the given pin can be a multi-io pin */ +static bool can_be_multiio_pin(struct hda_codec *codec, + unsigned int location, hda_nid_t nid) { - struct hda_gspec *spec = codec->spec; - struct hda_amp_list *p; - - if (spec->num_loopbacks >= MAX_LOOPBACK_AMPS) { - snd_printk(KERN_ERR "hda_generic: Too many loopback ctls\n"); - return; - } - p = &spec->loopback_list[spec->num_loopbacks++]; - p->nid = nid; - p->dir = dir; - p->idx = idx; - spec->loopback.amplist = spec->loopback_list; + unsigned int defcfg, caps; + + defcfg = snd_hda_codec_get_pincfg(codec, nid); + if (get_defcfg_connect(defcfg) != AC_JACK_PORT_COMPLEX) + return false; + if (location && get_defcfg_location(defcfg) != location) + return false; + caps = snd_hda_query_pin_caps(codec, nid); + if (!(caps & AC_PINCAP_OUT)) + return false; + return true; } -#else -#define add_input_loopback(codec,nid,dir,idx) -#endif /* - * create mixer controls if possible + * multi-io helper + * + * When hardwired is set, try to fill ony hardwired pins, and returns + * zero if any pins are filled, non-zero if nothing found. + * When hardwired is off, try to fill possible input pins, and returns + * the badness value. */ -static int create_mixer(struct hda_codec *codec, struct hda_gnode *node, - unsigned int index, const char *type, - const char *dir_sfx, int is_loopback) +static int fill_multi_ios(struct hda_codec *codec, + hda_nid_t reference_pin, + bool hardwired, int offset) { - char name[32]; - int err; - int created = 0; - struct snd_kcontrol_new knew; - - if (type) - sprintf(name, "%s %s Switch", type, dir_sfx); - else - sprintf(name, "%s Switch", dir_sfx); - if ((node->wid_caps & AC_WCAP_IN_AMP) && - (node->amp_in_caps & AC_AMPCAP_MUTE)) { - knew = (struct snd_kcontrol_new)HDA_CODEC_MUTE(name, node->nid, index, HDA_INPUT); - if (is_loopback) - add_input_loopback(codec, node->nid, HDA_INPUT, index); - snd_printdd("[%s] NID=0x%x, DIR=IN, IDX=0x%x\n", name, node->nid, index); - err = snd_hda_ctl_add(codec, node->nid, - snd_ctl_new1(&knew, codec)); - if (err < 0) - return err; - created = 1; - } else if ((node->wid_caps & AC_WCAP_OUT_AMP) && - (node->amp_out_caps & AC_AMPCAP_MUTE)) { - knew = (struct snd_kcontrol_new)HDA_CODEC_MUTE(name, node->nid, 0, HDA_OUTPUT); - if (is_loopback) - add_input_loopback(codec, node->nid, HDA_OUTPUT, 0); - snd_printdd("[%s] NID=0x%x, DIR=OUT\n", name, node->nid); - err = snd_hda_ctl_add(codec, node->nid, - snd_ctl_new1(&knew, codec)); - if (err < 0) - return err; - created = 1; + struct hda_gen_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + int type, i, j, dacs, num_pins, old_pins; + unsigned int defcfg = snd_hda_codec_get_pincfg(codec, reference_pin); + unsigned int location = get_defcfg_location(defcfg); + int badness = 0; + + old_pins = spec->multi_ios; + if (old_pins >= 2) + goto end_fill; + + num_pins = 0; + for (type = AUTO_PIN_LINE_IN; type >= AUTO_PIN_MIC; type--) { + for (i = 0; i < cfg->num_inputs; i++) { + if (cfg->inputs[i].type != type) + continue; + if (can_be_multiio_pin(codec, location, + cfg->inputs[i].pin)) + num_pins++; + } } + if (num_pins < 2) + goto end_fill; - if (type) - sprintf(name, "%s %s Volume", type, dir_sfx); - else - sprintf(name, "%s Volume", dir_sfx); - if ((node->wid_caps & AC_WCAP_IN_AMP) && - (node->amp_in_caps & AC_AMPCAP_NUM_STEPS)) { - knew = (struct snd_kcontrol_new)HDA_CODEC_VOLUME(name, node->nid, index, HDA_INPUT); - snd_printdd("[%s] NID=0x%x, DIR=IN, IDX=0x%x\n", name, node->nid, index); - err = snd_hda_ctl_add(codec, node->nid, - snd_ctl_new1(&knew, codec)); - if (err < 0) - return err; - created = 1; - } else if ((node->wid_caps & AC_WCAP_OUT_AMP) && - (node->amp_out_caps & AC_AMPCAP_NUM_STEPS)) { - knew = (struct snd_kcontrol_new)HDA_CODEC_VOLUME(name, node->nid, 0, HDA_OUTPUT); - snd_printdd("[%s] NID=0x%x, DIR=OUT\n", name, node->nid); - err = snd_hda_ctl_add(codec, node->nid, - snd_ctl_new1(&knew, codec)); - if (err < 0) - return err; - created = 1; - } + dacs = spec->multiout.num_dacs; + for (type = AUTO_PIN_LINE_IN; type >= AUTO_PIN_MIC; type--) { + for (i = 0; i < cfg->num_inputs; i++) { + hda_nid_t nid = cfg->inputs[i].pin; + hda_nid_t dac = 0; - return created; -} + if (cfg->inputs[i].type != type) + continue; + if (!can_be_multiio_pin(codec, location, nid)) + continue; + for (j = 0; j < spec->multi_ios; j++) { + if (nid == spec->multi_io[j].pin) + break; + } + if (j < spec->multi_ios) + continue; + + if (offset && offset + spec->multi_ios < dacs) { + dac = spec->private_dac_nids[offset + spec->multi_ios]; + if (!is_reachable_path(codec, dac, nid)) + dac = 0; + } + if (hardwired) + dac = get_dac_if_single(codec, nid); + else if (!dac) + dac = look_for_dac(codec, nid, false); + if (!dac) { + badness++; + continue; + } + if (!snd_hda_add_new_path(codec, dac, nid, 0)) { + badness++; + continue; + } + spec->multi_io[spec->multi_ios].pin = nid; + spec->multi_io[spec->multi_ios].dac = dac; + spec->multi_ios++; + if (spec->multi_ios >= 2) + break; + } + } + end_fill: + if (badness) + badness = BAD_MULTI_IO; + if (old_pins == spec->multi_ios) { + if (hardwired) + return 1; /* nothing found */ + else + return badness; /* no badness if nothing found */ + } + if (!hardwired && spec->multi_ios < 2) { + /* cancel newly assigned paths */ + spec->paths.used -= spec->multi_ios - old_pins; + spec->multi_ios = old_pins; + return badness; + } + + /* assign volume and mute controls */ + for (i = old_pins; i < spec->multi_ios; i++) + badness += assign_out_path_ctls(codec, spec->multi_io[i].pin, + spec->multi_io[i].dac); + + return badness; +} + +/* map DACs for all pins in the list if they are single connections */ +static bool map_singles(struct hda_codec *codec, int outs, + const hda_nid_t *pins, hda_nid_t *dacs) +{ + int i; + bool found = false; + for (i = 0; i < outs; i++) { + hda_nid_t dac; + if (dacs[i]) + continue; + dac = get_dac_if_single(codec, pins[i]); + if (!dac) + continue; + if (snd_hda_add_new_path(codec, dac, pins[i], 0)) { + dacs[i] = dac; + found = true; + } + } + return found; +} + +/* fill in the dac_nids table from the parsed pin configuration */ +static int fill_and_eval_dacs(struct hda_codec *codec, + bool fill_hardwired, + bool fill_mio_first) +{ + struct hda_gen_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + int i, err, badness; + + /* set num_dacs once to full for look_for_dac() */ + spec->multiout.num_dacs = cfg->line_outs; + spec->multiout.dac_nids = spec->private_dac_nids; + memset(spec->private_dac_nids, 0, sizeof(spec->private_dac_nids)); + memset(spec->multiout.hp_out_nid, 0, sizeof(spec->multiout.hp_out_nid)); + memset(spec->multiout.extra_out_nid, 0, sizeof(spec->multiout.extra_out_nid)); + spec->multi_ios = 0; + snd_array_free(&spec->paths); + badness = 0; + + /* fill hard-wired DACs first */ + if (fill_hardwired) { + bool mapped; + do { + mapped = map_singles(codec, cfg->line_outs, + cfg->line_out_pins, + spec->private_dac_nids); + mapped |= map_singles(codec, cfg->hp_outs, + cfg->hp_pins, + spec->multiout.hp_out_nid); + mapped |= map_singles(codec, cfg->speaker_outs, + cfg->speaker_pins, + spec->multiout.extra_out_nid); + if (fill_mio_first && cfg->line_outs == 1 && + cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) { + err = fill_multi_ios(codec, cfg->line_out_pins[0], true, 0); + if (!err) + mapped = true; + } + } while (mapped); + } + + badness += try_assign_dacs(codec, cfg->line_outs, cfg->line_out_pins, + spec->private_dac_nids, + &main_out_badness); + + /* re-count num_dacs and squash invalid entries */ + spec->multiout.num_dacs = 0; + for (i = 0; i < cfg->line_outs; i++) { + if (spec->private_dac_nids[i]) + spec->multiout.num_dacs++; + else { + memmove(spec->private_dac_nids + i, + spec->private_dac_nids + i + 1, + sizeof(hda_nid_t) * (cfg->line_outs - i - 1)); + spec->private_dac_nids[cfg->line_outs - 1] = 0; + } + } + + if (fill_mio_first && + cfg->line_outs == 1 && cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) { + /* try to fill multi-io first */ + err = fill_multi_ios(codec, cfg->line_out_pins[0], false, 0); + if (err < 0) + return err; + /* we don't count badness at this stage yet */ + } + + if (cfg->line_out_type != AUTO_PIN_HP_OUT) { + err = try_assign_dacs(codec, cfg->hp_outs, cfg->hp_pins, + spec->multiout.hp_out_nid, + &extra_out_badness); + if (err < 0) + return err; + badness += err; + } + if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) { + err = try_assign_dacs(codec, cfg->speaker_outs, + cfg->speaker_pins, + spec->multiout.extra_out_nid, + &extra_out_badness); + if (err < 0) + return err; + badness += err; + } + if (cfg->line_outs == 1 && cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) { + err = fill_multi_ios(codec, cfg->line_out_pins[0], false, 0); + if (err < 0) + return err; + badness += err; + } + if (cfg->hp_outs && cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) { + /* try multi-ios with HP + inputs */ + int offset = 0; + if (cfg->line_outs >= 3) + offset = 1; + err = fill_multi_ios(codec, cfg->hp_pins[0], false, offset); + if (err < 0) + return err; + badness += err; + } + + if (spec->multi_ios == 2) { + for (i = 0; i < 2; i++) + spec->private_dac_nids[spec->multiout.num_dacs++] = + spec->multi_io[i].dac; + spec->ext_channel_count = 2; + } else if (spec->multi_ios) { + spec->multi_ios = 0; + badness += BAD_MULTI_IO; + } + + return badness; +} + +#define DEBUG_BADNESS + +#ifdef DEBUG_BADNESS +#define debug_badness snd_printdd +#else +#define debug_badness(...) +#endif + +static void debug_show_configs(struct hda_gen_spec *spec, struct auto_pin_cfg *cfg) +{ + debug_badness("multi_outs = %x/%x/%x/%x : %x/%x/%x/%x\n", + cfg->line_out_pins[0], cfg->line_out_pins[1], + cfg->line_out_pins[2], cfg->line_out_pins[2], + spec->multiout.dac_nids[0], + spec->multiout.dac_nids[1], + spec->multiout.dac_nids[2], + spec->multiout.dac_nids[3]); + if (spec->multi_ios > 0) + debug_badness("multi_ios(%d) = %x/%x : %x/%x\n", + spec->multi_ios, + spec->multi_io[0].pin, spec->multi_io[1].pin, + spec->multi_io[0].dac, spec->multi_io[1].dac); + debug_badness("hp_outs = %x/%x/%x/%x : %x/%x/%x/%x\n", + cfg->hp_pins[0], cfg->hp_pins[1], + cfg->hp_pins[2], cfg->hp_pins[2], + spec->multiout.hp_out_nid[0], + spec->multiout.hp_out_nid[1], + spec->multiout.hp_out_nid[2], + spec->multiout.hp_out_nid[3]); + debug_badness("spk_outs = %x/%x/%x/%x : %x/%x/%x/%x\n", + cfg->speaker_pins[0], cfg->speaker_pins[1], + cfg->speaker_pins[2], cfg->speaker_pins[3], + spec->multiout.extra_out_nid[0], + spec->multiout.extra_out_nid[1], + spec->multiout.extra_out_nid[2], + spec->multiout.extra_out_nid[3]); +} + +/* find all available DACs of the codec */ +static void fill_all_dac_nids(struct hda_codec *codec) +{ + struct hda_gen_spec *spec = codec->spec; + int i; + hda_nid_t nid = codec->start_nid; + + spec->num_all_dacs = 0; + memset(spec->all_dacs, 0, sizeof(spec->all_dacs)); + for (i = 0; i < codec->num_nodes; i++, nid++) { + if (get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_AUD_OUT) + continue; + if (spec->num_all_dacs >= ARRAY_SIZE(spec->all_dacs)) { + snd_printk(KERN_ERR "hda: Too many DACs!\n"); + break; + } + spec->all_dacs[spec->num_all_dacs++] = nid; + } +} + +static int parse_output_paths(struct hda_codec *codec) +{ + struct hda_gen_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + struct auto_pin_cfg *best_cfg; + int best_badness = INT_MAX; + int badness; + bool fill_hardwired = true, fill_mio_first = true; + bool best_wired = true, best_mio = true; + bool hp_spk_swapped = false; + + fill_all_dac_nids(codec); + + best_cfg = kmalloc(sizeof(*best_cfg), GFP_KERNEL); + if (!best_cfg) + return -ENOMEM; + *best_cfg = *cfg; + + for (;;) { + badness = fill_and_eval_dacs(codec, fill_hardwired, + fill_mio_first); + if (badness < 0) { + kfree(best_cfg); + return badness; + } + debug_badness("==> lo_type=%d, wired=%d, mio=%d, badness=0x%x\n", + cfg->line_out_type, fill_hardwired, fill_mio_first, + badness); + debug_show_configs(spec, cfg); + if (badness < best_badness) { + best_badness = badness; + *best_cfg = *cfg; + best_wired = fill_hardwired; + best_mio = fill_mio_first; + } + if (!badness) + break; + fill_mio_first = !fill_mio_first; + if (!fill_mio_first) + continue; + fill_hardwired = !fill_hardwired; + if (!fill_hardwired) + continue; + if (hp_spk_swapped) + break; + hp_spk_swapped = true; + if (cfg->speaker_outs > 0 && + cfg->line_out_type == AUTO_PIN_HP_OUT) { + cfg->hp_outs = cfg->line_outs; + memcpy(cfg->hp_pins, cfg->line_out_pins, + sizeof(cfg->hp_pins)); + cfg->line_outs = cfg->speaker_outs; + memcpy(cfg->line_out_pins, cfg->speaker_pins, + sizeof(cfg->speaker_pins)); + cfg->speaker_outs = 0; + memset(cfg->speaker_pins, 0, sizeof(cfg->speaker_pins)); + cfg->line_out_type = AUTO_PIN_SPEAKER_OUT; + fill_hardwired = true; + continue; + } + if (cfg->hp_outs > 0 && + cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) { + cfg->speaker_outs = cfg->line_outs; + memcpy(cfg->speaker_pins, cfg->line_out_pins, + sizeof(cfg->speaker_pins)); + cfg->line_outs = cfg->hp_outs; + memcpy(cfg->line_out_pins, cfg->hp_pins, + sizeof(cfg->hp_pins)); + cfg->hp_outs = 0; + memset(cfg->hp_pins, 0, sizeof(cfg->hp_pins)); + cfg->line_out_type = AUTO_PIN_HP_OUT; + fill_hardwired = true; + continue; + } + break; + } + + if (badness) { + *cfg = *best_cfg; + fill_and_eval_dacs(codec, best_wired, best_mio); + } + debug_badness("==> Best config: lo_type=%d, wired=%d, mio=%d\n", + cfg->line_out_type, best_wired, best_mio); + debug_show_configs(spec, cfg); + + if (cfg->line_out_pins[0]) { + struct nid_path *path; + path = snd_hda_get_nid_path(codec, + spec->multiout.dac_nids[0], + cfg->line_out_pins[0]); + if (path) + spec->vmaster_nid = look_for_out_vol_nid(codec, path); + } + + kfree(best_cfg); + return 0; +} + +/* add playback controls from the parsed DAC table */ +static int create_multi_out_ctls(struct hda_codec *codec, + const struct auto_pin_cfg *cfg) +{ + struct hda_gen_spec *spec = codec->spec; + int i, err, noutputs; + + noutputs = cfg->line_outs; + if (spec->multi_ios > 0 && cfg->line_outs < 3) + noutputs += spec->multi_ios; + + for (i = 0; i < noutputs; i++) { + const char *name; + int index; + hda_nid_t dac, pin; + struct nid_path *path; + + dac = spec->multiout.dac_nids[i]; + if (!dac) + continue; + if (i >= cfg->line_outs) { + pin = spec->multi_io[i - 1].pin; + index = 0; + name = channel_name[i]; + } else { + pin = cfg->line_out_pins[i]; + name = get_line_out_pfx(spec, i, true, &index); + } + + path = snd_hda_get_nid_path(codec, dac, pin); + if (!path) + continue; + if (!name || !strcmp(name, "CLFE")) { + /* Center/LFE */ + err = add_vol_ctl(codec, "Center", 0, 1, path); + if (err < 0) + return err; + err = add_vol_ctl(codec, "LFE", 0, 2, path); + if (err < 0) + return err; + err = add_sw_ctl(codec, "Center", 0, 1, path); + if (err < 0) + return err; + err = add_sw_ctl(codec, "LFE", 0, 2, path); + if (err < 0) + return err; + } else { + err = add_stereo_vol(codec, name, index, path); + if (err < 0) + return err; + err = add_stereo_sw(codec, name, index, path); + if (err < 0) + return err; + } + } + return 0; +} + +static int create_extra_out(struct hda_codec *codec, hda_nid_t pin, + hda_nid_t dac, const char *pfx, int cidx) +{ + struct nid_path *path; + int err; + + path = snd_hda_get_nid_path(codec, dac, pin); + if (!path) + return 0; + /* bind volume control will be created in the case of dac = 0 */ + if (dac) { + err = add_stereo_vol(codec, pfx, cidx, path); + if (err < 0) + return err; + } + err = add_stereo_sw(codec, pfx, cidx, path); + if (err < 0) + return err; + return 0; +} + +/* add playback controls for speaker and HP outputs */ +static int create_extra_outs(struct hda_codec *codec, int num_pins, + const hda_nid_t *pins, const hda_nid_t *dacs, + const char *pfx) +{ + struct hda_gen_spec *spec = codec->spec; + struct hda_bind_ctls *ctl; + char name[32]; + int i, n, err; + + if (!num_pins || !pins[0]) + return 0; + + if (num_pins == 1) { + hda_nid_t dac = *dacs; + if (!dac) + dac = spec->multiout.dac_nids[0]; + return create_extra_out(codec, *pins, dac, pfx, 0); + } + + for (i = 0; i < num_pins; i++) { + hda_nid_t dac; + if (dacs[num_pins - 1]) + dac = dacs[i]; /* with individual volumes */ + else + dac = 0; + if (num_pins == 2 && i == 1 && !strcmp(pfx, "Speaker")) { + err = create_extra_out(codec, pins[i], dac, + "Bass Speaker", 0); + } else if (num_pins >= 3) { + snprintf(name, sizeof(name), "%s %s", + pfx, channel_name[i]); + err = create_extra_out(codec, pins[i], dac, name, 0); + } else { + err = create_extra_out(codec, pins[i], dac, pfx, i); + } + if (err < 0) + return err; + } + if (dacs[num_pins - 1]) + return 0; + + /* Let's create a bind-controls for volumes */ + ctl = new_bind_ctl(codec, num_pins, &snd_hda_bind_vol); + if (!ctl) + return -ENOMEM; + n = 0; + for (i = 0; i < num_pins; i++) { + hda_nid_t vol; + struct nid_path *path; + if (!pins[i] || !dacs[i]) + continue; + path = snd_hda_get_nid_path(codec, dacs[i], pins[i]); + if (!path) + continue; + vol = look_for_out_vol_nid(codec, path); + if (vol) + ctl->values[n++] = + HDA_COMPOSE_AMP_VAL(vol, 3, 0, HDA_OUTPUT); + } + if (n) { + snprintf(name, sizeof(name), "%s Playback Volume", pfx); + err = add_control(spec, HDA_CTL_BIND_VOL, name, 0, (long)ctl); + if (err < 0) + return err; + } + return 0; +} + +static int create_hp_out_ctls(struct hda_codec *codec) +{ + struct hda_gen_spec *spec = codec->spec; + return create_extra_outs(codec, spec->autocfg.hp_outs, + spec->autocfg.hp_pins, + spec->multiout.hp_out_nid, + "Headphone"); +} + +static int create_speaker_out_ctls(struct hda_codec *codec) +{ + struct hda_gen_spec *spec = codec->spec; + return create_extra_outs(codec, spec->autocfg.speaker_outs, + spec->autocfg.speaker_pins, + spec->multiout.extra_out_nid, + "Speaker"); +} + +/* + * channel mode enum control + */ + +static int ch_mode_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct hda_gen_spec *spec = codec->spec; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = spec->multi_ios + 1; + if (uinfo->value.enumerated.item > spec->multi_ios) + uinfo->value.enumerated.item = spec->multi_ios; + sprintf(uinfo->value.enumerated.name, "%dch", + (uinfo->value.enumerated.item + 1) * 2); + return 0; +} + +static int ch_mode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct hda_gen_spec *spec = codec->spec; + ucontrol->value.enumerated.item[0] = (spec->ext_channel_count - 1) / 2; + return 0; +} + +static int set_multi_io(struct hda_codec *codec, int idx, bool output) +{ + struct hda_gen_spec *spec = codec->spec; + hda_nid_t nid = spec->multi_io[idx].pin; + struct nid_path *path; + + path = snd_hda_get_nid_path(codec, spec->multi_io[idx].dac, nid); + if (!path) + return -EINVAL; + + if (path->active == output) + return 0; + + if (output) { + snd_hda_set_pin_ctl_cache(codec, nid, PIN_OUT); + snd_hda_activate_path(codec, path, true, true); + } else { + snd_hda_activate_path(codec, path, false, true); + snd_hda_set_pin_ctl_cache(codec, nid, + spec->multi_io[idx].ctl_in); + } + return 0; +} + +static int ch_mode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct hda_gen_spec *spec = codec->spec; + int i, ch; + + ch = ucontrol->value.enumerated.item[0]; + if (ch < 0 || ch > spec->multi_ios) + return -EINVAL; + if (ch == (spec->ext_channel_count - 1) / 2) + return 0; + spec->ext_channel_count = (ch + 1) * 2; + for (i = 0; i < spec->multi_ios; i++) + set_multi_io(codec, i, i < ch); + spec->multiout.max_channels = max(spec->ext_channel_count, + spec->const_channel_count); + if (spec->need_dac_fix) + spec->multiout.num_dacs = spec->multiout.max_channels / 2; + return 1; +} + +static const struct snd_kcontrol_new channel_mode_enum = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Channel Mode", + .info = ch_mode_info, + .get = ch_mode_get, + .put = ch_mode_put, +}; + +static int create_multi_channel_mode(struct hda_codec *codec) +{ + struct hda_gen_spec *spec = codec->spec; + + if (spec->multi_ios > 0) { + if (!add_kctl(spec, NULL, &channel_mode_enum)) + return -ENOMEM; + } + return 0; +} + +/* + * shared headphone/mic handling + */ + +static void call_update_outputs(struct hda_codec *codec); + +/* for shared I/O, change the pin-control accordingly */ +static void update_shared_mic_hp(struct hda_codec *codec, bool set_as_mic) +{ + struct hda_gen_spec *spec = codec->spec; + unsigned int val; + hda_nid_t pin = spec->autocfg.inputs[1].pin; + /* NOTE: this assumes that there are only two inputs, the + * first is the real internal mic and the second is HP/mic jack. + */ + + val = snd_hda_get_default_vref(codec, pin); + + /* This pin does not have vref caps - let's enable vref on pin 0x18 + instead, as suggested by Realtek */ + if (val == AC_PINCTL_VREF_HIZ && spec->shared_mic_vref_pin) { + const hda_nid_t vref_pin = spec->shared_mic_vref_pin; + unsigned int vref_val = snd_hda_get_default_vref(codec, vref_pin); + if (vref_val != AC_PINCTL_VREF_HIZ) + snd_hda_set_pin_ctl(codec, vref_pin, PIN_IN | (set_as_mic ? vref_val : 0)); + } + + val = set_as_mic ? val | PIN_IN : PIN_HP; + snd_hda_set_pin_ctl(codec, pin, val); + + spec->automute_speaker = !set_as_mic; + call_update_outputs(codec); +} + +/* create a shared input with the headphone out */ +static int create_shared_input(struct hda_codec *codec) +{ + struct hda_gen_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + unsigned int defcfg; + hda_nid_t nid; + + /* only one internal input pin? */ + if (cfg->num_inputs != 1) + return 0; + defcfg = snd_hda_codec_get_pincfg(codec, cfg->inputs[0].pin); + if (snd_hda_get_input_pin_attr(defcfg) != INPUT_PIN_ATTR_INT) + return 0; + + if (cfg->hp_outs == 1 && cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) + nid = cfg->hp_pins[0]; /* OK, we have a single HP-out */ + else if (cfg->line_outs == 1 && cfg->line_out_type == AUTO_PIN_HP_OUT) + nid = cfg->line_out_pins[0]; /* OK, we have a single line-out */ + else + return 0; /* both not available */ + + if (!(snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_IN)) + return 0; /* no input */ + + cfg->inputs[1].pin = nid; + cfg->inputs[1].type = AUTO_PIN_MIC; + cfg->num_inputs = 2; + spec->shared_mic_hp = 1; + snd_printdd("hda-codec: Enable shared I/O jack on NID 0x%x\n", nid); + return 0; +} + + +/* + * Parse input paths + */ + +#ifdef CONFIG_PM +/* add the powersave loopback-list entry */ +static void add_loopback_list(struct hda_gen_spec *spec, hda_nid_t mix, int idx) +{ + struct hda_amp_list *list; + + if (spec->num_loopbacks >= ARRAY_SIZE(spec->loopback_list) - 1) + return; + list = spec->loopback_list + spec->num_loopbacks; + list->nid = mix; + list->dir = HDA_INPUT; + list->idx = idx; + spec->num_loopbacks++; + spec->loopback.amplist = spec->loopback_list; +} +#else +#define add_loopback_list(spec, mix, idx) /* NOP */ +#endif + +/* create input playback/capture controls for the given pin */ +static int new_analog_input(struct hda_codec *codec, hda_nid_t pin, + const char *ctlname, int ctlidx, + hda_nid_t mix_nid) +{ + struct hda_gen_spec *spec = codec->spec; + struct nid_path *path; + unsigned int val; + int err, idx; + + if (!nid_has_volume(codec, mix_nid, HDA_INPUT) && + !nid_has_mute(codec, mix_nid, HDA_INPUT)) + return 0; /* no need for analog loopback */ + + path = snd_hda_add_new_path(codec, pin, mix_nid, 2); + if (!path) + return -EINVAL; + + idx = path->idx[path->depth - 1]; + if (nid_has_volume(codec, mix_nid, HDA_INPUT)) { + val = HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT); + err = __add_pb_vol_ctrl(spec, HDA_CTL_WIDGET_VOL, ctlname, ctlidx, val); + if (err < 0) + return err; + path->ctls[NID_PATH_VOL_CTL] = val; + } + + if (nid_has_mute(codec, mix_nid, HDA_INPUT)) { + val = HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT); + err = __add_pb_sw_ctrl(spec, HDA_CTL_WIDGET_MUTE, ctlname, ctlidx, val); + if (err < 0) + return err; + path->ctls[NID_PATH_MUTE_CTL] = val; + } + + path->active = true; + add_loopback_list(spec, mix_nid, idx); + return 0; +} + +static int is_input_pin(struct hda_codec *codec, hda_nid_t nid) +{ + unsigned int pincap = snd_hda_query_pin_caps(codec, nid); + return (pincap & AC_PINCAP_IN) != 0; +} + +/* Parse the codec tree and retrieve ADCs */ +static int fill_adc_nids(struct hda_codec *codec) +{ + struct hda_gen_spec *spec = codec->spec; + hda_nid_t nid; + hda_nid_t *adc_nids = spec->adc_nids; + int max_nums = ARRAY_SIZE(spec->adc_nids); + int i, nums = 0; + + nid = codec->start_nid; + for (i = 0; i < codec->num_nodes; i++, nid++) { + unsigned int caps = get_wcaps(codec, nid); + int type = get_wcaps_type(caps); + + if (type != AC_WID_AUD_IN || (caps & AC_WCAP_DIGITAL)) + continue; + adc_nids[nums] = nid; + if (++nums >= max_nums) + break; + } + spec->num_adc_nids = nums; + return nums; +} + +/* filter out invalid adc_nids that don't give all active input pins; + * if needed, check whether dynamic ADC-switching is available + */ +static int check_dyn_adc_switch(struct hda_codec *codec) +{ + struct hda_gen_spec *spec = codec->spec; + struct hda_input_mux *imux = &spec->input_mux; + hda_nid_t adc_nids[ARRAY_SIZE(spec->adc_nids)]; + int i, n, nums; + hda_nid_t pin, adc; + + again: + nums = 0; + for (n = 0; n < spec->num_adc_nids; n++) { + adc = spec->adc_nids[n]; + for (i = 0; i < imux->num_items; i++) { + pin = spec->imux_pins[i]; + if (!is_reachable_path(codec, pin, adc)) + break; + } + if (i >= imux->num_items) + adc_nids[nums++] = adc; + } + + if (!nums) { + if (spec->shared_mic_hp) { + spec->shared_mic_hp = 0; + imux->num_items = 1; + goto again; + } + + /* check whether ADC-switch is possible */ + for (i = 0; i < imux->num_items; i++) { + pin = spec->imux_pins[i]; + for (n = 0; n < spec->num_adc_nids; n++) { + adc = spec->adc_nids[n]; + if (is_reachable_path(codec, pin, adc)) { + spec->dyn_adc_idx[i] = n; + break; + } + } + } + + snd_printdd("hda-codec: enabling ADC switching\n"); + spec->dyn_adc_switch = 1; + } else if (nums != spec->num_adc_nids) { + memcpy(spec->adc_nids, adc_nids, nums * sizeof(hda_nid_t)); + spec->num_adc_nids = nums; + } + + if (imux->num_items == 1 || spec->shared_mic_hp) { + snd_printdd("hda-codec: reducing to a single ADC\n"); + spec->num_adc_nids = 1; /* reduce to a single ADC */ + } + + /* single index for individual volumes ctls */ + if (!spec->dyn_adc_switch && spec->multi_cap_vol) + spec->num_adc_nids = 1; + + return 0; +} + +/* + * create playback/capture controls for input pins + */ +static int create_input_ctls(struct hda_codec *codec) +{ + struct hda_gen_spec *spec = codec->spec; + const struct auto_pin_cfg *cfg = &spec->autocfg; + hda_nid_t mixer = spec->mixer_nid; + struct hda_input_mux *imux = &spec->input_mux; + int num_adcs; + int i, c, err, type_idx = 0; + const char *prev_label = NULL; + + num_adcs = fill_adc_nids(codec); + if (num_adcs < 0) + return 0; + + for (i = 0; i < cfg->num_inputs; i++) { + hda_nid_t pin; + const char *label; + bool imux_added; + + pin = cfg->inputs[i].pin; + if (!is_input_pin(codec, pin)) + continue; + + label = hda_get_autocfg_input_label(codec, cfg, i); + if (spec->shared_mic_hp && !strcmp(label, "Misc")) + label = "Headphone Mic"; + if (prev_label && !strcmp(label, prev_label)) + type_idx++; + else + type_idx = 0; + prev_label = label; + + if (mixer) { + if (is_reachable_path(codec, pin, mixer)) { + err = new_analog_input(codec, pin, + label, type_idx, mixer); + if (err < 0) + return err; + } + } + + imux_added = false; + for (c = 0; c < num_adcs; c++) { + struct nid_path *path; + hda_nid_t adc = spec->adc_nids[c]; + + if (!is_reachable_path(codec, pin, adc)) + continue; + path = snd_array_new(&spec->paths); + if (!path) + return -ENOMEM; + memset(path, 0, sizeof(*path)); + if (!snd_hda_parse_nid_path(codec, pin, adc, 2, path)) { + snd_printd(KERN_ERR + "invalid input path 0x%x -> 0x%x\n", + pin, adc); + spec->paths.used--; + continue; + } + + if (!imux_added) { + spec->imux_pins[imux->num_items] = pin; + snd_hda_add_imux_item(imux, label, + imux->num_items, NULL); + imux_added = true; + } + } + } + + return 0; +} + + +/* + * input source mux + */ + +/* get the ADC NID corresponding to the given index */ +static hda_nid_t get_adc_nid(struct hda_codec *codec, int adc_idx, int imux_idx) +{ + struct hda_gen_spec *spec = codec->spec; + if (spec->dyn_adc_switch) + adc_idx = spec->dyn_adc_idx[imux_idx]; + return spec->adc_nids[adc_idx]; +} + +static int mux_select(struct hda_codec *codec, unsigned int adc_idx, + unsigned int idx); + +static int mux_enum_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct hda_gen_spec *spec = codec->spec; + return snd_hda_input_mux_info(&spec->input_mux, uinfo); +} + +static int mux_enum_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct hda_gen_spec *spec = codec->spec; + unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); + + ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx]; + return 0; +} + +static int mux_enum_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); + return mux_select(codec, adc_idx, + ucontrol->value.enumerated.item[0]); +} + +/* + * capture volume and capture switch ctls + */ + +static const struct snd_kcontrol_new cap_src_temp = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Input Source", + .info = mux_enum_info, + .get = mux_enum_get, + .put = mux_enum_put, +}; + +typedef int (*put_call_t)(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); + +static int cap_put_caller(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol, + put_call_t func, int type) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct hda_gen_spec *spec = codec->spec; + const struct hda_input_mux *imux; + struct nid_path *path; + int i, adc_idx, err = 0; + + imux = &spec->input_mux; + adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); + mutex_lock(&codec->control_mutex); + codec->cached_write = 1; + for (i = 0; i < imux->num_items; i++) { + path = snd_hda_get_nid_path(codec, spec->imux_pins[i], + get_adc_nid(codec, adc_idx, i)); + if (!path->ctls[type]) + continue; + kcontrol->private_value = path->ctls[type]; + err = func(kcontrol, ucontrol); + if (err < 0) + goto error; + } + error: + codec->cached_write = 0; + mutex_unlock(&codec->control_mutex); + if (err >= 0 && spec->cap_sync_hook) + spec->cap_sync_hook(codec); + return err; +} + +/* capture volume ctl callbacks */ +#define cap_vol_info snd_hda_mixer_amp_volume_info +#define cap_vol_get snd_hda_mixer_amp_volume_get +#define cap_vol_tlv snd_hda_mixer_amp_tlv + +static int cap_vol_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + return cap_put_caller(kcontrol, ucontrol, + snd_hda_mixer_amp_volume_put, + NID_PATH_VOL_CTL); +} + +static const struct snd_kcontrol_new cap_vol_temp = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Capture Volume", + .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ | + SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK), + .info = cap_vol_info, + .get = cap_vol_get, + .put = cap_vol_put, + .tlv = { .c = cap_vol_tlv }, +}; + +/* capture switch ctl callbacks */ +#define cap_sw_info snd_ctl_boolean_stereo_info +#define cap_sw_get snd_hda_mixer_amp_switch_get + +static int cap_sw_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + return cap_put_caller(kcontrol, ucontrol, + snd_hda_mixer_amp_switch_put, + NID_PATH_MUTE_CTL); +} + +static const struct snd_kcontrol_new cap_sw_temp = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Capture Switch", + .info = cap_sw_info, + .get = cap_sw_get, + .put = cap_sw_put, +}; + +static int parse_capvol_in_path(struct hda_codec *codec, struct nid_path *path) +{ + hda_nid_t nid; + int i, depth; + + path->ctls[NID_PATH_VOL_CTL] = path->ctls[NID_PATH_MUTE_CTL] = 0; + for (depth = 0; depth < 3; depth++) { + if (depth >= path->depth) + return -EINVAL; + i = path->depth - depth - 1; + nid = path->path[i]; + if (!path->ctls[NID_PATH_VOL_CTL]) { + if (nid_has_volume(codec, nid, HDA_OUTPUT)) + path->ctls[NID_PATH_VOL_CTL] = + HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT); + else if (nid_has_volume(codec, nid, HDA_INPUT)) { + int idx = path->idx[i]; + if (!depth && codec->single_adc_amp) + idx = 0; + path->ctls[NID_PATH_VOL_CTL] = + HDA_COMPOSE_AMP_VAL(nid, 3, idx, HDA_INPUT); + } + } + if (!path->ctls[NID_PATH_MUTE_CTL]) { + if (nid_has_mute(codec, nid, HDA_OUTPUT)) + path->ctls[NID_PATH_MUTE_CTL] = + HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT); + else if (nid_has_mute(codec, nid, HDA_INPUT)) { + int idx = path->idx[i]; + if (!depth && codec->single_adc_amp) + idx = 0; + path->ctls[NID_PATH_MUTE_CTL] = + HDA_COMPOSE_AMP_VAL(nid, 3, idx, HDA_INPUT); + } + } + } + return 0; +} + +static bool is_inv_dmic_pin(struct hda_codec *codec, hda_nid_t nid) +{ + struct hda_gen_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + unsigned int val; + int i; + + if (!spec->inv_dmic_split) + return false; + for (i = 0; i < cfg->num_inputs; i++) { + if (cfg->inputs[i].pin != nid) + continue; + if (cfg->inputs[i].type != AUTO_PIN_MIC) + return false; + val = snd_hda_codec_get_pincfg(codec, nid); + return snd_hda_get_input_pin_attr(val) == INPUT_PIN_ATTR_INT; + } + return false; +} + +static int add_single_cap_ctl(struct hda_codec *codec, const char *label, + int idx, bool is_switch, unsigned int ctl, + bool inv_dmic) +{ + struct hda_gen_spec *spec = codec->spec; + char tmpname[44]; + int type = is_switch ? HDA_CTL_WIDGET_MUTE : HDA_CTL_WIDGET_VOL; + const char *sfx = is_switch ? "Switch" : "Volume"; + unsigned int chs = inv_dmic ? 1 : 3; + int err; + + if (!ctl) + return 0; + + if (label) + snprintf(tmpname, sizeof(tmpname), + "%s Capture %s", label, sfx); + else + snprintf(tmpname, sizeof(tmpname), + "Capture %s", sfx); + err = add_control(spec, type, tmpname, idx, + amp_val_replace_channels(ctl, chs)); + if (err < 0 || !inv_dmic) + return err; + + /* Make independent right kcontrol */ + if (label) + snprintf(tmpname, sizeof(tmpname), + "Inverted %s Capture %s", label, sfx); + else + snprintf(tmpname, sizeof(tmpname), + "Inverted Capture %s", sfx); + return add_control(spec, type, tmpname, idx, + amp_val_replace_channels(ctl, 2)); +} + +/* create single (and simple) capture volume and switch controls */ +static int create_single_cap_vol_ctl(struct hda_codec *codec, int idx, + unsigned int vol_ctl, unsigned int sw_ctl, + bool inv_dmic) +{ + int err; + err = add_single_cap_ctl(codec, NULL, idx, false, vol_ctl, inv_dmic); + if (err < 0) + return err; + err = add_single_cap_ctl(codec, NULL, idx, true, sw_ctl, inv_dmic); + if (err < 0) + return err; + return 0; +} + +/* create bound capture volume and switch controls */ +static int create_bind_cap_vol_ctl(struct hda_codec *codec, int idx, + unsigned int vol_ctl, unsigned int sw_ctl) +{ + struct hda_gen_spec *spec = codec->spec; + struct snd_kcontrol_new *knew; + + if (vol_ctl) { + knew = add_kctl(spec, NULL, &cap_vol_temp); + if (!knew) + return -ENOMEM; + knew->index = idx; + knew->private_value = vol_ctl; + knew->subdevice = HDA_SUBDEV_AMP_FLAG; + } + if (sw_ctl) { + knew = add_kctl(spec, NULL, &cap_sw_temp); + if (!knew) + return -ENOMEM; + knew->index = idx; + knew->private_value = sw_ctl; + knew->subdevice = HDA_SUBDEV_AMP_FLAG; + } + return 0; +} + +/* return the vol ctl when used first in the imux list */ +static unsigned int get_first_cap_ctl(struct hda_codec *codec, int idx, int type) +{ + struct hda_gen_spec *spec = codec->spec; + struct nid_path *path; + unsigned int ctl; + int i; + + path = snd_hda_get_nid_path(codec, spec->imux_pins[idx], + get_adc_nid(codec, 0, idx)); + if (!path) + return 0; + ctl = path->ctls[type]; + if (!ctl) + return 0; + for (i = 0; i < idx - 1; i++) { + path = snd_hda_get_nid_path(codec, spec->imux_pins[i], + get_adc_nid(codec, 0, i)); + if (path && path->ctls[type] == ctl) + return 0; + } + return ctl; +} + +/* create individual capture volume and switch controls per input */ +static int create_multi_cap_vol_ctl(struct hda_codec *codec) +{ + struct hda_gen_spec *spec = codec->spec; + struct hda_input_mux *imux = &spec->input_mux; + int i, err, type, type_idx = 0; + const char *prev_label = NULL; + + for (i = 0; i < imux->num_items; i++) { + const char *label; + bool inv_dmic; + label = hda_get_autocfg_input_label(codec, &spec->autocfg, i); + if (prev_label && !strcmp(label, prev_label)) + type_idx++; + else + type_idx = 0; + prev_label = label; + inv_dmic = is_inv_dmic_pin(codec, spec->imux_pins[i]); + + for (type = 0; type < 2; type++) { + err = add_single_cap_ctl(codec, label, type_idx, type, + get_first_cap_ctl(codec, i, type), + inv_dmic); + if (err < 0) + return err; + } + } + return 0; +} + +static int create_capture_mixers(struct hda_codec *codec) +{ + struct hda_gen_spec *spec = codec->spec; + struct hda_input_mux *imux = &spec->input_mux; + int i, n, nums, err; + + if (spec->dyn_adc_switch) + nums = 1; + else + nums = spec->num_adc_nids; + + if (!spec->auto_mic && imux->num_items > 1) { + struct snd_kcontrol_new *knew; + knew = add_kctl(spec, NULL, &cap_src_temp); + if (!knew) + return -ENOMEM; + knew->count = nums; + } + + for (n = 0; n < nums; n++) { + bool multi = false; + bool inv_dmic = false; + int vol, sw; + + vol = sw = 0; + for (i = 0; i < imux->num_items; i++) { + struct nid_path *path; + path = snd_hda_get_nid_path(codec, spec->imux_pins[i], + get_adc_nid(codec, n, i)); + if (!path) + continue; + parse_capvol_in_path(codec, path); + if (!vol) + vol = path->ctls[NID_PATH_VOL_CTL]; + else if (vol != path->ctls[NID_PATH_VOL_CTL]) + multi = true; + if (!sw) + sw = path->ctls[NID_PATH_MUTE_CTL]; + else if (sw != path->ctls[NID_PATH_MUTE_CTL]) + multi = true; + if (is_inv_dmic_pin(codec, spec->imux_pins[i])) + inv_dmic = true; + } + + if (!multi) + err = create_single_cap_vol_ctl(codec, n, vol, sw, + inv_dmic); + else if (!spec->multi_cap_vol) + err = create_bind_cap_vol_ctl(codec, n, vol, sw); + else + err = create_multi_cap_vol_ctl(codec); + if (err < 0) + return err; + } + + return 0; +} + +/* + * add mic boosts if needed + */ +static int parse_mic_boost(struct hda_codec *codec) +{ + struct hda_gen_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + int i, err; + int type_idx = 0; + hda_nid_t nid; + const char *prev_label = NULL; + + for (i = 0; i < cfg->num_inputs; i++) { + if (cfg->inputs[i].type > AUTO_PIN_MIC) + break; + nid = cfg->inputs[i].pin; + if (get_wcaps(codec, nid) & AC_WCAP_IN_AMP) { + const char *label; + char boost_label[32]; + struct nid_path *path; + unsigned int val; + + label = hda_get_autocfg_input_label(codec, cfg, i); + if (spec->shared_mic_hp && !strcmp(label, "Misc")) + label = "Headphone Mic"; + if (prev_label && !strcmp(label, prev_label)) + type_idx++; + else + type_idx = 0; + prev_label = label; + + snprintf(boost_label, sizeof(boost_label), + "%s Boost Volume", label); + val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT); + err = add_control(spec, HDA_CTL_WIDGET_VOL, + boost_label, type_idx, val); + if (err < 0) + return err; + + path = snd_hda_get_nid_path(codec, nid, 0); + if (path) + path->ctls[NID_PATH_BOOST_CTL] = val; + } + } + return 0; +} + +/* + * parse digital I/Os and set up NIDs in BIOS auto-parse mode + */ +static void parse_digital(struct hda_codec *codec) +{ + struct hda_gen_spec *spec = codec->spec; + int i, nums; + hda_nid_t dig_nid; + + /* support multiple SPDIFs; the secondary is set up as a slave */ + nums = 0; + for (i = 0; i < spec->autocfg.dig_outs; i++) { + hda_nid_t pin = spec->autocfg.dig_out_pins[i]; + dig_nid = look_for_dac(codec, pin, true); + if (!dig_nid) + continue; + if (!snd_hda_add_new_path(codec, dig_nid, pin, 2)) + continue; + if (!nums) { + spec->multiout.dig_out_nid = dig_nid; + spec->dig_out_type = spec->autocfg.dig_out_type[0]; + } else { + spec->multiout.slave_dig_outs = spec->slave_dig_outs; + if (nums >= ARRAY_SIZE(spec->slave_dig_outs) - 1) + break; + spec->slave_dig_outs[nums - 1] = dig_nid; + } + nums++; + } + + if (spec->autocfg.dig_in_pin) { + dig_nid = codec->start_nid; + for (i = 0; i < codec->num_nodes; i++, dig_nid++) { + struct nid_path *path; + unsigned int wcaps = get_wcaps(codec, dig_nid); + if (get_wcaps_type(wcaps) != AC_WID_AUD_IN) + continue; + if (!(wcaps & AC_WCAP_DIGITAL)) + continue; + path = snd_hda_add_new_path(codec, + spec->autocfg.dig_in_pin, + dig_nid, 2); + if (path) { + path->active = true; + spec->dig_in_nid = dig_nid; + break; + } + } + } +} + + +/* + * input MUX handling + */ + +static bool dyn_adc_pcm_resetup(struct hda_codec *codec, int cur); + +/* select the given imux item; either unmute exclusively or select the route */ +static int mux_select(struct hda_codec *codec, unsigned int adc_idx, + unsigned int idx) +{ + struct hda_gen_spec *spec = codec->spec; + const struct hda_input_mux *imux; + struct nid_path *path; + + imux = &spec->input_mux; + if (!imux->num_items) + return 0; + + if (idx >= imux->num_items) + idx = imux->num_items - 1; + if (spec->cur_mux[adc_idx] == idx) + return 0; + + path = snd_hda_get_nid_path(codec, + spec->imux_pins[spec->cur_mux[adc_idx]], + spec->adc_nids[adc_idx]); + if (!path) + return 0; + if (path->active) + snd_hda_activate_path(codec, path, false, false); + + spec->cur_mux[adc_idx] = idx; + + if (spec->shared_mic_hp) + update_shared_mic_hp(codec, spec->cur_mux[adc_idx]); + + if (spec->dyn_adc_switch) + dyn_adc_pcm_resetup(codec, idx); + + path = snd_hda_get_nid_path(codec, spec->imux_pins[idx], + get_adc_nid(codec, adc_idx, idx)); + if (!path) + return 0; + if (path->active) + return 0; + snd_hda_activate_path(codec, path, true, false); + if (spec->cap_sync_hook) + spec->cap_sync_hook(codec); + return 1; +} + + +/* + * Jack detections for HP auto-mute and mic-switch + */ + +/* check each pin in the given array; returns true if any of them is plugged */ +static bool detect_jacks(struct hda_codec *codec, int num_pins, hda_nid_t *pins) +{ + int i, present = 0; + + for (i = 0; i < num_pins; i++) { + hda_nid_t nid = pins[i]; + if (!nid) + break; + present |= snd_hda_jack_detect(codec, nid); + } + return present; +} + +/* standard HP/line-out auto-mute helper */ +static void do_automute(struct hda_codec *codec, int num_pins, hda_nid_t *pins, + bool mute, bool hp_out) +{ + struct hda_gen_spec *spec = codec->spec; + unsigned int pin_bits = mute ? 0 : (hp_out ? PIN_HP : PIN_OUT); + int i; + + for (i = 0; i < num_pins; i++) { + hda_nid_t nid = pins[i]; + unsigned int val; + if (!nid) + break; + /* don't reset VREF value in case it's controlling + * the amp (see alc861_fixup_asus_amp_vref_0f()) + */ + if (spec->keep_vref_in_automute) { + val = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_PIN_WIDGET_CONTROL, 0); + val &= ~PIN_HP; + } else + val = 0; + val |= pin_bits; + snd_hda_set_pin_ctl(codec, nid, val); + } +} + +/* Toggle outputs muting */ +static void update_outputs(struct hda_codec *codec) +{ + struct hda_gen_spec *spec = codec->spec; + int on; + + /* Control HP pins/amps depending on master_mute state; + * in general, HP pins/amps control should be enabled in all cases, + * but currently set only for master_mute, just to be safe + */ + if (!spec->shared_mic_hp) /* don't change HP-pin when shared with mic */ + do_automute(codec, ARRAY_SIZE(spec->autocfg.hp_pins), + spec->autocfg.hp_pins, spec->master_mute, true); + + if (!spec->automute_speaker) + on = 0; + else + on = spec->hp_jack_present | spec->line_jack_present; + on |= spec->master_mute; + do_automute(codec, ARRAY_SIZE(spec->autocfg.speaker_pins), + spec->autocfg.speaker_pins, on, false); + + /* toggle line-out mutes if needed, too */ + /* if LO is a copy of either HP or Speaker, don't need to handle it */ + if (spec->autocfg.line_out_pins[0] == spec->autocfg.hp_pins[0] || + spec->autocfg.line_out_pins[0] == spec->autocfg.speaker_pins[0]) + return; + if (!spec->automute_lo) + on = 0; + else + on = spec->hp_jack_present; + on |= spec->master_mute; + do_automute(codec, ARRAY_SIZE(spec->autocfg.line_out_pins), + spec->autocfg.line_out_pins, on, false); +} + +static void call_update_outputs(struct hda_codec *codec) +{ + struct hda_gen_spec *spec = codec->spec; + if (spec->automute_hook) + spec->automute_hook(codec); + else + update_outputs(codec); +} + +/* standard HP-automute helper */ +static void hp_automute(struct hda_codec *codec, struct hda_jack_tbl *jack) +{ + struct hda_gen_spec *spec = codec->spec; + + spec->hp_jack_present = + detect_jacks(codec, ARRAY_SIZE(spec->autocfg.hp_pins), + spec->autocfg.hp_pins); + if (!spec->detect_hp || (!spec->automute_speaker && !spec->automute_lo)) + return; + call_update_outputs(codec); +} + +/* standard line-out-automute helper */ +static void line_automute(struct hda_codec *codec, struct hda_jack_tbl *jack) +{ + struct hda_gen_spec *spec = codec->spec; + + if (spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT) + return; + /* check LO jack only when it's different from HP */ + if (spec->autocfg.line_out_pins[0] == spec->autocfg.hp_pins[0]) + return; + + spec->line_jack_present = + detect_jacks(codec, ARRAY_SIZE(spec->autocfg.line_out_pins), + spec->autocfg.line_out_pins); + if (!spec->automute_speaker || !spec->detect_lo) + return; + call_update_outputs(codec); +} + +/* standard mic auto-switch helper */ +static void mic_autoswitch(struct hda_codec *codec, struct hda_jack_tbl *jack) +{ + struct hda_gen_spec *spec = codec->spec; + int i; + + if (!spec->auto_mic) + return; + + for (i = spec->am_num_entries - 1; i > 0; i--) { + if (snd_hda_jack_detect(codec, spec->am_entry[i].pin)) { + mux_select(codec, 0, spec->am_entry[i].idx); + return; + } + } + mux_select(codec, 0, spec->am_entry[0].idx); +} + +/* + * Auto-Mute mode mixer enum support + */ +static int automute_mode_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct hda_gen_spec *spec = codec->spec; + static const char * const texts3[] = { + "Disabled", "Speaker Only", "Line Out+Speaker" + }; + + if (spec->automute_speaker_possible && spec->automute_lo_possible) + return snd_hda_enum_helper_info(kcontrol, uinfo, 3, texts3); + return snd_hda_enum_bool_helper_info(kcontrol, uinfo); +} + +static int automute_mode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct hda_gen_spec *spec = codec->spec; + unsigned int val = 0; + if (spec->automute_speaker) + val++; + if (spec->automute_lo) + val++; + + ucontrol->value.enumerated.item[0] = val; + return 0; +} + +static int automute_mode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct hda_gen_spec *spec = codec->spec; + + switch (ucontrol->value.enumerated.item[0]) { + case 0: + if (!spec->automute_speaker && !spec->automute_lo) + return 0; + spec->automute_speaker = 0; + spec->automute_lo = 0; + break; + case 1: + if (spec->automute_speaker_possible) { + if (!spec->automute_lo && spec->automute_speaker) + return 0; + spec->automute_speaker = 1; + spec->automute_lo = 0; + } else if (spec->automute_lo_possible) { + if (spec->automute_lo) + return 0; + spec->automute_lo = 1; + } else + return -EINVAL; + break; + case 2: + if (!spec->automute_lo_possible || !spec->automute_speaker_possible) + return -EINVAL; + if (spec->automute_speaker && spec->automute_lo) + return 0; + spec->automute_speaker = 1; + spec->automute_lo = 1; + break; + default: + return -EINVAL; + } + call_update_outputs(codec); + return 1; +} + +static const struct snd_kcontrol_new automute_mode_enum = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Auto-Mute Mode", + .info = automute_mode_info, + .get = automute_mode_get, + .put = automute_mode_put, +}; + +static int add_automute_mode_enum(struct hda_codec *codec) +{ + struct hda_gen_spec *spec = codec->spec; + + if (!add_kctl(spec, NULL, &automute_mode_enum)) + return -ENOMEM; + return 0; +} + +/* + * Check the availability of HP/line-out auto-mute; + * Set up appropriately if really supported + */ +static int check_auto_mute_availability(struct hda_codec *codec) +{ + struct hda_gen_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + int present = 0; + int i, err; + + if (cfg->hp_pins[0]) + present++; + if (cfg->line_out_pins[0]) + present++; + if (cfg->speaker_pins[0]) + present++; + if (present < 2) /* need two different output types */ + return 0; + + if (!cfg->speaker_pins[0] && + cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) { + memcpy(cfg->speaker_pins, cfg->line_out_pins, + sizeof(cfg->speaker_pins)); + cfg->speaker_outs = cfg->line_outs; + } + + if (!cfg->hp_pins[0] && + cfg->line_out_type == AUTO_PIN_HP_OUT) { + memcpy(cfg->hp_pins, cfg->line_out_pins, + sizeof(cfg->hp_pins)); + cfg->hp_outs = cfg->line_outs; + } + + for (i = 0; i < cfg->hp_outs; i++) { + hda_nid_t nid = cfg->hp_pins[i]; + if (!is_jack_detectable(codec, nid)) + continue; + snd_printdd("hda-codec: Enable HP auto-muting on NID 0x%x\n", + nid); + snd_hda_jack_detect_enable_callback(codec, nid, HDA_GEN_HP_EVENT, + hp_automute); + spec->detect_hp = 1; + } + + if (cfg->line_out_type == AUTO_PIN_LINE_OUT && cfg->line_outs) { + if (cfg->speaker_outs) + for (i = 0; i < cfg->line_outs; i++) { + hda_nid_t nid = cfg->line_out_pins[i]; + if (!is_jack_detectable(codec, nid)) + continue; + snd_printdd("hda-codec: Enable Line-Out auto-muting on NID 0x%x\n", nid); + snd_hda_jack_detect_enable_callback(codec, nid, + HDA_GEN_FRONT_EVENT, + line_automute); + spec->detect_lo = 1; + } + spec->automute_lo_possible = spec->detect_hp; + } + + spec->automute_speaker_possible = cfg->speaker_outs && + (spec->detect_hp || spec->detect_lo); + + spec->automute_lo = spec->automute_lo_possible; + spec->automute_speaker = spec->automute_speaker_possible; + + if (spec->automute_speaker_possible || spec->automute_lo_possible) { + /* create a control for automute mode */ + err = add_automute_mode_enum(codec); + if (err < 0) + return err; + } + return 0; +} + +/* return the position of NID in the list, or -1 if not found */ +static int find_idx_in_nid_list(hda_nid_t nid, const hda_nid_t *list, int nums) +{ + int i; + for (i = 0; i < nums; i++) + if (list[i] == nid) + return i; + return -1; +} + +/* check whether all auto-mic pins are valid; setup indices if OK */ +static bool auto_mic_check_imux(struct hda_codec *codec) +{ + struct hda_gen_spec *spec = codec->spec; + const struct hda_input_mux *imux; + int i; + + imux = &spec->input_mux; + for (i = 0; i < spec->am_num_entries; i++) { + spec->am_entry[i].idx = + find_idx_in_nid_list(spec->am_entry[i].pin, + spec->imux_pins, imux->num_items); + if (spec->am_entry[i].idx < 0) + return false; /* no corresponding imux */ + } + + /* we don't need the jack detection for the first pin */ + for (i = 1; i < spec->am_num_entries; i++) + snd_hda_jack_detect_enable_callback(codec, + spec->am_entry[i].pin, + HDA_GEN_MIC_EVENT, + mic_autoswitch); + return true; +} + +static int compare_attr(const void *ap, const void *bp) +{ + const struct automic_entry *a = ap; + const struct automic_entry *b = bp; + return (int)(a->attr - b->attr); +} /* - * check whether the controls with the given name and direction suffix already exist + * Check the availability of auto-mic switch; + * Set up if really supported */ -static int check_existing_control(struct hda_codec *codec, const char *type, const char *dir) -{ - struct snd_ctl_elem_id id; - memset(&id, 0, sizeof(id)); - sprintf(id.name, "%s %s Volume", type, dir); - id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; - if (snd_ctl_find_id(codec->bus->card, &id)) - return 1; - sprintf(id.name, "%s %s Switch", type, dir); - id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; - if (snd_ctl_find_id(codec->bus->card, &id)) - return 1; +static int check_auto_mic_availability(struct hda_codec *codec) +{ + struct hda_gen_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + unsigned int types; + int i, num_pins; + + types = 0; + num_pins = 0; + for (i = 0; i < cfg->num_inputs; i++) { + hda_nid_t nid = cfg->inputs[i].pin; + unsigned int attr; + attr = snd_hda_codec_get_pincfg(codec, nid); + attr = snd_hda_get_input_pin_attr(attr); + if (types & (1 << attr)) + return 0; /* already occupied */ + switch (attr) { + case INPUT_PIN_ATTR_INT: + if (cfg->inputs[i].type != AUTO_PIN_MIC) + return 0; /* invalid type */ + break; + case INPUT_PIN_ATTR_UNUSED: + return 0; /* invalid entry */ + default: + if (cfg->inputs[i].type > AUTO_PIN_LINE_IN) + return 0; /* invalid type */ + if (!spec->line_in_auto_switch && + cfg->inputs[i].type != AUTO_PIN_MIC) + return 0; /* only mic is allowed */ + if (!is_jack_detectable(codec, nid)) + return 0; /* no unsol support */ + break; + } + if (num_pins >= MAX_AUTO_MIC_PINS) + return 0; + types |= (1 << attr); + spec->am_entry[num_pins].pin = nid; + spec->am_entry[num_pins].attr = attr; + num_pins++; + } + + if (num_pins < 2) + return 0; + + spec->am_num_entries = num_pins; + /* sort the am_entry in the order of attr so that the pin with a + * higher attr will be selected when the jack is plugged. + */ + sort(spec->am_entry, num_pins, sizeof(spec->am_entry[0]), + compare_attr, NULL); + + if (!auto_mic_check_imux(codec)) + return 0; + + spec->auto_mic = 1; + spec->num_adc_nids = 1; + spec->cur_mux[0] = spec->am_entry[0].idx; + snd_printdd("hda-codec: Enable auto-mic switch on NID 0x%x/0x%x/0x%x\n", + spec->am_entry[0].pin, + spec->am_entry[1].pin, + spec->am_entry[2].pin); + return 0; } -/* - * build output mixer controls - */ -static int create_output_mixers(struct hda_codec *codec, - const char * const *names) -{ - struct hda_gspec *spec = codec->spec; - int i, err; - for (i = 0; i < spec->pcm_vol_nodes; i++) { - err = create_mixer(codec, spec->pcm_vol[i].node, - spec->pcm_vol[i].index, - names[i], "Playback", 0); +/* parse the BIOS configuration and set up the hda_gen_spec */ +/* return 1 if successful, 0 if the proper config is not found, + * or a negative error code + */ +int snd_hda_gen_parse_auto_config(struct hda_codec *codec, + const hda_nid_t *ignore_nids) +{ + struct hda_gen_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + int err; + + err = snd_hda_parse_pin_defcfg(codec, cfg, ignore_nids, + spec->parse_flags); + if (err < 0) + return err; + if (!cfg->line_outs) { + if (cfg->dig_outs || cfg->dig_in_pin) { + spec->multiout.max_channels = 2; + spec->no_analog = 1; + goto dig_only; + } + return 0; /* can't find valid BIOS pin config */ + } + + if (!spec->no_primary_hp && + cfg->line_out_type == AUTO_PIN_SPEAKER_OUT && + cfg->line_outs <= cfg->hp_outs) { + /* use HP as primary out */ + cfg->speaker_outs = cfg->line_outs; + memcpy(cfg->speaker_pins, cfg->line_out_pins, + sizeof(cfg->speaker_pins)); + cfg->line_outs = cfg->hp_outs; + memcpy(cfg->line_out_pins, cfg->hp_pins, sizeof(cfg->hp_pins)); + cfg->hp_outs = 0; + memset(cfg->hp_pins, 0, sizeof(cfg->hp_pins)); + cfg->line_out_type = AUTO_PIN_HP_OUT; + } + + err = parse_output_paths(codec); + if (err < 0) + return err; + err = create_multi_channel_mode(codec); + if (err < 0) + return err; + err = create_multi_out_ctls(codec, cfg); + if (err < 0) + return err; + err = create_hp_out_ctls(codec); + if (err < 0) + return err; + err = create_speaker_out_ctls(codec); + if (err < 0) + return err; + err = create_shared_input(codec); + if (err < 0) + return err; + err = create_input_ctls(codec); + if (err < 0) + return err; + + /* check the multiple speaker pins */ + if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) + spec->const_channel_count = cfg->line_outs * 2; + else + spec->const_channel_count = cfg->speaker_outs * 2; + + if (spec->multi_ios > 0) + spec->multiout.max_channels = max(spec->ext_channel_count, + spec->const_channel_count); + else + spec->multiout.max_channels = spec->multiout.num_dacs * 2; + + err = check_auto_mute_availability(codec); + if (err < 0) + return err; + + err = check_dyn_adc_switch(codec); + if (err < 0) + return err; + + if (!spec->shared_mic_hp) { + err = check_auto_mic_availability(codec); if (err < 0) return err; } - return 0; -} -static int build_output_controls(struct hda_codec *codec) -{ - struct hda_gspec *spec = codec->spec; - static const char * const types_speaker[] = { "Speaker", "Headphone" }; - static const char * const types_line[] = { "Front", "Headphone" }; + err = create_capture_mixers(codec); + if (err < 0) + return err; - switch (spec->pcm_vol_nodes) { - case 1: - return create_mixer(codec, spec->pcm_vol[0].node, - spec->pcm_vol[0].index, - "Master", "Playback", 0); - case 2: - if (defcfg_type(spec->out_pin_node[0]) == AC_JACK_SPEAKER) - return create_output_mixers(codec, types_speaker); - else - return create_output_mixers(codec, types_line); - } - return 0; + err = parse_mic_boost(codec); + if (err < 0) + return err; + + dig_only: + parse_digital(codec); + + return 1; } +EXPORT_SYMBOL_HDA(snd_hda_gen_parse_auto_config); -/* create capture volume/switch */ -static int build_input_controls(struct hda_codec *codec) -{ - struct hda_gspec *spec = codec->spec; - struct hda_gnode *adc_node = spec->adc_node; - int i, err; - static struct snd_kcontrol_new cap_sel = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Capture Source", - .info = capture_source_info, - .get = capture_source_get, - .put = capture_source_put, - }; - if (! adc_node || ! spec->input_mux.num_items) - return 0; /* not found */ +/* + * Build control elements + */ + +/* slave controls for virtual master */ +static const char * const slave_pfxs[] = { + "Front", "Surround", "Center", "LFE", "Side", + "Headphone", "Speaker", "Mono", "Line Out", + "CLFE", "Bass Speaker", "PCM", + NULL, +}; + +int snd_hda_gen_build_controls(struct hda_codec *codec) +{ + struct hda_gen_spec *spec = codec->spec; + int err; - spec->cur_cap_src = 0; - select_input_connection(codec, adc_node, - spec->input_mux.items[0].index); + err = snd_hda_add_new_ctls(codec, spec->kctls.list); + if (err < 0) + return err; - /* create capture volume and switch controls if the ADC has an amp */ - /* do we have only a single item? */ - if (spec->input_mux.num_items == 1) { - err = create_mixer(codec, adc_node, - spec->input_mux.items[0].index, - NULL, "Capture", 0); + if (spec->multiout.dig_out_nid) { + err = snd_hda_create_dig_out_ctls(codec, + spec->multiout.dig_out_nid, + spec->multiout.dig_out_nid, + spec->pcm_rec[1].pcm_type); + if (err < 0) + return err; + if (!spec->no_analog) { + err = snd_hda_create_spdif_share_sw(codec, + &spec->multiout); + if (err < 0) + return err; + spec->multiout.share_spdif = 1; + } + } + if (spec->dig_in_nid) { + err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid); if (err < 0) return err; - return 0; } - /* create input MUX if multiple sources are available */ - err = snd_hda_ctl_add(codec, spec->adc_node->nid, - snd_ctl_new1(&cap_sel, codec)); - if (err < 0) - return err; + /* if we have no master control, let's create it */ + if (!spec->no_analog && + !snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) { + unsigned int vmaster_tlv[4]; + snd_hda_set_vmaster_tlv(codec, spec->vmaster_nid, + HDA_OUTPUT, vmaster_tlv); + err = snd_hda_add_vmaster(codec, "Master Playback Volume", + vmaster_tlv, slave_pfxs, + "Playback Volume"); + if (err < 0) + return err; + } + if (!spec->no_analog && + !snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) { + err = __snd_hda_add_vmaster(codec, "Master Playback Switch", + NULL, slave_pfxs, + "Playback Switch", + true, &spec->vmaster_mute.sw_kctl); + if (err < 0) + return err; + if (spec->vmaster_mute.hook) + snd_hda_add_vmaster_hook(codec, &spec->vmaster_mute, true); + } - /* no volume control? */ - if (! (adc_node->wid_caps & AC_WCAP_IN_AMP) || - ! (adc_node->amp_in_caps & AC_AMPCAP_NUM_STEPS)) - return 0; + free_kctls(spec); /* no longer needed */ - for (i = 0; i < spec->input_mux.num_items; i++) { - struct snd_kcontrol_new knew; - char name[32]; - sprintf(name, "%s Capture Volume", - spec->input_mux.items[i].label); - knew = (struct snd_kcontrol_new) - HDA_CODEC_VOLUME(name, adc_node->nid, - spec->input_mux.items[i].index, - HDA_INPUT); - err = snd_hda_ctl_add(codec, adc_node->nid, - snd_ctl_new1(&knew, codec)); + if (spec->shared_mic_hp) { + int err; + int nid = spec->autocfg.inputs[1].pin; + err = snd_hda_jack_add_kctl(codec, nid, "Headphone Mic", 0); + if (err < 0) + return err; + err = snd_hda_jack_detect_enable(codec, nid, 0); if (err < 0) return err; } + err = snd_hda_jack_add_kctls(codec, &spec->autocfg); + if (err < 0) + return err; + return 0; } +EXPORT_SYMBOL_HDA(snd_hda_gen_build_controls); /* - * parse the nodes recursively until reach to the output PIN. - * - * returns 0 - if not found, - * 1 - if found, but no mixer is created - * 2 - if found and mixer was already created, (just skip) - * a negative error code + * PCM definitions */ -static int parse_loopback_path(struct hda_codec *codec, struct hda_gspec *spec, - struct hda_gnode *node, struct hda_gnode *dest_node, - const char *type) -{ - int i, err; - if (node->checked) - return 0; +/* + * Analog playback callbacks + */ +static int playback_pcm_open(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct hda_gen_spec *spec = codec->spec; + return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream, + hinfo); +} - node->checked = 1; - if (node == dest_node) { - /* loopback connection found */ - return 1; - } +static int playback_pcm_prepare(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + unsigned int stream_tag, + unsigned int format, + struct snd_pcm_substream *substream) +{ + struct hda_gen_spec *spec = codec->spec; + return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, + stream_tag, format, substream); +} - for (i = 0; i < node->nconns; i++) { - struct hda_gnode *child = hda_get_node(spec, node->conn_list[i]); - if (! child) - continue; - err = parse_loopback_path(codec, spec, child, dest_node, type); - if (err < 0) - return err; - else if (err >= 1) { - if (err == 1) { - err = create_mixer(codec, node, i, type, - "Playback", 1); - if (err < 0) - return err; - if (err > 0) - return 2; /* ok, created */ - /* not created, maybe in the lower path */ - err = 1; - } - /* connect and unmute */ - if (node->nconns > 1) - select_input_connection(codec, node, i); - unmute_input(codec, node, i); - unmute_output(codec, node); - return err; - } - } - return 0; +static int playback_pcm_cleanup(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct hda_gen_spec *spec = codec->spec; + return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout); } /* - * parse the tree and build the loopback controls + * Digital out */ -static int build_loopback_controls(struct hda_codec *codec) +static int dig_playback_pcm_open(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) { - struct hda_gspec *spec = codec->spec; - struct hda_gnode *node; - int err; - const char *type; + struct hda_gen_spec *spec = codec->spec; + return snd_hda_multi_out_dig_open(codec, &spec->multiout); +} - if (! spec->out_pin_node[0]) - return 0; +static int dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + unsigned int stream_tag, + unsigned int format, + struct snd_pcm_substream *substream) +{ + struct hda_gen_spec *spec = codec->spec; + return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, + stream_tag, format, substream); +} - list_for_each_entry(node, &spec->nid_list, list) { - if (node->type != AC_WID_PIN) - continue; - /* input capable? */ - if (! (node->pin_caps & AC_PINCAP_IN)) - return 0; - type = get_input_type(node, NULL); - if (type) { - if (check_existing_control(codec, type, "Playback")) - continue; - clear_check_flags(spec); - err = parse_loopback_path(codec, spec, - spec->out_pin_node[0], - node, type); - if (err < 0) - return err; - if (! err) - continue; - } - } - return 0; +static int dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct hda_gen_spec *spec = codec->spec; + return snd_hda_multi_out_dig_cleanup(codec, &spec->multiout); +} + +static int dig_playback_pcm_close(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct hda_gen_spec *spec = codec->spec; + return snd_hda_multi_out_dig_close(codec, &spec->multiout); } /* - * build mixer controls + * Analog capture */ -static int build_generic_controls(struct hda_codec *codec) +static int alt_capture_pcm_prepare(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + unsigned int stream_tag, + unsigned int format, + struct snd_pcm_substream *substream) { - int err; + struct hda_gen_spec *spec = codec->spec; - if ((err = build_input_controls(codec)) < 0 || - (err = build_output_controls(codec)) < 0 || - (err = build_loopback_controls(codec)) < 0) - return err; + snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number + 1], + stream_tag, 0, format); + return 0; +} + +static int alt_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct hda_gen_spec *spec = codec->spec; + snd_hda_codec_cleanup_stream(codec, + spec->adc_nids[substream->number + 1]); return 0; } /* - * PCM */ -static struct hda_pcm_stream generic_pcm_playback = { +static const struct hda_pcm_stream pcm_analog_playback = { + .substreams = 1, + .channels_min = 2, + .channels_max = 8, + /* NID is set in build_pcms */ + .ops = { + .open = playback_pcm_open, + .prepare = playback_pcm_prepare, + .cleanup = playback_pcm_cleanup + }, +}; + +static const struct hda_pcm_stream pcm_analog_capture = { .substreams = 1, .channels_min = 2, .channels_max = 2, + /* NID is set in build_pcms */ }; -static int generic_pcm2_prepare(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - unsigned int stream_tag, - unsigned int format, - struct snd_pcm_substream *substream) +static const struct hda_pcm_stream pcm_analog_alt_playback = { + .substreams = 1, + .channels_min = 2, + .channels_max = 2, + /* NID is set in build_pcms */ +}; + +static const struct hda_pcm_stream pcm_analog_alt_capture = { + .substreams = 2, /* can be overridden */ + .channels_min = 2, + .channels_max = 2, + /* NID is set in build_pcms */ + .ops = { + .prepare = alt_capture_pcm_prepare, + .cleanup = alt_capture_pcm_cleanup + }, +}; + +static const struct hda_pcm_stream pcm_digital_playback = { + .substreams = 1, + .channels_min = 2, + .channels_max = 2, + /* NID is set in build_pcms */ + .ops = { + .open = dig_playback_pcm_open, + .close = dig_playback_pcm_close, + .prepare = dig_playback_pcm_prepare, + .cleanup = dig_playback_pcm_cleanup + }, +}; + +static const struct hda_pcm_stream pcm_digital_capture = { + .substreams = 1, + .channels_min = 2, + .channels_max = 2, + /* NID is set in build_pcms */ +}; + +/* Used by build_pcms to flag that a PCM has no playback stream */ +static const struct hda_pcm_stream pcm_null_stream = { + .substreams = 0, + .channels_min = 0, + .channels_max = 0, +}; + +/* + * dynamic changing ADC PCM streams + */ +static bool dyn_adc_pcm_resetup(struct hda_codec *codec, int cur) { - struct hda_gspec *spec = codec->spec; + struct hda_gen_spec *spec = codec->spec; + hda_nid_t new_adc = spec->adc_nids[spec->dyn_adc_idx[cur]]; + + if (spec->cur_adc && spec->cur_adc != new_adc) { + /* stream is running, let's swap the current ADC */ + __snd_hda_codec_cleanup_stream(codec, spec->cur_adc, 1); + spec->cur_adc = new_adc; + snd_hda_codec_setup_stream(codec, new_adc, + spec->cur_adc_stream_tag, 0, + spec->cur_adc_format); + return true; + } + return false; +} - snd_hda_codec_setup_stream(codec, hinfo->nid, stream_tag, 0, format); - snd_hda_codec_setup_stream(codec, spec->dac_node[1]->nid, - stream_tag, 0, format); +/* analog capture with dynamic dual-adc changes */ +static int dyn_adc_capture_pcm_prepare(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + unsigned int stream_tag, + unsigned int format, + struct snd_pcm_substream *substream) +{ + struct hda_gen_spec *spec = codec->spec; + spec->cur_adc = spec->adc_nids[spec->dyn_adc_idx[spec->cur_mux[0]]]; + spec->cur_adc_stream_tag = stream_tag; + spec->cur_adc_format = format; + snd_hda_codec_setup_stream(codec, spec->cur_adc, stream_tag, 0, format); return 0; } -static int generic_pcm2_cleanup(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) +static int dyn_adc_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) { - struct hda_gspec *spec = codec->spec; - - snd_hda_codec_cleanup_stream(codec, hinfo->nid); - snd_hda_codec_cleanup_stream(codec, spec->dac_node[1]->nid); + struct hda_gen_spec *spec = codec->spec; + snd_hda_codec_cleanup_stream(codec, spec->cur_adc); + spec->cur_adc = 0; return 0; } -static int build_generic_pcms(struct hda_codec *codec) +static const struct hda_pcm_stream dyn_adc_pcm_analog_capture = { + .substreams = 1, + .channels_min = 2, + .channels_max = 2, + .nid = 0, /* fill later */ + .ops = { + .prepare = dyn_adc_capture_pcm_prepare, + .cleanup = dyn_adc_capture_pcm_cleanup + }, +}; + +/* build PCM streams based on the parsed results */ +int snd_hda_gen_build_pcms(struct hda_codec *codec) { - struct hda_gspec *spec = codec->spec; - struct hda_pcm *info = &spec->pcm_rec; + struct hda_gen_spec *spec = codec->spec; + struct hda_pcm *info = spec->pcm_rec; + const struct hda_pcm_stream *p; + bool have_multi_adcs; + int i; + + codec->num_pcms = 1; + codec->pcm_info = info; + + if (spec->no_analog) + goto skip_analog; + + snprintf(spec->stream_name_analog, sizeof(spec->stream_name_analog), + "%s Analog", codec->chip_name); + info->name = spec->stream_name_analog; + + if (spec->multiout.num_dacs > 0) { + p = spec->stream_analog_playback; + if (!p) + p = &pcm_analog_playback; + info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *p; + info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dac_nids[0]; + info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = + spec->multiout.max_channels; + if (spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT && + spec->autocfg.line_outs == 2) + info->stream[SNDRV_PCM_STREAM_PLAYBACK].chmap = + snd_pcm_2_1_chmaps; + } + if (spec->num_adc_nids) { + p = spec->stream_analog_capture; + if (!p) { + if (spec->dyn_adc_switch) + p = &dyn_adc_pcm_analog_capture; + else + p = &pcm_analog_capture; + } + info->stream[SNDRV_PCM_STREAM_CAPTURE] = *p; + info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0]; + } + + if (spec->channel_mode) { + info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = 0; + for (i = 0; i < spec->num_channel_mode; i++) { + if (spec->channel_mode[i].channels > info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max) { + info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = spec->channel_mode[i].channels; + } + } + } + + skip_analog: + /* SPDIF for stream index #1 */ + if (spec->multiout.dig_out_nid || spec->dig_in_nid) { + snprintf(spec->stream_name_digital, + sizeof(spec->stream_name_digital), + "%s Digital", codec->chip_name); + codec->num_pcms = 2; + codec->slave_dig_outs = spec->multiout.slave_dig_outs; + info = spec->pcm_rec + 1; + info->name = spec->stream_name_digital; + if (spec->dig_out_type) + info->pcm_type = spec->dig_out_type; + else + info->pcm_type = HDA_PCM_TYPE_SPDIF; + if (spec->multiout.dig_out_nid) { + p = spec->stream_digital_playback; + if (!p) + p = &pcm_digital_playback; + info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *p; + info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dig_out_nid; + } + if (spec->dig_in_nid) { + p = spec->stream_digital_capture; + if (!p) + p = &pcm_digital_capture; + info->stream[SNDRV_PCM_STREAM_CAPTURE] = *p; + info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in_nid; + } + } - if (! spec->dac_node[0] && ! spec->adc_node) { - snd_printd("hda_generic: no PCM found\n"); + if (spec->no_analog) return 0; + + /* If the use of more than one ADC is requested for the current + * model, configure a second analog capture-only PCM. + */ + have_multi_adcs = (spec->num_adc_nids > 1) && + !spec->dyn_adc_switch && !spec->auto_mic; + /* Additional Analaog capture for index #2 */ + if (spec->alt_dac_nid || have_multi_adcs) { + codec->num_pcms = 3; + info = spec->pcm_rec + 2; + info->name = spec->stream_name_analog; + if (spec->alt_dac_nid) { + p = spec->stream_analog_alt_playback; + if (!p) + p = &pcm_analog_alt_playback; + info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *p; + info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = + spec->alt_dac_nid; + } else { + info->stream[SNDRV_PCM_STREAM_PLAYBACK] = + pcm_null_stream; + info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = 0; + } + if (have_multi_adcs) { + p = spec->stream_analog_alt_capture; + if (!p) + p = &pcm_analog_alt_capture; + info->stream[SNDRV_PCM_STREAM_CAPTURE] = *p; + info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = + spec->adc_nids[1]; + info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = + spec->num_adc_nids - 1; + } else { + info->stream[SNDRV_PCM_STREAM_CAPTURE] = + pcm_null_stream; + info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = 0; + } } - codec->num_pcms = 1; - codec->pcm_info = info; + return 0; +} +EXPORT_SYMBOL_HDA(snd_hda_gen_build_pcms); + + +/* + * Standard auto-parser initializations + */ + +/* configure the path from the given dac to the pin as the proper output */ +static void set_output_and_unmute(struct hda_codec *codec, hda_nid_t pin, + int pin_type, hda_nid_t dac) +{ + struct nid_path *path; + + snd_hda_set_pin_ctl_cache(codec, pin, pin_type); + path = snd_hda_get_nid_path(codec, dac, pin); + if (!path) + return; + if (path->active) + return; + snd_hda_activate_path(codec, path, true, true); +} + +/* initialize primary output paths */ +static void init_multi_out(struct hda_codec *codec) +{ + struct hda_gen_spec *spec = codec->spec; + int pin_type; + int i; + + if (spec->autocfg.line_out_type == AUTO_PIN_HP_OUT) + pin_type = PIN_HP; + else + pin_type = PIN_OUT; + + for (i = 0; i <= HDA_SIDE; i++) { + hda_nid_t nid = spec->autocfg.line_out_pins[i]; + if (nid) + set_output_and_unmute(codec, nid, pin_type, + spec->multiout.dac_nids[i]); + + } +} + +/* initialize hp and speaker paths */ +static void init_extra_out(struct hda_codec *codec) +{ + struct hda_gen_spec *spec = codec->spec; + int i; + hda_nid_t pin, dac; + + for (i = 0; i < spec->autocfg.hp_outs; i++) { + if (spec->autocfg.line_out_type == AUTO_PIN_HP_OUT) + break; + pin = spec->autocfg.hp_pins[i]; + if (!pin) + break; + dac = spec->multiout.hp_out_nid[i]; + if (!dac) { + if (i > 0 && spec->multiout.hp_out_nid[0]) + dac = spec->multiout.hp_out_nid[0]; + else + dac = spec->multiout.dac_nids[0]; + } + set_output_and_unmute(codec, pin, PIN_HP, dac); + } + for (i = 0; i < spec->autocfg.speaker_outs; i++) { + if (spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT) + break; + pin = spec->autocfg.speaker_pins[i]; + if (!pin) + break; + dac = spec->multiout.extra_out_nid[i]; + if (!dac) { + if (i > 0 && spec->multiout.extra_out_nid[0]) + dac = spec->multiout.extra_out_nid[0]; + else + dac = spec->multiout.dac_nids[0]; + } + set_output_and_unmute(codec, pin, PIN_OUT, dac); + } +} + +/* initialize multi-io paths */ +static void init_multi_io(struct hda_codec *codec) +{ + struct hda_gen_spec *spec = codec->spec; + int i; + + for (i = 0; i < spec->multi_ios; i++) { + hda_nid_t pin = spec->multi_io[i].pin; + struct nid_path *path; + path = snd_hda_get_nid_path(codec, spec->multi_io[i].dac, pin); + if (!path) + continue; + if (!spec->multi_io[i].ctl_in) + spec->multi_io[i].ctl_in = + snd_hda_codec_update_cache(codec, pin, 0, + AC_VERB_GET_PIN_WIDGET_CONTROL, 0); + snd_hda_activate_path(codec, path, path->active, true); + } +} + +/* set up the input pin config, depending on the given auto-pin type */ +static void set_input_pin(struct hda_codec *codec, hda_nid_t nid, + int auto_pin_type) +{ + unsigned int val = PIN_IN; + if (auto_pin_type == AUTO_PIN_MIC) + val |= snd_hda_get_default_vref(codec, nid); + snd_hda_set_pin_ctl(codec, nid, val); +} + +/* set up input pins and loopback paths */ +static void init_analog_input(struct hda_codec *codec) +{ + struct hda_gen_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + int i; + + for (i = 0; i < cfg->num_inputs; i++) { + hda_nid_t nid = cfg->inputs[i].pin; + if (is_input_pin(codec, nid)) + set_input_pin(codec, nid, cfg->inputs[i].type); + + /* init loopback inputs */ + if (spec->mixer_nid) { + struct nid_path *path; + path = snd_hda_get_nid_path(codec, nid, spec->mixer_nid); + if (path) + snd_hda_activate_path(codec, path, + path->active, false); + } + } +} + +/* initialize ADC paths */ +static void init_input_src(struct hda_codec *codec) +{ + struct hda_gen_spec *spec = codec->spec; + struct hda_input_mux *imux = &spec->input_mux; + struct nid_path *path; + int i, c, nums; - info->name = "HDA Generic"; - if (spec->dac_node[0]) { - info->stream[0] = generic_pcm_playback; - info->stream[0].nid = spec->dac_node[0]->nid; - if (spec->dac_node[1]) { - info->stream[0].ops.prepare = generic_pcm2_prepare; - info->stream[0].ops.cleanup = generic_pcm2_cleanup; + if (spec->dyn_adc_switch) + nums = 1; + else + nums = spec->num_adc_nids; + + for (c = 0; c < nums; c++) { + for (i = 0; i < imux->num_items; i++) { + path = snd_hda_get_nid_path(codec, spec->imux_pins[i], + get_adc_nid(codec, c, i)); + if (path) { + bool active = path->active; + if (i == spec->cur_mux[c]) + active = true; + snd_hda_activate_path(codec, path, active, false); + } } } - if (spec->adc_node) { - info->stream[1] = generic_pcm_playback; - info->stream[1].nid = spec->adc_node->nid; + + if (spec->shared_mic_hp) + update_shared_mic_hp(codec, spec->cur_mux[0]); + + if (spec->cap_sync_hook) + spec->cap_sync_hook(codec); +} + +/* set right pin controls for digital I/O */ +static void init_digital(struct hda_codec *codec) +{ + struct hda_gen_spec *spec = codec->spec; + int i; + hda_nid_t pin; + + for (i = 0; i < spec->autocfg.dig_outs; i++) { + pin = spec->autocfg.dig_out_pins[i]; + if (!pin) + continue; + set_output_and_unmute(codec, pin, PIN_OUT, 0); } + pin = spec->autocfg.dig_in_pin; + if (pin) + snd_hda_set_pin_ctl(codec, pin, PIN_IN); +} + +int snd_hda_gen_init(struct hda_codec *codec) +{ + struct hda_gen_spec *spec = codec->spec; + + if (spec->init_hook) + spec->init_hook(codec); + + snd_hda_apply_verbs(codec); + + init_multi_out(codec); + init_extra_out(codec); + init_multi_io(codec); + init_analog_input(codec); + init_input_src(codec); + init_digital(codec); + /* call init functions of standard auto-mute helpers */ + hp_automute(codec, NULL); + line_automute(codec, NULL); + mic_autoswitch(codec, NULL); + + if (spec->vmaster_mute.sw_kctl && spec->vmaster_mute.hook) + snd_hda_sync_vmaster_hook(&spec->vmaster_mute); + + hda_call_check_power_status(codec, 0x01); return 0; } +EXPORT_SYMBOL(snd_hda_gen_init); + + +/* + * the generic codec support + */ #ifdef CONFIG_PM static int generic_check_power_status(struct hda_codec *codec, hda_nid_t nid) { - struct hda_gspec *spec = codec->spec; + struct hda_gen_spec *spec = codec->spec; return snd_hda_check_amp_list_power(codec, &spec->loopback, nid); } #endif +static void generic_free(struct hda_codec *codec) +{ + snd_hda_gen_spec_free(codec->spec); + kfree(codec->spec); + codec->spec = NULL; +} -/* - */ -static struct hda_codec_ops generic_patch_ops = { - .build_controls = build_generic_controls, - .build_pcms = build_generic_pcms, - .free = snd_hda_generic_free, +static const struct hda_codec_ops generic_patch_ops = { + .build_controls = snd_hda_gen_build_controls, + .build_pcms = snd_hda_gen_build_pcms, + .init = snd_hda_gen_init, + .free = generic_free, + .unsol_event = snd_hda_jack_unsol_event, #ifdef CONFIG_PM .check_power_status = generic_check_power_status, #endif }; -/* - * the generic parser - */ int snd_hda_parse_generic_codec(struct hda_codec *codec) { - struct hda_gspec *spec; + struct hda_gen_spec *spec; int err; - if(!codec->afg) - return 0; - spec = kzalloc(sizeof(*spec), GFP_KERNEL); - if (spec == NULL) { - printk(KERN_ERR "hda_generic: can't allocate spec\n"); + if (!spec) return -ENOMEM; - } + snd_hda_gen_spec_init(spec); codec->spec = spec; - INIT_LIST_HEAD(&spec->nid_list); - - if ((err = build_afg_tree(codec)) < 0) - goto error; - if ((err = parse_input(codec)) < 0 || - (err = parse_output(codec)) < 0) + err = snd_hda_gen_parse_auto_config(codec, NULL); + if (err < 0) goto error; codec->patch_ops = generic_patch_ops; - return 0; - error: - snd_hda_generic_free(codec); +error: + generic_free(codec); return err; } EXPORT_SYMBOL(snd_hda_parse_generic_codec); diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h new file mode 100644 index 000000000000..a9f4f63b4894 --- /dev/null +++ b/sound/pci/hda/hda_generic.h @@ -0,0 +1,199 @@ +/* + * Generic BIOS auto-parser helper functions for HD-audio + * + * Copyright (c) 2012 Takashi Iwai + * + * This driver is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef __SOUND_HDA_GENERIC_H +#define __SOUND_HDA_GENERIC_H + +/* unsol event tags */ +enum { + HDA_GEN_HP_EVENT, HDA_GEN_FRONT_EVENT, HDA_GEN_MIC_EVENT, + HDA_GEN_LAST_EVENT = HDA_GEN_MIC_EVENT +}; + +/* table entry for multi-io paths */ +struct hda_multi_io { + hda_nid_t pin; /* multi-io widget pin NID */ + hda_nid_t dac; /* DAC to be connected */ + unsigned int ctl_in; /* cached input-pin control value */ +}; + +/* Widget connection path + * + * For output, stored in the order of DAC -> ... -> pin, + * for input, pin -> ... -> ADC. + * + * idx[i] contains the source index number to select on of the widget path[i]; + * e.g. idx[1] is the index of the DAC (path[0]) selected by path[1] widget + * multi[] indicates whether it's a selector widget with multi-connectors + * (i.e. the connection selection is mandatory) + * vol_ctl and mute_ctl contains the NIDs for the assigned mixers + */ + +#define MAX_NID_PATH_DEPTH 5 + +enum { + NID_PATH_VOL_CTL, + NID_PATH_MUTE_CTL, + NID_PATH_BOOST_CTL, + NID_PATH_NUM_CTLS +}; + +struct nid_path { + int depth; + hda_nid_t path[MAX_NID_PATH_DEPTH]; + unsigned char idx[MAX_NID_PATH_DEPTH]; + unsigned char multi[MAX_NID_PATH_DEPTH]; + unsigned int ctls[NID_PATH_NUM_CTLS]; /* NID_PATH_XXX_CTL */ + bool active; +}; + +/* mic/line-in auto switching entry */ + +#define MAX_AUTO_MIC_PINS 3 + +struct automic_entry { + hda_nid_t pin; /* pin */ + int idx; /* imux index, -1 = invalid */ + unsigned int attr; /* pin attribute (INPUT_PIN_ATTR_*) */ +}; + +struct hda_gen_spec { + char stream_name_analog[32]; /* analog PCM stream */ + const struct hda_pcm_stream *stream_analog_playback; + const struct hda_pcm_stream *stream_analog_capture; + const struct hda_pcm_stream *stream_analog_alt_playback; + const struct hda_pcm_stream *stream_analog_alt_capture; + + char stream_name_digital[32]; /* digital PCM stream */ + const struct hda_pcm_stream *stream_digital_playback; + const struct hda_pcm_stream *stream_digital_capture; + + /* playback */ + struct hda_multi_out multiout; /* playback set-up + * max_channels, dacs must be set + * dig_out_nid and hp_nid are optional + */ + hda_nid_t alt_dac_nid; + hda_nid_t slave_dig_outs[3]; /* optional - for auto-parsing */ + int dig_out_type; + + /* capture */ + unsigned int num_adc_nids; + hda_nid_t adc_nids[AUTO_CFG_MAX_OUTS]; + hda_nid_t dig_in_nid; /* digital-in NID; optional */ + hda_nid_t mixer_nid; /* analog-mixer NID */ + + /* capture setup for dynamic dual-adc switch */ + hda_nid_t cur_adc; + unsigned int cur_adc_stream_tag; + unsigned int cur_adc_format; + + /* capture source */ + struct hda_input_mux input_mux; + unsigned int cur_mux[3]; + + /* channel model */ + const struct hda_channel_mode *channel_mode; + int num_channel_mode; + int const_channel_count; /* min. channel count (for speakers) */ + int ext_channel_count; /* current channel count for multi-io */ + + /* PCM information */ + struct hda_pcm pcm_rec[3]; /* used in build_pcms() */ + + /* dynamic controls, init_verbs and input_mux */ + struct auto_pin_cfg autocfg; + struct snd_array kctls; + hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS]; + hda_nid_t imux_pins[HDA_MAX_NUM_INPUTS]; + unsigned int dyn_adc_idx[HDA_MAX_NUM_INPUTS]; + hda_nid_t shared_mic_vref_pin; + + /* DAC list */ + int num_all_dacs; + hda_nid_t all_dacs[16]; + + /* path list */ + struct snd_array paths; + + /* auto-mic stuff */ + int am_num_entries; + struct automic_entry am_entry[MAX_AUTO_MIC_PINS]; + + /* for pin sensing */ + unsigned int hp_jack_present:1; + unsigned int line_jack_present:1; + unsigned int master_mute:1; + unsigned int auto_mic:1; + unsigned int automute_speaker:1; /* automute speaker outputs */ + unsigned int automute_lo:1; /* automute LO outputs */ + unsigned int detect_hp:1; /* Headphone detection enabled */ + unsigned int detect_lo:1; /* Line-out detection enabled */ + unsigned int automute_speaker_possible:1; /* there are speakers and either LO or HP */ + unsigned int automute_lo_possible:1; /* there are line outs and HP */ + unsigned int keep_vref_in_automute:1; /* Don't clear VREF in automute */ + unsigned int line_in_auto_switch:1; /* allow line-in auto switch */ + + /* other flags */ + unsigned int need_dac_fix:1; /* need to limit DACs for multi channels */ + unsigned int no_analog:1; /* digital I/O only */ + unsigned int dyn_adc_switch:1; /* switch ADCs (for ALC275) */ + unsigned int shared_mic_hp:1; /* HP/Mic-in sharing */ + unsigned int no_primary_hp:1; /* Don't prefer HP pins to speaker pins */ + unsigned int multi_cap_vol:1; /* allow multiple capture xxx volumes */ + unsigned int inv_dmic_split:1; /* inverted dmic w/a for conexant */ + + unsigned int parse_flags; /* passed to snd_hda_parse_pin_defcfg() */ + + /* for virtual master */ + hda_nid_t vmaster_nid; + struct hda_vmaster_mute_hook vmaster_mute; +#ifdef CONFIG_PM + struct hda_loopback_check loopback; + int num_loopbacks; + struct hda_amp_list loopback_list[8]; +#endif + + /* multi-io */ + int multi_ios; + struct hda_multi_io multi_io[4]; + + /* bind volumes */ + struct snd_array bind_ctls; + + /* hooks */ + void (*init_hook)(struct hda_codec *codec); + void (*automute_hook)(struct hda_codec *codec); + void (*cap_sync_hook)(struct hda_codec *codec); +}; + +int snd_hda_gen_spec_init(struct hda_gen_spec *spec); +void snd_hda_gen_spec_free(struct hda_gen_spec *spec); + +int snd_hda_gen_init(struct hda_codec *codec); + +struct nid_path *snd_hda_get_nid_path(struct hda_codec *codec, + hda_nid_t from_nid, hda_nid_t to_nid); +bool snd_hda_parse_nid_path(struct hda_codec *codec, hda_nid_t from_nid, + hda_nid_t to_nid, int with_aa_mix, + struct nid_path *path); +struct nid_path * +snd_hda_add_new_path(struct hda_codec *codec, hda_nid_t from_nid, + hda_nid_t to_nid, int with_aa_mix); +void snd_hda_activate_path(struct hda_codec *codec, struct nid_path *path, + bool enable, bool add_aamix); + +int snd_hda_gen_parse_auto_config(struct hda_codec *codec, + const hda_nid_t *ignore_nids); +int snd_hda_gen_build_controls(struct hda_codec *codec); +int snd_hda_gen_build_pcms(struct hda_codec *codec); + +#endif /* __SOUND_HDA_GENERIC_H */ -- cgit v1.2.3 From 731dc3019c7b9fa7a787f9b0a74e94638eee3fe1 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 19 Dec 2012 13:01:54 +0100 Subject: ALSA: hda - Add EAPD control to generic parser Enable EAPD in output path initializations automatically unless the new flag spec->own_eapd_ctl is set. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 6 ++++++ sound/pci/hda/hda_generic.h | 1 + 2 files changed, 7 insertions(+) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 2d19b915dbf1..31c5677e6210 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -3333,6 +3333,7 @@ EXPORT_SYMBOL_HDA(snd_hda_gen_build_pcms); static void set_output_and_unmute(struct hda_codec *codec, hda_nid_t pin, int pin_type, hda_nid_t dac) { + struct hda_gen_spec *spec = codec->spec; struct nid_path *path; snd_hda_set_pin_ctl_cache(codec, pin, pin_type); @@ -3342,6 +3343,11 @@ static void set_output_and_unmute(struct hda_codec *codec, hda_nid_t pin, if (path->active) return; snd_hda_activate_path(codec, path, true, true); + + if (!spec->own_eapd_ctl && + (snd_hda_query_pin_caps(codec, pin) & AC_PINCAP_EAPD)) + snd_hda_codec_update_cache(codec, pin, 0, + AC_VERB_SET_EAPD_BTLENABLE, 0x02); } /* initialize primary output paths */ diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h index a9f4f63b4894..9c00bd5863e5 100644 --- a/sound/pci/hda/hda_generic.h +++ b/sound/pci/hda/hda_generic.h @@ -150,6 +150,7 @@ struct hda_gen_spec { unsigned int no_primary_hp:1; /* Don't prefer HP pins to speaker pins */ unsigned int multi_cap_vol:1; /* allow multiple capture xxx volumes */ unsigned int inv_dmic_split:1; /* inverted dmic w/a for conexant */ + unsigned int own_eapd_ctl:1; /* set EAPD by own function */ unsigned int parse_flags; /* passed to snd_hda_parse_pin_defcfg() */ -- cgit v1.2.3 From 12c93df60ccf926f8798723f97f9f45175fce85b Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 19 Dec 2012 14:38:33 +0100 Subject: ALSA: hda - Export snd_hda_gen_add_kctl() It may be used in other codec drivers, so let it free. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 19 ++++++++++--------- sound/pci/hda/hda_generic.h | 4 ++++ 2 files changed, 14 insertions(+), 9 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 31c5677e6210..49e968c8140b 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -43,9 +43,9 @@ int snd_hda_gen_spec_init(struct hda_gen_spec *spec) } EXPORT_SYMBOL_HDA(snd_hda_gen_spec_init); -static struct snd_kcontrol_new * -add_kctl(struct hda_gen_spec *spec, const char *name, - const struct snd_kcontrol_new *temp) +struct snd_kcontrol_new * +snd_hda_gen_add_kctl(struct hda_gen_spec *spec, const char *name, + const struct snd_kcontrol_new *temp) { struct snd_kcontrol_new *knew = snd_array_new(&spec->kctls); if (!knew) @@ -59,6 +59,7 @@ add_kctl(struct hda_gen_spec *spec, const char *name, return NULL; return knew; } +EXPORT_SYMBOL_HDA(snd_hda_gen_add_kctl); static void free_kctls(struct hda_gen_spec *spec) { @@ -548,7 +549,7 @@ static int add_control(struct hda_gen_spec *spec, int type, const char *name, { struct snd_kcontrol_new *knew; - knew = add_kctl(spec, name, &control_templates[type]); + knew = snd_hda_gen_add_kctl(spec, name, &control_templates[type]); if (!knew) return -ENOMEM; knew->index = cidx; @@ -1527,7 +1528,7 @@ static int create_multi_channel_mode(struct hda_codec *codec) struct hda_gen_spec *spec = codec->spec; if (spec->multi_ios > 0) { - if (!add_kctl(spec, NULL, &channel_mode_enum)) + if (!snd_hda_gen_add_kctl(spec, NULL, &channel_mode_enum)) return -ENOMEM; } return 0; @@ -2086,7 +2087,7 @@ static int create_bind_cap_vol_ctl(struct hda_codec *codec, int idx, struct snd_kcontrol_new *knew; if (vol_ctl) { - knew = add_kctl(spec, NULL, &cap_vol_temp); + knew = snd_hda_gen_add_kctl(spec, NULL, &cap_vol_temp); if (!knew) return -ENOMEM; knew->index = idx; @@ -2094,7 +2095,7 @@ static int create_bind_cap_vol_ctl(struct hda_codec *codec, int idx, knew->subdevice = HDA_SUBDEV_AMP_FLAG; } if (sw_ctl) { - knew = add_kctl(spec, NULL, &cap_sw_temp); + knew = snd_hda_gen_add_kctl(spec, NULL, &cap_sw_temp); if (!knew) return -ENOMEM; knew->index = idx; @@ -2171,7 +2172,7 @@ static int create_capture_mixers(struct hda_codec *codec) if (!spec->auto_mic && imux->num_items > 1) { struct snd_kcontrol_new *knew; - knew = add_kctl(spec, NULL, &cap_src_temp); + knew = snd_hda_gen_add_kctl(spec, NULL, &cap_src_temp); if (!knew) return -ENOMEM; knew->count = nums; @@ -2592,7 +2593,7 @@ static int add_automute_mode_enum(struct hda_codec *codec) { struct hda_gen_spec *spec = codec->spec; - if (!add_kctl(spec, NULL, &automute_mode_enum)) + if (!snd_hda_gen_add_kctl(spec, NULL, &automute_mode_enum)) return -ENOMEM; return 0; } diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h index 9c00bd5863e5..d71e86de9060 100644 --- a/sound/pci/hda/hda_generic.h +++ b/sound/pci/hda/hda_generic.h @@ -192,6 +192,10 @@ snd_hda_add_new_path(struct hda_codec *codec, hda_nid_t from_nid, void snd_hda_activate_path(struct hda_codec *codec, struct nid_path *path, bool enable, bool add_aamix); +struct snd_kcontrol_new * +snd_hda_gen_add_kctl(struct hda_gen_spec *spec, const char *name, + const struct snd_kcontrol_new *temp); + int snd_hda_gen_parse_auto_config(struct hda_codec *codec, const hda_nid_t *ignore_nids); int snd_hda_gen_build_controls(struct hda_codec *codec); -- cgit v1.2.3 From 9eb413e5e4801753f7851ec6c46528adcc15579f Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 19 Dec 2012 14:41:21 +0100 Subject: ALSA: hda - Move the call of snd_hda_parse_pin_defcfg() from snd_hda_gen_parse_auto_config() In some cases, we want to manipulate the auto_pin_cfg table before passing to snd_hda_gen_parse_auto_config() (e.g. Realtek SSID check code fiddles with the headphone pin). Also passing ignore_pins just for snd_hda_parse_pin_defcfg() isn't good. In this patch, snd_hda_gen_parse_auto_config() is changed to receive the auto_pin_cfg table to be parsed. The passed auto_pin_cfg table must have been initialized (typically by calling snd_hda_gen_parse_auto_config()) beforehand by the caller. Also together with this change, spec->parse_flags is also removed. Since this was referred only at the place calling snd_hda_parse_pin_defcfg(), no longer needed to be kept in spec. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 24 +++++++++++++++--------- sound/pci/hda/hda_generic.h | 4 +--- 2 files changed, 16 insertions(+), 12 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 49e968c8140b..e512cab22e91 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -2785,21 +2785,23 @@ static int check_auto_mic_availability(struct hda_codec *codec) } -/* parse the BIOS configuration and set up the hda_gen_spec */ -/* return 1 if successful, 0 if the proper config is not found, +/* + * Parse the given BIOS configuration and set up the hda_gen_spec + * + * return 1 if successful, 0 if the proper config is not found, * or a negative error code */ int snd_hda_gen_parse_auto_config(struct hda_codec *codec, - const hda_nid_t *ignore_nids) + struct auto_pin_cfg *cfg) { struct hda_gen_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; int err; - err = snd_hda_parse_pin_defcfg(codec, cfg, ignore_nids, - spec->parse_flags); - if (err < 0) - return err; + if (cfg != &spec->autocfg) { + spec->autocfg = *cfg; + cfg = &spec->autocfg; + } + if (!cfg->line_outs) { if (cfg->dig_outs || cfg->dig_in_pin) { spec->multiout.max_channels = 2; @@ -3586,7 +3588,11 @@ int snd_hda_parse_generic_codec(struct hda_codec *codec) snd_hda_gen_spec_init(spec); codec->spec = spec; - err = snd_hda_gen_parse_auto_config(codec, NULL); + err = snd_hda_parse_pin_defcfg(codec, &spec->autocfg, NULL, 0); + if (err < 0) + return err; + + err = snd_hda_gen_parse_auto_config(codec, &spec->autocfg); if (err < 0) goto error; diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h index d71e86de9060..1a3b4042c5ba 100644 --- a/sound/pci/hda/hda_generic.h +++ b/sound/pci/hda/hda_generic.h @@ -152,8 +152,6 @@ struct hda_gen_spec { unsigned int inv_dmic_split:1; /* inverted dmic w/a for conexant */ unsigned int own_eapd_ctl:1; /* set EAPD by own function */ - unsigned int parse_flags; /* passed to snd_hda_parse_pin_defcfg() */ - /* for virtual master */ hda_nid_t vmaster_nid; struct hda_vmaster_mute_hook vmaster_mute; @@ -197,7 +195,7 @@ snd_hda_gen_add_kctl(struct hda_gen_spec *spec, const char *name, const struct snd_kcontrol_new *temp); int snd_hda_gen_parse_auto_config(struct hda_codec *codec, - const hda_nid_t *ignore_nids); + struct auto_pin_cfg *cfg); int snd_hda_gen_build_controls(struct hda_codec *codec); int snd_hda_gen_build_pcms(struct hda_codec *codec); -- cgit v1.2.3 From 36502d020030665bcfc558767cbb0ddf87b9892f Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 19 Dec 2012 15:15:10 +0100 Subject: ALSA: hda - Fix NULL dereference in snd_hda_gen_build_controls() When no controls are assigned in the parser (e.g. no analog path), spec->kctls.list is still NULL. We need to check it before passing to snd_hda_add_new_ctls(). Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index e512cab22e91..364ec06071ae 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -2906,9 +2906,11 @@ int snd_hda_gen_build_controls(struct hda_codec *codec) struct hda_gen_spec *spec = codec->spec; int err; - err = snd_hda_add_new_ctls(codec, spec->kctls.list); - if (err < 0) - return err; + if (spec->kctls.used) { + err = snd_hda_add_new_ctls(codec, spec->kctls.list); + if (err < 0) + return err; + } if (spec->multiout.dig_out_nid) { err = snd_hda_create_dig_out_ctls(codec, -- cgit v1.2.3 From 5d550e15be0a960c7ff5dbcf12b5a454e70403b6 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 19 Dec 2012 15:16:44 +0100 Subject: ALSA: hda - Export standard jack event handlers for generic parser These handlers are supposed to be called externally from the codec drivers once when they need to handle own jack events. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 26 +++++++++++++++----------- sound/pci/hda/hda_generic.h | 9 +++++++++ 2 files changed, 24 insertions(+), 11 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 364ec06071ae..6914d70df09a 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -2414,7 +2414,7 @@ static void do_automute(struct hda_codec *codec, int num_pins, hda_nid_t *pins, } /* Toggle outputs muting */ -static void update_outputs(struct hda_codec *codec) +void snd_hda_gen_update_outputs(struct hda_codec *codec) { struct hda_gen_spec *spec = codec->spec; int on; @@ -2448,6 +2448,7 @@ static void update_outputs(struct hda_codec *codec) do_automute(codec, ARRAY_SIZE(spec->autocfg.line_out_pins), spec->autocfg.line_out_pins, on, false); } +EXPORT_SYMBOL_HDA(snd_hda_gen_update_outputs); static void call_update_outputs(struct hda_codec *codec) { @@ -2455,11 +2456,11 @@ static void call_update_outputs(struct hda_codec *codec) if (spec->automute_hook) spec->automute_hook(codec); else - update_outputs(codec); + snd_hda_gen_update_outputs(codec); } /* standard HP-automute helper */ -static void hp_automute(struct hda_codec *codec, struct hda_jack_tbl *jack) +void snd_hda_gen_hp_automute(struct hda_codec *codec, struct hda_jack_tbl *jack) { struct hda_gen_spec *spec = codec->spec; @@ -2470,9 +2471,10 @@ static void hp_automute(struct hda_codec *codec, struct hda_jack_tbl *jack) return; call_update_outputs(codec); } +EXPORT_SYMBOL_HDA(snd_hda_gen_hp_automute); /* standard line-out-automute helper */ -static void line_automute(struct hda_codec *codec, struct hda_jack_tbl *jack) +void snd_hda_gen_line_automute(struct hda_codec *codec, struct hda_jack_tbl *jack) { struct hda_gen_spec *spec = codec->spec; @@ -2489,9 +2491,10 @@ static void line_automute(struct hda_codec *codec, struct hda_jack_tbl *jack) return; call_update_outputs(codec); } +EXPORT_SYMBOL_HDA(snd_hda_gen_line_automute); /* standard mic auto-switch helper */ -static void mic_autoswitch(struct hda_codec *codec, struct hda_jack_tbl *jack) +void snd_hda_gen_mic_autoswitch(struct hda_codec *codec, struct hda_jack_tbl *jack) { struct hda_gen_spec *spec = codec->spec; int i; @@ -2507,6 +2510,7 @@ static void mic_autoswitch(struct hda_codec *codec, struct hda_jack_tbl *jack) } mux_select(codec, 0, spec->am_entry[0].idx); } +EXPORT_SYMBOL_HDA(snd_hda_gen_mic_autoswitch); /* * Auto-Mute mode mixer enum support @@ -2639,7 +2643,7 @@ static int check_auto_mute_availability(struct hda_codec *codec) snd_printdd("hda-codec: Enable HP auto-muting on NID 0x%x\n", nid); snd_hda_jack_detect_enable_callback(codec, nid, HDA_GEN_HP_EVENT, - hp_automute); + snd_hda_gen_hp_automute); spec->detect_hp = 1; } @@ -2652,7 +2656,7 @@ static int check_auto_mute_availability(struct hda_codec *codec) snd_printdd("hda-codec: Enable Line-Out auto-muting on NID 0x%x\n", nid); snd_hda_jack_detect_enable_callback(codec, nid, HDA_GEN_FRONT_EVENT, - line_automute); + snd_hda_gen_line_automute); spec->detect_lo = 1; } spec->automute_lo_possible = spec->detect_hp; @@ -2704,7 +2708,7 @@ static bool auto_mic_check_imux(struct hda_codec *codec) snd_hda_jack_detect_enable_callback(codec, spec->am_entry[i].pin, HDA_GEN_MIC_EVENT, - mic_autoswitch); + snd_hda_gen_mic_autoswitch); return true; } @@ -3536,9 +3540,9 @@ int snd_hda_gen_init(struct hda_codec *codec) init_digital(codec); /* call init functions of standard auto-mute helpers */ - hp_automute(codec, NULL); - line_automute(codec, NULL); - mic_autoswitch(codec, NULL); + snd_hda_gen_hp_automute(codec, NULL); + snd_hda_gen_line_automute(codec, NULL); + snd_hda_gen_mic_autoswitch(codec, NULL); if (spec->vmaster_mute.sw_kctl && spec->vmaster_mute.hook) snd_hda_sync_vmaster_hook(&spec->vmaster_mute); diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h index 1a3b4042c5ba..417ab65a323e 100644 --- a/sound/pci/hda/hda_generic.h +++ b/sound/pci/hda/hda_generic.h @@ -199,4 +199,13 @@ int snd_hda_gen_parse_auto_config(struct hda_codec *codec, int snd_hda_gen_build_controls(struct hda_codec *codec); int snd_hda_gen_build_pcms(struct hda_codec *codec); +/* standard jack event callbacks */ +void snd_hda_gen_hp_automute(struct hda_codec *codec, + struct hda_jack_tbl *jack); +void snd_hda_gen_line_automute(struct hda_codec *codec, + struct hda_jack_tbl *jack); +void snd_hda_gen_mic_autoswitch(struct hda_codec *codec, + struct hda_jack_tbl *jack); +void snd_hda_gen_update_outputs(struct hda_codec *codec); + #endif /* __SOUND_HDA_GENERIC_H */ -- cgit v1.2.3 From 08c189f2c5523eda847cddb89eb8f44c0b957431 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 19 Dec 2012 15:22:24 +0100 Subject: ALSA: hda - Use generic parser codes for Realtek driver The next migration step is to use the common code in generic driver for Realtek driver. This is no drastic change and there should be no real functional changes, as the generic parser code comes from Realtek driver originally. As Realtek driver requires the generic parser code, it needs a reverse-selection of CONFIG_SND_HDA_GENERIC kconfig. Signed-off-by: Takashi Iwai --- sound/pci/hda/Kconfig | 1 + sound/pci/hda/patch_realtek.c | 4102 +++-------------------------------------- 2 files changed, 298 insertions(+), 3805 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig index 6eeb8897624b..ebec1b707fb6 100644 --- a/sound/pci/hda/Kconfig +++ b/sound/pci/hda/Kconfig @@ -86,6 +86,7 @@ config SND_HDA_PATCH_LOADER config SND_HDA_CODEC_REALTEK bool "Build Realtek HD-audio codec support" default y + select SND_HDA_GENERIC help Say Y here to include Realtek HD-audio codec support in snd-hda-intel driver, such as ALC880. diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index c85899d50404..896bc2cc9293 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -36,12 +36,10 @@ #include "hda_auto_parser.h" #include "hda_beep.h" #include "hda_jack.h" +#include "hda_generic.h" /* unsol event tags */ -#define ALC_FRONT_EVENT 0x01 -#define ALC_DCVOL_EVENT 0x02 -#define ALC_HP_EVENT 0x04 -#define ALC_MIC_EVENT 0x08 +#define ALC_DCVOL_EVENT 0x08 /* for GPIO Poll */ #define GPIO_MASK 0x03 @@ -68,12 +66,6 @@ struct alc_customize_define { unsigned int fixup:1; /* Means that this sku is set by driver, not read from hw */ }; -struct alc_multi_io { - hda_nid_t pin; /* multi-io widget pin NID */ - hda_nid_t dac; /* DAC to be connected */ - unsigned int ctl_in; /* cached input-pin control value */ -}; - /* make compatible with old code */ #define alc_apply_pincfgs snd_hda_apply_pincfgs #define alc_apply_fixup snd_hda_apply_fixup @@ -91,112 +83,21 @@ struct alc_multi_io { #define ALC_FIXUP_ACT_INIT HDA_FIXUP_ACT_INIT #define ALC_FIXUP_ACT_BUILD HDA_FIXUP_ACT_BUILD -#define MAX_AUTO_MIC_PINS 3 - -struct alc_automic_entry { - hda_nid_t pin; /* pin */ - int idx; /* imux index, -1 = invalid */ - unsigned int attr; /* pin attribute (INPUT_PIN_ATTR_*) */ -}; - -#define MAX_NID_PATH_DEPTH 5 - -enum { - NID_PATH_VOL_CTL, - NID_PATH_MUTE_CTL, - NID_PATH_BOOST_CTL, - NID_PATH_NUM_CTLS -}; - -/* Widget connection path - * - * For output, stored in the order of DAC -> ... -> pin, - * for input, pin -> ... -> ADC. - * - * idx[i] contains the source index number to select on of the widget path[i]; - * e.g. idx[1] is the index of the DAC (path[0]) selected by path[1] widget - * multi[] indicates whether it's a selector widget with multi-connectors - * (i.e. the connection selection is mandatory) - * vol_ctl and mute_ctl contains the NIDs for the assigned mixers - */ -struct nid_path { - int depth; - hda_nid_t path[MAX_NID_PATH_DEPTH]; - unsigned char idx[MAX_NID_PATH_DEPTH]; - unsigned char multi[MAX_NID_PATH_DEPTH]; - unsigned int ctls[NID_PATH_NUM_CTLS]; /* NID_PATH_XXX_CTL */ - bool active; -}; - struct alc_spec { + struct hda_gen_spec gen; /* must be at head */ + /* codec parameterization */ const struct snd_kcontrol_new *mixers[5]; /* mixer arrays */ unsigned int num_mixers; unsigned int beep_amp; /* beep amp value, set via set_beep_amp() */ - char stream_name_analog[32]; /* analog PCM stream */ - const struct hda_pcm_stream *stream_analog_playback; - const struct hda_pcm_stream *stream_analog_capture; - const struct hda_pcm_stream *stream_analog_alt_playback; - const struct hda_pcm_stream *stream_analog_alt_capture; - - char stream_name_digital[32]; /* digital PCM stream */ - const struct hda_pcm_stream *stream_digital_playback; - const struct hda_pcm_stream *stream_digital_capture; - - /* playback */ - struct hda_multi_out multiout; /* playback set-up - * max_channels, dacs must be set - * dig_out_nid and hp_nid are optional - */ - hda_nid_t alt_dac_nid; - hda_nid_t slave_dig_outs[3]; /* optional - for auto-parsing */ - int dig_out_type; - - /* capture */ - unsigned int num_adc_nids; - hda_nid_t adc_nids[AUTO_CFG_MAX_OUTS]; - hda_nid_t dig_in_nid; /* digital-in NID; optional */ - hda_nid_t mixer_nid; /* analog-mixer NID */ - - /* capture setup for dynamic dual-adc switch */ - hda_nid_t cur_adc; - unsigned int cur_adc_stream_tag; - unsigned int cur_adc_format; - - /* capture source */ - struct hda_input_mux input_mux; - unsigned int cur_mux[3]; - - /* channel model */ - const struct hda_channel_mode *channel_mode; - int num_channel_mode; - int const_channel_count; /* min. channel count (for speakers) */ - int ext_channel_count; /* current channel count for multi-io */ - - /* PCM information */ - struct hda_pcm pcm_rec[3]; /* used in alc_build_pcms() */ - - /* dynamic controls, init_verbs and input_mux */ - struct auto_pin_cfg autocfg; struct alc_customize_define cdefine; - struct snd_array kctls; - hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS]; - hda_nid_t imux_pins[HDA_MAX_NUM_INPUTS]; - unsigned int dyn_adc_idx[HDA_MAX_NUM_INPUTS]; - hda_nid_t inv_dmic_pin; - hda_nid_t shared_mic_vref_pin; + unsigned int parse_flags; /* flag for snd_hda_parse_pin_defcfg() */ - /* DAC list */ - int num_all_dacs; - hda_nid_t all_dacs[16]; - - /* path list */ - struct snd_array paths; - - /* auto-mic stuff */ - int am_num_entries; - struct alc_automic_entry am_entry[MAX_AUTO_MIC_PINS]; + /* inverted dmic fix */ + unsigned int inv_dmic_fixup:1; /* has inverted digital-mic workaround */ + unsigned int inv_dmic_muted:1; /* R-ch of inv d-mic is muted? */ + hda_nid_t inv_dmic_pin; /* hooks */ void (*init_hook)(struct hda_codec *codec); @@ -204,224 +105,16 @@ struct alc_spec { void (*power_hook)(struct hda_codec *codec); #endif void (*shutup)(struct hda_codec *codec); - void (*automute_hook)(struct hda_codec *codec); - - /* for pin sensing */ - unsigned int hp_jack_present:1; - unsigned int line_jack_present:1; - unsigned int master_mute:1; - unsigned int auto_mic:1; - unsigned int automute_speaker:1; /* automute speaker outputs */ - unsigned int automute_lo:1; /* automute LO outputs */ - unsigned int detect_hp:1; /* Headphone detection enabled */ - unsigned int detect_lo:1; /* Line-out detection enabled */ - unsigned int automute_speaker_possible:1; /* there are speakers and either LO or HP */ - unsigned int automute_lo_possible:1; /* there are line outs and HP */ - unsigned int keep_vref_in_automute:1; /* Don't clear VREF in automute */ - unsigned int line_in_auto_switch:1; /* allow line-in auto switch */ - - /* other flags */ - unsigned int need_dac_fix:1; /* need to limit DACs for multi channels */ - unsigned int no_analog :1; /* digital I/O only */ - unsigned int dyn_adc_switch:1; /* switch ADCs (for ALC275) */ - unsigned int shared_mic_hp:1; /* HP/Mic-in sharing */ - unsigned int inv_dmic_fixup:1; /* has inverted digital-mic workaround */ - unsigned int inv_dmic_muted:1; /* R-ch of inv d-mic is muted? */ - unsigned int no_primary_hp:1; /* Don't prefer HP pins to speaker pins */ - unsigned int multi_cap_vol:1; /* allow multiple capture xxx volumes */ - unsigned int inv_dmic_split:1; /* inverted dmic w/a for conexant */ - - unsigned int parse_flags; /* passed to snd_hda_parse_pin_defcfg() */ int init_amp; int codec_variant; /* flag for other variants */ - /* for virtual master */ - hda_nid_t vmaster_nid; - struct hda_vmaster_mute_hook vmaster_mute; -#ifdef CONFIG_PM - struct hda_loopback_check loopback; - int num_loopbacks; - struct hda_amp_list loopback_list[8]; -#endif - /* for PLL fix */ hda_nid_t pll_nid; unsigned int pll_coef_idx, pll_coef_bit; unsigned int coef0; - - /* multi-io */ - int multi_ios; - struct alc_multi_io multi_io[4]; - - /* bind volumes */ - struct snd_array bind_ctls; }; -static bool check_amp_caps(struct hda_codec *codec, hda_nid_t nid, - int dir, unsigned int bits) -{ - if (!nid) - return false; - if (get_wcaps(codec, nid) & (1 << (dir + 1))) - if (query_amp_caps(codec, nid, dir) & bits) - return true; - return false; -} - -#define nid_has_mute(codec, nid, dir) \ - check_amp_caps(codec, nid, dir, AC_AMPCAP_MUTE) -#define nid_has_volume(codec, nid, dir) \ - check_amp_caps(codec, nid, dir, AC_AMPCAP_NUM_STEPS) - -static struct nid_path * -get_nid_path(struct hda_codec *codec, hda_nid_t from_nid, hda_nid_t to_nid); -static void activate_path(struct hda_codec *codec, struct nid_path *path, - bool enable, bool add_aamix); - -/* - * input MUX handling - */ -static int alc_mux_enum_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct alc_spec *spec = codec->spec; - return snd_hda_input_mux_info(&spec->input_mux, uinfo); -} - -static int alc_mux_enum_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct alc_spec *spec = codec->spec; - unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); - - ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx]; - return 0; -} - -static hda_nid_t get_adc_nid(struct hda_codec *codec, int adc_idx, int imux_idx) -{ - struct alc_spec *spec = codec->spec; - if (spec->dyn_adc_switch) - adc_idx = spec->dyn_adc_idx[imux_idx]; - return spec->adc_nids[adc_idx]; -} - -static bool alc_dyn_adc_pcm_resetup(struct hda_codec *codec, int cur) -{ - struct alc_spec *spec = codec->spec; - hda_nid_t new_adc = spec->adc_nids[spec->dyn_adc_idx[cur]]; - - if (spec->cur_adc && spec->cur_adc != new_adc) { - /* stream is running, let's swap the current ADC */ - __snd_hda_codec_cleanup_stream(codec, spec->cur_adc, 1); - spec->cur_adc = new_adc; - snd_hda_codec_setup_stream(codec, new_adc, - spec->cur_adc_stream_tag, 0, - spec->cur_adc_format); - return true; - } - return false; -} - -static void call_update_outputs(struct hda_codec *codec); -static void alc_inv_dmic_sync(struct hda_codec *codec, bool force); -static void alc_inv_dmic_sync_adc(struct hda_codec *codec, int adc_idx); - -/* for shared I/O, change the pin-control accordingly */ -static void update_shared_mic_hp(struct hda_codec *codec, bool set_as_mic) -{ - struct alc_spec *spec = codec->spec; - unsigned int val; - hda_nid_t pin = spec->autocfg.inputs[1].pin; - /* NOTE: this assumes that there are only two inputs, the - * first is the real internal mic and the second is HP/mic jack. - */ - - val = snd_hda_get_default_vref(codec, pin); - - /* This pin does not have vref caps - let's enable vref on pin 0x18 - instead, as suggested by Realtek */ - if (val == AC_PINCTL_VREF_HIZ && spec->shared_mic_vref_pin) { - const hda_nid_t vref_pin = spec->shared_mic_vref_pin; - unsigned int vref_val = snd_hda_get_default_vref(codec, vref_pin); - if (vref_val != AC_PINCTL_VREF_HIZ) - snd_hda_set_pin_ctl(codec, vref_pin, PIN_IN | (set_as_mic ? vref_val : 0)); - } - - val = set_as_mic ? val | PIN_IN : PIN_HP; - snd_hda_set_pin_ctl(codec, pin, val); - - spec->automute_speaker = !set_as_mic; - call_update_outputs(codec); -} - -/* select the given imux item; either unmute exclusively or select the route */ -static int alc_mux_select(struct hda_codec *codec, unsigned int adc_idx, - unsigned int idx) -{ - struct alc_spec *spec = codec->spec; - const struct hda_input_mux *imux; - struct nid_path *path; - - imux = &spec->input_mux; - if (!imux->num_items) - return 0; - - if (idx >= imux->num_items) - idx = imux->num_items - 1; - if (spec->cur_mux[adc_idx] == idx) - return 0; - - path = get_nid_path(codec, spec->imux_pins[spec->cur_mux[adc_idx]], - spec->adc_nids[adc_idx]); - if (!path) - return 0; - if (path->active) - activate_path(codec, path, false, false); - - spec->cur_mux[adc_idx] = idx; - - if (spec->shared_mic_hp) - update_shared_mic_hp(codec, spec->cur_mux[adc_idx]); - - if (spec->dyn_adc_switch) - alc_dyn_adc_pcm_resetup(codec, idx); - - path = get_nid_path(codec, spec->imux_pins[idx], - get_adc_nid(codec, adc_idx, idx)); - if (!path) - return 0; - if (path->active) - return 0; - activate_path(codec, path, true, false); - alc_inv_dmic_sync(codec, true); - return 1; -} - -static int alc_mux_enum_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); - return alc_mux_select(codec, adc_idx, - ucontrol->value.enumerated.item[0]); -} - -/* - * set up the input pin config (depending on the given auto-pin type) - */ -static void alc_set_input_pin(struct hda_codec *codec, hda_nid_t nid, - int auto_pin_type) -{ - unsigned int val = PIN_IN; - if (auto_pin_type == AUTO_PIN_MIC) - val |= snd_hda_get_default_vref(codec, nid); - snd_hda_set_pin_ctl(codec, nid, val); -} - /* * Append the given mixer and verb elements for the later use * The mixer array is referred in build_controls(), and init_verbs are @@ -491,146 +184,6 @@ static void alc_fix_pll_init(struct hda_codec *codec, hda_nid_t nid, alc_fix_pll(codec); } -/* - * Jack detections for HP auto-mute and mic-switch - */ - -/* check each pin in the given array; returns true if any of them is plugged */ -static bool detect_jacks(struct hda_codec *codec, int num_pins, hda_nid_t *pins) -{ - int i, present = 0; - - for (i = 0; i < num_pins; i++) { - hda_nid_t nid = pins[i]; - if (!nid) - break; - present |= snd_hda_jack_detect(codec, nid); - } - return present; -} - -/* standard HP/line-out auto-mute helper */ -static void do_automute(struct hda_codec *codec, int num_pins, hda_nid_t *pins, - bool mute, bool hp_out) -{ - struct alc_spec *spec = codec->spec; - unsigned int pin_bits = mute ? 0 : (hp_out ? PIN_HP : PIN_OUT); - int i; - - for (i = 0; i < num_pins; i++) { - hda_nid_t nid = pins[i]; - unsigned int val; - if (!nid) - break; - /* don't reset VREF value in case it's controlling - * the amp (see alc861_fixup_asus_amp_vref_0f()) - */ - if (spec->keep_vref_in_automute) { - val = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_PIN_WIDGET_CONTROL, 0); - val &= ~PIN_HP; - } else - val = 0; - val |= pin_bits; - snd_hda_set_pin_ctl(codec, nid, val); - } -} - -/* Toggle outputs muting */ -static void update_outputs(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - int on; - - /* Control HP pins/amps depending on master_mute state; - * in general, HP pins/amps control should be enabled in all cases, - * but currently set only for master_mute, just to be safe - */ - if (!spec->shared_mic_hp) /* don't change HP-pin when shared with mic */ - do_automute(codec, ARRAY_SIZE(spec->autocfg.hp_pins), - spec->autocfg.hp_pins, spec->master_mute, true); - - if (!spec->automute_speaker) - on = 0; - else - on = spec->hp_jack_present | spec->line_jack_present; - on |= spec->master_mute; - do_automute(codec, ARRAY_SIZE(spec->autocfg.speaker_pins), - spec->autocfg.speaker_pins, on, false); - - /* toggle line-out mutes if needed, too */ - /* if LO is a copy of either HP or Speaker, don't need to handle it */ - if (spec->autocfg.line_out_pins[0] == spec->autocfg.hp_pins[0] || - spec->autocfg.line_out_pins[0] == spec->autocfg.speaker_pins[0]) - return; - if (!spec->automute_lo) - on = 0; - else - on = spec->hp_jack_present; - on |= spec->master_mute; - do_automute(codec, ARRAY_SIZE(spec->autocfg.line_out_pins), - spec->autocfg.line_out_pins, on, false); -} - -static void call_update_outputs(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - if (spec->automute_hook) - spec->automute_hook(codec); - else - update_outputs(codec); -} - -/* standard HP-automute helper */ -static void alc_hp_automute(struct hda_codec *codec, struct hda_jack_tbl *jack) -{ - struct alc_spec *spec = codec->spec; - - spec->hp_jack_present = - detect_jacks(codec, ARRAY_SIZE(spec->autocfg.hp_pins), - spec->autocfg.hp_pins); - if (!spec->detect_hp || (!spec->automute_speaker && !spec->automute_lo)) - return; - call_update_outputs(codec); -} - -/* standard line-out-automute helper */ -static void alc_line_automute(struct hda_codec *codec, struct hda_jack_tbl *jack) -{ - struct alc_spec *spec = codec->spec; - - if (spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT) - return; - /* check LO jack only when it's different from HP */ - if (spec->autocfg.line_out_pins[0] == spec->autocfg.hp_pins[0]) - return; - - spec->line_jack_present = - detect_jacks(codec, ARRAY_SIZE(spec->autocfg.line_out_pins), - spec->autocfg.line_out_pins); - if (!spec->automute_speaker || !spec->detect_lo) - return; - call_update_outputs(codec); -} - -/* standard mic auto-switch helper */ -static void alc_mic_automute(struct hda_codec *codec, struct hda_jack_tbl *jack) -{ - struct alc_spec *spec = codec->spec; - int i; - - if (!spec->auto_mic) - return; - - for (i = spec->am_num_entries - 1; i > 0; i--) { - if (snd_hda_jack_detect(codec, spec->am_entry[i].pin)) { - alc_mux_select(codec, 0, spec->am_entry[i].idx); - return; - } - } - alc_mux_select(codec, 0, spec->am_entry[0].idx); -} - /* update the master volume per volume-knob's unsol event */ static void alc_update_knob_master(struct hda_codec *codec, struct hda_jack_tbl *jack) { @@ -780,356 +333,64 @@ static void alc_auto_init_amp(struct hda_codec *codec, int type) } } + /* - * Auto-Mute mode mixer enum support + * Realtek SSID verification */ -static int alc_automute_mode_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct alc_spec *spec = codec->spec; - static const char * const texts3[] = { - "Disabled", "Speaker Only", "Line Out+Speaker" - }; - - if (spec->automute_speaker_possible && spec->automute_lo_possible) - return snd_hda_enum_helper_info(kcontrol, uinfo, 3, texts3); - return snd_hda_enum_bool_helper_info(kcontrol, uinfo); -} - -static int alc_automute_mode_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct alc_spec *spec = codec->spec; - unsigned int val = 0; - if (spec->automute_speaker) - val++; - if (spec->automute_lo) - val++; - ucontrol->value.enumerated.item[0] = val; - return 0; -} +/* Could be any non-zero and even value. When used as fixup, tells + * the driver to ignore any present sku defines. + */ +#define ALC_FIXUP_SKU_IGNORE (2) -static int alc_automute_mode_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +static void alc_fixup_sku_ignore(struct hda_codec *codec, + const struct hda_fixup *fix, int action) { - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct alc_spec *spec = codec->spec; - - switch (ucontrol->value.enumerated.item[0]) { - case 0: - if (!spec->automute_speaker && !spec->automute_lo) - return 0; - spec->automute_speaker = 0; - spec->automute_lo = 0; - break; - case 1: - if (spec->automute_speaker_possible) { - if (!spec->automute_lo && spec->automute_speaker) - return 0; - spec->automute_speaker = 1; - spec->automute_lo = 0; - } else if (spec->automute_lo_possible) { - if (spec->automute_lo) - return 0; - spec->automute_lo = 1; - } else - return -EINVAL; - break; - case 2: - if (!spec->automute_lo_possible || !spec->automute_speaker_possible) - return -EINVAL; - if (spec->automute_speaker && spec->automute_lo) - return 0; - spec->automute_speaker = 1; - spec->automute_lo = 1; - break; - default: - return -EINVAL; + if (action == HDA_FIXUP_ACT_PRE_PROBE) { + spec->cdefine.fixup = 1; + spec->cdefine.sku_cfg = ALC_FIXUP_SKU_IGNORE; } - call_update_outputs(codec); - return 1; -} - -static const struct snd_kcontrol_new alc_automute_mode_enum = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Auto-Mute Mode", - .info = alc_automute_mode_info, - .get = alc_automute_mode_get, - .put = alc_automute_mode_put, -}; - -static struct snd_kcontrol_new * -alc_kcontrol_new(struct alc_spec *spec, const char *name, - const struct snd_kcontrol_new *temp) -{ - struct snd_kcontrol_new *knew = snd_array_new(&spec->kctls); - if (!knew) - return NULL; - *knew = *temp; - if (name) - knew->name = kstrdup(name, GFP_KERNEL); - else if (knew->name) - knew->name = kstrdup(knew->name, GFP_KERNEL); - if (!knew->name) - return NULL; - return knew; } -static int alc_add_automute_mode_enum(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - if (!alc_kcontrol_new(spec, NULL, &alc_automute_mode_enum)) - return -ENOMEM; - return 0; -} - -/* - * Check the availability of HP/line-out auto-mute; - * Set up appropriately if really supported - */ -static int alc_init_automute(struct hda_codec *codec) +static int alc_auto_parse_customize_define(struct hda_codec *codec) { + unsigned int ass, tmp, i; + unsigned nid = 0; struct alc_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - int present = 0; - int i, err; - if (cfg->hp_pins[0]) - present++; - if (cfg->line_out_pins[0]) - present++; - if (cfg->speaker_pins[0]) - present++; - if (present < 2) /* need two different output types */ - return 0; + spec->cdefine.enable_pcbeep = 1; /* assume always enabled */ - if (!cfg->speaker_pins[0] && - cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) { - memcpy(cfg->speaker_pins, cfg->line_out_pins, - sizeof(cfg->speaker_pins)); - cfg->speaker_outs = cfg->line_outs; + if (spec->cdefine.fixup) { + ass = spec->cdefine.sku_cfg; + if (ass == ALC_FIXUP_SKU_IGNORE) + return -1; + goto do_sku; } - if (!cfg->hp_pins[0] && - cfg->line_out_type == AUTO_PIN_HP_OUT) { - memcpy(cfg->hp_pins, cfg->line_out_pins, - sizeof(cfg->hp_pins)); - cfg->hp_outs = cfg->line_outs; - } + ass = codec->subsystem_id & 0xffff; + if (ass != codec->bus->pci->subsystem_device && (ass & 1)) + goto do_sku; - for (i = 0; i < cfg->hp_outs; i++) { - hda_nid_t nid = cfg->hp_pins[i]; - if (!is_jack_detectable(codec, nid)) - continue; - snd_printdd("realtek: Enable HP auto-muting on NID 0x%x\n", - nid); - snd_hda_jack_detect_enable_callback(codec, nid, ALC_HP_EVENT, - alc_hp_automute); - spec->detect_hp = 1; - } + nid = 0x1d; + if (codec->vendor_id == 0x10ec0260) + nid = 0x17; + ass = snd_hda_codec_get_pincfg(codec, nid); - if (cfg->line_out_type == AUTO_PIN_LINE_OUT && cfg->line_outs) { - if (cfg->speaker_outs) - for (i = 0; i < cfg->line_outs; i++) { - hda_nid_t nid = cfg->line_out_pins[i]; - if (!is_jack_detectable(codec, nid)) - continue; - snd_printdd("realtek: Enable Line-Out " - "auto-muting on NID 0x%x\n", nid); - snd_hda_jack_detect_enable_callback(codec, nid, ALC_FRONT_EVENT, - alc_line_automute); - spec->detect_lo = 1; - } - spec->automute_lo_possible = spec->detect_hp; + if (!(ass & 1)) { + printk(KERN_INFO "hda_codec: %s: SKU not ready 0x%08x\n", + codec->chip_name, ass); + return -1; } - spec->automute_speaker_possible = cfg->speaker_outs && - (spec->detect_hp || spec->detect_lo); - - spec->automute_lo = spec->automute_lo_possible; - spec->automute_speaker = spec->automute_speaker_possible; - - if (spec->automute_speaker_possible || spec->automute_lo_possible) { - /* create a control for automute mode */ - err = alc_add_automute_mode_enum(codec); - if (err < 0) - return err; + /* check sum */ + tmp = 0; + for (i = 1; i < 16; i++) { + if ((ass >> i) & 1) + tmp++; } - return 0; -} - -/* return the position of NID in the list, or -1 if not found */ -static int find_idx_in_nid_list(hda_nid_t nid, const hda_nid_t *list, int nums) -{ - int i; - for (i = 0; i < nums; i++) - if (list[i] == nid) - return i; - return -1; -} - -/* check whether all auto-mic pins are valid; setup indices if OK */ -static bool alc_auto_mic_check_imux(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - const struct hda_input_mux *imux; - int i; - - imux = &spec->input_mux; - for (i = 0; i < spec->am_num_entries; i++) { - spec->am_entry[i].idx = - find_idx_in_nid_list(spec->am_entry[i].pin, - spec->imux_pins, imux->num_items); - if (spec->am_entry[i].idx < 0) - return false; /* no corresponding imux */ - } - - /* we don't need the jack detection for the first pin */ - for (i = 1; i < spec->am_num_entries; i++) - snd_hda_jack_detect_enable_callback(codec, - spec->am_entry[i].pin, - ALC_MIC_EVENT, - alc_mic_automute); - return true; -} - -static int compare_attr(const void *ap, const void *bp) -{ - const struct alc_automic_entry *a = ap; - const struct alc_automic_entry *b = bp; - return (int)(a->attr - b->attr); -} - -/* - * Check the availability of auto-mic switch; - * Set up if really supported - */ -static int alc_init_auto_mic(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - unsigned int types; - int i, num_pins; - - types = 0; - num_pins = 0; - for (i = 0; i < cfg->num_inputs; i++) { - hda_nid_t nid = cfg->inputs[i].pin; - unsigned int attr; - attr = snd_hda_codec_get_pincfg(codec, nid); - attr = snd_hda_get_input_pin_attr(attr); - if (types & (1 << attr)) - return 0; /* already occupied */ - switch (attr) { - case INPUT_PIN_ATTR_INT: - if (cfg->inputs[i].type != AUTO_PIN_MIC) - return 0; /* invalid type */ - break; - case INPUT_PIN_ATTR_UNUSED: - return 0; /* invalid entry */ - default: - if (cfg->inputs[i].type > AUTO_PIN_LINE_IN) - return 0; /* invalid type */ - if (!spec->line_in_auto_switch && - cfg->inputs[i].type != AUTO_PIN_MIC) - return 0; /* only mic is allowed */ - if (!is_jack_detectable(codec, nid)) - return 0; /* no unsol support */ - break; - } - if (num_pins >= MAX_AUTO_MIC_PINS) - return 0; - types |= (1 << attr); - spec->am_entry[num_pins].pin = nid; - spec->am_entry[num_pins].attr = attr; - num_pins++; - } - - if (num_pins < 2) - return 0; - - spec->am_num_entries = num_pins; - /* sort the am_entry in the order of attr so that the pin with a - * higher attr will be selected when the jack is plugged. - */ - sort(spec->am_entry, num_pins, sizeof(spec->am_entry[0]), - compare_attr, NULL); - - if (!alc_auto_mic_check_imux(codec)) - return 0; - - spec->auto_mic = 1; - spec->num_adc_nids = 1; - spec->cur_mux[0] = spec->am_entry[0].idx; - snd_printdd("realtek: Enable auto-mic switch on NID 0x%x/0x%x/0x%x\n", - spec->am_entry[0].pin, - spec->am_entry[1].pin, - spec->am_entry[2].pin); - - return 0; -} - -/* - * Realtek SSID verification - */ - -/* Could be any non-zero and even value. When used as fixup, tells - * the driver to ignore any present sku defines. - */ -#define ALC_FIXUP_SKU_IGNORE (2) - -static void alc_fixup_sku_ignore(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - spec->cdefine.fixup = 1; - spec->cdefine.sku_cfg = ALC_FIXUP_SKU_IGNORE; - } -} - -static int alc_auto_parse_customize_define(struct hda_codec *codec) -{ - unsigned int ass, tmp, i; - unsigned nid = 0; - struct alc_spec *spec = codec->spec; - - spec->cdefine.enable_pcbeep = 1; /* assume always enabled */ - - if (spec->cdefine.fixup) { - ass = spec->cdefine.sku_cfg; - if (ass == ALC_FIXUP_SKU_IGNORE) - return -1; - goto do_sku; - } - - ass = codec->subsystem_id & 0xffff; - if (ass != codec->bus->pci->subsystem_device && (ass & 1)) - goto do_sku; - - nid = 0x1d; - if (codec->vendor_id == 0x10ec0260) - nid = 0x17; - ass = snd_hda_codec_get_pincfg(codec, nid); - - if (!(ass & 1)) { - printk(KERN_INFO "hda_codec: %s: SKU not ready 0x%08x\n", - codec->chip_name, ass); - return -1; - } - - /* check sum */ - tmp = 0; - for (i = 1; i < 16; i++) { - if ((ass >> i) & 1) - tmp++; - } - if (((ass >> 16) & 0xf) != tmp) - return -1; + if (((ass >> 16) & 0xf) != tmp) + return -1; spec->cdefine.port_connectivity = ass >> 30; spec->cdefine.enable_pcbeep = (ass & 0x100000) >> 20; @@ -1157,6 +418,15 @@ do_sku: return 0; } +/* return the position of NID in the list, or -1 if not found */ +static int find_idx_in_nid_list(hda_nid_t nid, const hda_nid_t *list, int nums) +{ + int i; + for (i = 0; i < nums; i++) + if (list[i] == nid) + return i; + return -1; +} /* return true if the given NID is found in the list */ static bool found_in_nid_list(hda_nid_t nid, const hda_nid_t *list, int nums) { @@ -1259,9 +529,9 @@ do_sku: * 15 : 1 --> enable the function "Mute internal speaker * when the external headphone out jack is plugged" */ - if (!spec->autocfg.hp_pins[0] && - !(spec->autocfg.line_out_pins[0] && - spec->autocfg.line_out_type == AUTO_PIN_HP_OUT)) { + if (!spec->gen.autocfg.hp_pins[0] && + !(spec->gen.autocfg.line_out_pins[0] && + spec->gen.autocfg.line_out_type == AUTO_PIN_HP_OUT)) { hda_nid_t nid; tmp = (ass >> 11) & 0x3; /* HP to chassis */ if (tmp == 0) @@ -1274,10 +544,10 @@ do_sku: nid = porti; else return 1; - if (found_in_nid_list(nid, spec->autocfg.line_out_pins, - spec->autocfg.line_outs)) + if (found_in_nid_list(nid, spec->gen.autocfg.line_out_pins, + spec->gen.autocfg.line_outs)) return 1; - spec->autocfg.hp_pins[0] = nid; + spec->gen.autocfg.hp_pins[0] = nid; } return 1; } @@ -1326,170 +596,35 @@ static unsigned int alc_get_coef0(struct hda_codec *codec) return spec->coef0; } -static void alc_auto_set_output_and_unmute(struct hda_codec *codec, - hda_nid_t pin, int pin_type, - hda_nid_t dac); -static hda_nid_t alc_auto_look_for_dac(struct hda_codec *codec, hda_nid_t pin, - bool is_digital); -static bool parse_nid_path(struct hda_codec *codec, hda_nid_t from_nid, - hda_nid_t to_nid, int with_aa_mix, - struct nid_path *path); -static struct nid_path *add_new_nid_path(struct hda_codec *codec, - hda_nid_t from_nid, hda_nid_t to_nid, - int with_aa_mix); - -/* - * Digital I/O handling - */ - -/* set right pin controls for digital I/O */ -static void alc_auto_init_digital(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - int i; - hda_nid_t pin; - - for (i = 0; i < spec->autocfg.dig_outs; i++) { - pin = spec->autocfg.dig_out_pins[i]; - if (!pin) - continue; - alc_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0); - } - pin = spec->autocfg.dig_in_pin; - if (pin) - snd_hda_set_pin_ctl(codec, pin, PIN_IN); -} - -/* parse digital I/Os and set up NIDs in BIOS auto-parse mode */ -static void alc_auto_parse_digital(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - int i, nums; - hda_nid_t dig_nid; - - /* support multiple SPDIFs; the secondary is set up as a slave */ - nums = 0; - for (i = 0; i < spec->autocfg.dig_outs; i++) { - hda_nid_t pin = spec->autocfg.dig_out_pins[i]; - dig_nid = alc_auto_look_for_dac(codec, pin, true); - if (!dig_nid) - continue; - if (!add_new_nid_path(codec, dig_nid, pin, 2)) - continue; - if (!nums) { - spec->multiout.dig_out_nid = dig_nid; - spec->dig_out_type = spec->autocfg.dig_out_type[0]; - } else { - spec->multiout.slave_dig_outs = spec->slave_dig_outs; - if (nums >= ARRAY_SIZE(spec->slave_dig_outs) - 1) - break; - spec->slave_dig_outs[nums - 1] = dig_nid; - } - nums++; - } - - if (spec->autocfg.dig_in_pin) { - dig_nid = codec->start_nid; - for (i = 0; i < codec->num_nodes; i++, dig_nid++) { - struct nid_path *path; - unsigned int wcaps = get_wcaps(codec, dig_nid); - if (get_wcaps_type(wcaps) != AC_WID_AUD_IN) - continue; - if (!(wcaps & AC_WCAP_DIGITAL)) - continue; - path = add_new_nid_path(codec, spec->autocfg.dig_in_pin, - dig_nid, 2); - if (path) { - path->active = true; - spec->dig_in_nid = dig_nid; - break; - } - } - } -} - /* - * capture mixer elements */ -#define alc_cap_vol_info snd_hda_mixer_amp_volume_info -#define alc_cap_vol_get snd_hda_mixer_amp_volume_get -#define alc_cap_vol_tlv snd_hda_mixer_amp_tlv - -typedef int (*put_call_t)(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol); - -static int alc_cap_put_caller(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol, - put_call_t func, int type) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct alc_spec *spec = codec->spec; - const struct hda_input_mux *imux; - struct nid_path *path; - int i, adc_idx, err = 0; - - imux = &spec->input_mux; - adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); - mutex_lock(&codec->control_mutex); - codec->cached_write = 1; - for (i = 0; i < imux->num_items; i++) { - path = get_nid_path(codec, spec->imux_pins[i], - get_adc_nid(codec, adc_idx, i)); - if (!path->ctls[type]) - continue; - kcontrol->private_value = path->ctls[type]; - err = func(kcontrol, ucontrol); - if (err < 0) - goto error; - } - error: - codec->cached_write = 0; - mutex_unlock(&codec->control_mutex); - snd_hda_codec_resume_amp(codec); - if (err >= 0 && type == NID_PATH_MUTE_CTL && - spec->inv_dmic_fixup && spec->inv_dmic_muted) - alc_inv_dmic_sync_adc(codec, adc_idx); - return err; -} - -static int alc_cap_vol_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - return alc_cap_put_caller(kcontrol, ucontrol, - snd_hda_mixer_amp_volume_put, - NID_PATH_VOL_CTL); -} - -/* capture mixer elements */ -#define alc_cap_sw_info snd_ctl_boolean_stereo_info -#define alc_cap_sw_get snd_hda_mixer_amp_switch_get -static int alc_cap_sw_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +static hda_nid_t get_adc_nid(struct hda_codec *codec, int adc_idx, int imux_idx) { - return alc_cap_put_caller(kcontrol, ucontrol, - snd_hda_mixer_amp_switch_put, - NID_PATH_MUTE_CTL); + struct hda_gen_spec *spec = codec->spec; + if (spec->dyn_adc_switch) + adc_idx = spec->dyn_adc_idx[imux_idx]; + return spec->adc_nids[adc_idx]; } static void alc_inv_dmic_sync_adc(struct hda_codec *codec, int adc_idx) { struct alc_spec *spec = codec->spec; - struct hda_input_mux *imux = &spec->input_mux; + struct hda_input_mux *imux = &spec->gen.input_mux; struct nid_path *path; hda_nid_t nid; int i, dir, parm; unsigned int val; for (i = 0; i < imux->num_items; i++) { - if (spec->imux_pins[i] == spec->inv_dmic_pin) + if (spec->gen.imux_pins[i] == spec->inv_dmic_pin) break; } if (i >= imux->num_items) return; - path = get_nid_path(codec, spec->inv_dmic_pin, - get_adc_nid(codec, adc_idx, i)); + path = snd_hda_get_nid_path(codec, spec->inv_dmic_pin, + get_adc_nid(codec, adc_idx, i)); val = path->ctls[NID_PATH_MUTE_CTL]; if (!val) return; @@ -1531,12 +666,12 @@ static void alc_inv_dmic_sync(struct hda_codec *codec, bool force) return; if (!spec->inv_dmic_muted && !force) return; - nums = spec->dyn_adc_switch ? 1 : spec->num_adc_nids; + nums = spec->gen.dyn_adc_switch ? 1 : spec->gen.num_adc_nids; for (src = 0; src < nums; src++) { bool dmic_fixup = false; if (spec->inv_dmic_muted && - spec->imux_pins[spec->cur_mux[src]] == spec->inv_dmic_pin) + spec->gen.imux_pins[spec->gen.cur_mux[src]] == spec->inv_dmic_pin) dmic_fixup = true; if (!dmic_fixup && !force) continue; @@ -1544,6 +679,11 @@ static void alc_inv_dmic_sync(struct hda_codec *codec, bool force) } } +static void alc_inv_dmic_hook(struct hda_codec *codec) +{ + alc_inv_dmic_sync(codec, false); +} + static int alc_inv_dmic_sw_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -1580,11 +720,12 @@ static int alc_add_inv_dmic_mixer(struct hda_codec *codec, hda_nid_t nid) { struct alc_spec *spec = codec->spec; - if (!alc_kcontrol_new(spec, NULL, &alc_inv_dmic_sw)) + if (!snd_hda_gen_add_kctl(&spec->gen, NULL, &alc_inv_dmic_sw)) return -ENOMEM; spec->inv_dmic_fixup = 1; spec->inv_dmic_muted = 0; spec->inv_dmic_pin = nid; + spec->gen.cap_sync_hook = alc_inv_dmic_hook; return 0; } @@ -1594,2807 +735,208 @@ static void alc_fixup_inv_dmic_0x12(struct hda_codec *codec, { if (action == ALC_FIXUP_ACT_PROBE) alc_add_inv_dmic_mixer(codec, 0x12); -} - -/* - * virtual master controls - */ - -/* - * slave controls for virtual master - */ -static const char * const alc_slave_pfxs[] = { - "Front", "Surround", "Center", "LFE", "Side", - "Headphone", "Speaker", "Mono", "Line Out", - "CLFE", "Bass Speaker", "PCM", - NULL, -}; - -/* - * build control elements - */ - -static void alc_free_kctls(struct hda_codec *codec); - -#ifdef CONFIG_SND_HDA_INPUT_BEEP -/* additional beep mixers; the actual parameters are overwritten at build */ -static const struct snd_kcontrol_new alc_beep_mixer[] = { - HDA_CODEC_VOLUME("Beep Playback Volume", 0, 0, HDA_INPUT), - HDA_CODEC_MUTE_BEEP("Beep Playback Switch", 0, 0, HDA_INPUT), - { } /* end */ -}; -#endif - -static int alc_build_controls(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - int i, err; - - for (i = 0; i < spec->num_mixers; i++) { - err = snd_hda_add_new_ctls(codec, spec->mixers[i]); - if (err < 0) - return err; - } - if (spec->multiout.dig_out_nid) { - err = snd_hda_create_dig_out_ctls(codec, - spec->multiout.dig_out_nid, - spec->multiout.dig_out_nid, - spec->pcm_rec[1].pcm_type); - if (err < 0) - return err; - if (!spec->no_analog) { - err = snd_hda_create_spdif_share_sw(codec, - &spec->multiout); - if (err < 0) - return err; - spec->multiout.share_spdif = 1; - } - } - if (spec->dig_in_nid) { - err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid); - if (err < 0) - return err; - } - -#ifdef CONFIG_SND_HDA_INPUT_BEEP - /* create beep controls if needed */ - if (spec->beep_amp) { - const struct snd_kcontrol_new *knew; - for (knew = alc_beep_mixer; knew->name; knew++) { - struct snd_kcontrol *kctl; - kctl = snd_ctl_new1(knew, codec); - if (!kctl) - return -ENOMEM; - kctl->private_value = spec->beep_amp; - err = snd_hda_ctl_add(codec, 0, kctl); - if (err < 0) - return err; - } - } -#endif - - /* if we have no master control, let's create it */ - if (!spec->no_analog && - !snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) { - unsigned int vmaster_tlv[4]; - snd_hda_set_vmaster_tlv(codec, spec->vmaster_nid, - HDA_OUTPUT, vmaster_tlv); - err = snd_hda_add_vmaster(codec, "Master Playback Volume", - vmaster_tlv, alc_slave_pfxs, - "Playback Volume"); - if (err < 0) - return err; - } - if (!spec->no_analog && - !snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) { - err = __snd_hda_add_vmaster(codec, "Master Playback Switch", - NULL, alc_slave_pfxs, - "Playback Switch", - true, &spec->vmaster_mute.sw_kctl); - if (err < 0) - return err; - if (spec->vmaster_mute.hook) - snd_hda_add_vmaster_hook(codec, &spec->vmaster_mute, true); - } - - alc_free_kctls(codec); /* no longer needed */ - - if (spec->shared_mic_hp) { - int err; - int nid = spec->autocfg.inputs[1].pin; - err = snd_hda_jack_add_kctl(codec, nid, "Headphone Mic", 0); - if (err < 0) - return err; - err = snd_hda_jack_detect_enable(codec, nid, 0); - if (err < 0) - return err; - } - - err = snd_hda_jack_add_kctls(codec, &spec->autocfg); - if (err < 0) - return err; - - alc_apply_fixup(codec, ALC_FIXUP_ACT_BUILD); - return 0; -} - - -/* - * Common callbacks - */ - -static void alc_auto_init_std(struct hda_codec *codec); - -static int alc_init(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - if (spec->init_hook) - spec->init_hook(codec); - - alc_fix_pll(codec); - alc_auto_init_amp(codec, spec->init_amp); - - snd_hda_apply_verbs(codec); - alc_auto_init_std(codec); - - if (spec->vmaster_mute.sw_kctl && spec->vmaster_mute.hook) - snd_hda_sync_vmaster_hook(&spec->vmaster_mute); - - alc_apply_fixup(codec, ALC_FIXUP_ACT_INIT); - - hda_call_check_power_status(codec, 0x01); - return 0; -} - -#ifdef CONFIG_PM -static int alc_check_power_status(struct hda_codec *codec, hda_nid_t nid) -{ - struct alc_spec *spec = codec->spec; - return snd_hda_check_amp_list_power(codec, &spec->loopback, nid); -} -#endif - -/* - * Analog playback callbacks - */ -static int alc_playback_pcm_open(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct alc_spec *spec = codec->spec; - return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream, - hinfo); -} - -static int alc_playback_pcm_prepare(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - unsigned int stream_tag, - unsigned int format, - struct snd_pcm_substream *substream) -{ - struct alc_spec *spec = codec->spec; - return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, - stream_tag, format, substream); -} - -static int alc_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct alc_spec *spec = codec->spec; - return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout); -} - -/* - * Digital out - */ -static int alc_dig_playback_pcm_open(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct alc_spec *spec = codec->spec; - return snd_hda_multi_out_dig_open(codec, &spec->multiout); -} - -static int alc_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - unsigned int stream_tag, - unsigned int format, - struct snd_pcm_substream *substream) -{ - struct alc_spec *spec = codec->spec; - return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, - stream_tag, format, substream); -} - -static int alc_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct alc_spec *spec = codec->spec; - return snd_hda_multi_out_dig_cleanup(codec, &spec->multiout); -} - -static int alc_dig_playback_pcm_close(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct alc_spec *spec = codec->spec; - return snd_hda_multi_out_dig_close(codec, &spec->multiout); -} - -/* - * Analog capture - */ -static int alc_alt_capture_pcm_prepare(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - unsigned int stream_tag, - unsigned int format, - struct snd_pcm_substream *substream) -{ - struct alc_spec *spec = codec->spec; - - snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number + 1], - stream_tag, 0, format); - return 0; -} - -static int alc_alt_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct alc_spec *spec = codec->spec; - - snd_hda_codec_cleanup_stream(codec, - spec->adc_nids[substream->number + 1]); - return 0; -} - -/* analog capture with dynamic dual-adc changes */ -static int dyn_adc_capture_pcm_prepare(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - unsigned int stream_tag, - unsigned int format, - struct snd_pcm_substream *substream) -{ - struct alc_spec *spec = codec->spec; - spec->cur_adc = spec->adc_nids[spec->dyn_adc_idx[spec->cur_mux[0]]]; - spec->cur_adc_stream_tag = stream_tag; - spec->cur_adc_format = format; - snd_hda_codec_setup_stream(codec, spec->cur_adc, stream_tag, 0, format); - return 0; -} - -static int dyn_adc_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct alc_spec *spec = codec->spec; - snd_hda_codec_cleanup_stream(codec, spec->cur_adc); - spec->cur_adc = 0; - return 0; -} - -static const struct hda_pcm_stream dyn_adc_pcm_analog_capture = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, - .nid = 0, /* fill later */ - .ops = { - .prepare = dyn_adc_capture_pcm_prepare, - .cleanup = dyn_adc_capture_pcm_cleanup - }, -}; - -/* - */ -static const struct hda_pcm_stream alc_pcm_analog_playback = { - .substreams = 1, - .channels_min = 2, - .channels_max = 8, - /* NID is set in alc_build_pcms */ - .ops = { - .open = alc_playback_pcm_open, - .prepare = alc_playback_pcm_prepare, - .cleanup = alc_playback_pcm_cleanup - }, -}; - -static const struct hda_pcm_stream alc_pcm_analog_capture = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, - /* NID is set in alc_build_pcms */ -}; - -static const struct hda_pcm_stream alc_pcm_analog_alt_playback = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, - /* NID is set in alc_build_pcms */ -}; - -static const struct hda_pcm_stream alc_pcm_analog_alt_capture = { - .substreams = 2, /* can be overridden */ - .channels_min = 2, - .channels_max = 2, - /* NID is set in alc_build_pcms */ - .ops = { - .prepare = alc_alt_capture_pcm_prepare, - .cleanup = alc_alt_capture_pcm_cleanup - }, -}; - -static const struct hda_pcm_stream alc_pcm_digital_playback = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, - /* NID is set in alc_build_pcms */ - .ops = { - .open = alc_dig_playback_pcm_open, - .close = alc_dig_playback_pcm_close, - .prepare = alc_dig_playback_pcm_prepare, - .cleanup = alc_dig_playback_pcm_cleanup - }, -}; - -static const struct hda_pcm_stream alc_pcm_digital_capture = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, - /* NID is set in alc_build_pcms */ -}; - -/* Used by alc_build_pcms to flag that a PCM has no playback stream */ -static const struct hda_pcm_stream alc_pcm_null_stream = { - .substreams = 0, - .channels_min = 0, - .channels_max = 0, -}; - -static int alc_build_pcms(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - struct hda_pcm *info = spec->pcm_rec; - const struct hda_pcm_stream *p; - bool have_multi_adcs; - int i; - - codec->num_pcms = 1; - codec->pcm_info = info; - - if (spec->no_analog) - goto skip_analog; - - snprintf(spec->stream_name_analog, sizeof(spec->stream_name_analog), - "%s Analog", codec->chip_name); - info->name = spec->stream_name_analog; - - if (spec->multiout.num_dacs > 0) { - p = spec->stream_analog_playback; - if (!p) - p = &alc_pcm_analog_playback; - info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *p; - info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dac_nids[0]; - info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = - spec->multiout.max_channels; - if (spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT && - spec->autocfg.line_outs == 2) - info->stream[SNDRV_PCM_STREAM_PLAYBACK].chmap = - snd_pcm_2_1_chmaps; - } - if (spec->num_adc_nids) { - p = spec->stream_analog_capture; - if (!p) { - if (spec->dyn_adc_switch) - p = &dyn_adc_pcm_analog_capture; - else - p = &alc_pcm_analog_capture; - } - info->stream[SNDRV_PCM_STREAM_CAPTURE] = *p; - info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0]; - } - - if (spec->channel_mode) { - info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = 0; - for (i = 0; i < spec->num_channel_mode; i++) { - if (spec->channel_mode[i].channels > info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max) { - info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = spec->channel_mode[i].channels; - } - } - } - - skip_analog: - /* SPDIF for stream index #1 */ - if (spec->multiout.dig_out_nid || spec->dig_in_nid) { - snprintf(spec->stream_name_digital, - sizeof(spec->stream_name_digital), - "%s Digital", codec->chip_name); - codec->num_pcms = 2; - codec->slave_dig_outs = spec->multiout.slave_dig_outs; - info = spec->pcm_rec + 1; - info->name = spec->stream_name_digital; - if (spec->dig_out_type) - info->pcm_type = spec->dig_out_type; - else - info->pcm_type = HDA_PCM_TYPE_SPDIF; - if (spec->multiout.dig_out_nid) { - p = spec->stream_digital_playback; - if (!p) - p = &alc_pcm_digital_playback; - info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *p; - info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dig_out_nid; - } - if (spec->dig_in_nid) { - p = spec->stream_digital_capture; - if (!p) - p = &alc_pcm_digital_capture; - info->stream[SNDRV_PCM_STREAM_CAPTURE] = *p; - info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in_nid; - } - /* FIXME: do we need this for all Realtek codec models? */ - codec->spdif_status_reset = 1; - } - - if (spec->no_analog) - return 0; - - /* If the use of more than one ADC is requested for the current - * model, configure a second analog capture-only PCM. - */ - have_multi_adcs = (spec->num_adc_nids > 1) && - !spec->dyn_adc_switch && !spec->auto_mic; - /* Additional Analaog capture for index #2 */ - if (spec->alt_dac_nid || have_multi_adcs) { - codec->num_pcms = 3; - info = spec->pcm_rec + 2; - info->name = spec->stream_name_analog; - if (spec->alt_dac_nid) { - p = spec->stream_analog_alt_playback; - if (!p) - p = &alc_pcm_analog_alt_playback; - info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *p; - info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = - spec->alt_dac_nid; - } else { - info->stream[SNDRV_PCM_STREAM_PLAYBACK] = - alc_pcm_null_stream; - info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = 0; - } - if (have_multi_adcs) { - p = spec->stream_analog_alt_capture; - if (!p) - p = &alc_pcm_analog_alt_capture; - info->stream[SNDRV_PCM_STREAM_CAPTURE] = *p; - info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = - spec->adc_nids[1]; - info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = - spec->num_adc_nids - 1; - } else { - info->stream[SNDRV_PCM_STREAM_CAPTURE] = - alc_pcm_null_stream; - info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = 0; - } - } - - return 0; -} - -static inline void alc_shutup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - if (spec && spec->shutup) - spec->shutup(codec); - snd_hda_shutup_pins(codec); -} - -static void alc_free_kctls(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - if (spec->kctls.list) { - struct snd_kcontrol_new *kctl = spec->kctls.list; - int i; - for (i = 0; i < spec->kctls.used; i++) - kfree(kctl[i].name); - } - snd_array_free(&spec->kctls); -} - -static void alc_free_bind_ctls(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - if (spec->bind_ctls.list) { - struct hda_bind_ctls **ctl = spec->bind_ctls.list; - int i; - for (i = 0; i < spec->bind_ctls.used; i++) - kfree(ctl[i]); - } - snd_array_free(&spec->bind_ctls); -} - -static void alc_free(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - if (!spec) - return; - - alc_free_kctls(codec); - alc_free_bind_ctls(codec); - snd_array_free(&spec->paths); - kfree(spec); - snd_hda_detach_beep_device(codec); -} - -#ifdef CONFIG_PM -static void alc_power_eapd(struct hda_codec *codec) -{ - alc_auto_setup_eapd(codec, false); -} - -static int alc_suspend(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - alc_shutup(codec); - if (spec && spec->power_hook) - spec->power_hook(codec); - return 0; -} -#endif - -#ifdef CONFIG_PM -static int alc_resume(struct hda_codec *codec) -{ - msleep(150); /* to avoid pop noise */ - codec->patch_ops.init(codec); - snd_hda_codec_resume_amp(codec); - snd_hda_codec_resume_cache(codec); - alc_inv_dmic_sync(codec, true); - hda_call_check_power_status(codec, 0x01); - return 0; -} -#endif - -/* - */ -static const struct hda_codec_ops alc_patch_ops = { - .build_controls = alc_build_controls, - .build_pcms = alc_build_pcms, - .init = alc_init, - .free = alc_free, - .unsol_event = snd_hda_jack_unsol_event, -#ifdef CONFIG_PM - .resume = alc_resume, -#endif -#ifdef CONFIG_PM - .suspend = alc_suspend, - .check_power_status = alc_check_power_status, -#endif - .reboot_notify = alc_shutup, -}; - - -/* replace the codec chip_name with the given string */ -static int alc_codec_rename(struct hda_codec *codec, const char *name) -{ - kfree(codec->chip_name); - codec->chip_name = kstrdup(name, GFP_KERNEL); - if (!codec->chip_name) { - alc_free(codec); - return -ENOMEM; - } - return 0; -} - -/* - * Rename codecs appropriately from COEF value - */ -struct alc_codec_rename_table { - unsigned int vendor_id; - unsigned short coef_mask; - unsigned short coef_bits; - const char *name; -}; - -static struct alc_codec_rename_table rename_tbl[] = { - { 0x10ec0269, 0xfff0, 0x3010, "ALC277" }, - { 0x10ec0269, 0xf0f0, 0x2010, "ALC259" }, - { 0x10ec0269, 0xf0f0, 0x3010, "ALC258" }, - { 0x10ec0269, 0x00f0, 0x0010, "ALC269VB" }, - { 0x10ec0269, 0xffff, 0xa023, "ALC259" }, - { 0x10ec0269, 0xffff, 0x6023, "ALC281X" }, - { 0x10ec0269, 0x00f0, 0x0020, "ALC269VC" }, - { 0x10ec0269, 0x00f0, 0x0030, "ALC269VD" }, - { 0x10ec0887, 0x00f0, 0x0030, "ALC887-VD" }, - { 0x10ec0888, 0x00f0, 0x0030, "ALC888-VD" }, - { 0x10ec0888, 0xf0f0, 0x3020, "ALC886" }, - { 0x10ec0899, 0x2000, 0x2000, "ALC899" }, - { 0x10ec0892, 0xffff, 0x8020, "ALC661" }, - { 0x10ec0892, 0xffff, 0x8011, "ALC661" }, - { 0x10ec0892, 0xffff, 0x4011, "ALC656" }, - { } /* terminator */ -}; - -static int alc_codec_rename_from_preset(struct hda_codec *codec) -{ - const struct alc_codec_rename_table *p; - - for (p = rename_tbl; p->vendor_id; p++) { - if (p->vendor_id != codec->vendor_id) - continue; - if ((alc_get_coef0(codec) & p->coef_mask) == p->coef_bits) - return alc_codec_rename(codec, p->name); - } - return 0; -} - -/* - * Automatic parse of I/O pins from the BIOS configuration - */ - -enum { - ALC_CTL_WIDGET_VOL, - ALC_CTL_WIDGET_MUTE, - ALC_CTL_BIND_MUTE, - ALC_CTL_BIND_VOL, - ALC_CTL_BIND_SW, -}; -static const struct snd_kcontrol_new alc_control_templates[] = { - HDA_CODEC_VOLUME(NULL, 0, 0, 0), - HDA_CODEC_MUTE(NULL, 0, 0, 0), - HDA_BIND_MUTE(NULL, 0, 0, 0), - HDA_BIND_VOL(NULL, 0), - HDA_BIND_SW(NULL, 0), -}; - -/* add dynamic controls */ -static int add_control(struct alc_spec *spec, int type, const char *name, - int cidx, unsigned long val) -{ - struct snd_kcontrol_new *knew; - - knew = alc_kcontrol_new(spec, name, &alc_control_templates[type]); - if (!knew) - return -ENOMEM; - knew->index = cidx; - if (get_amp_nid_(val)) - knew->subdevice = HDA_SUBDEV_AMP_FLAG; - knew->private_value = val; - return 0; -} - -static int add_control_with_pfx(struct alc_spec *spec, int type, - const char *pfx, const char *dir, - const char *sfx, int cidx, unsigned long val) -{ - char name[32]; - snprintf(name, sizeof(name), "%s %s %s", pfx, dir, sfx); - return add_control(spec, type, name, cidx, val); -} - -#define add_pb_vol_ctrl(spec, type, pfx, val) \ - add_control_with_pfx(spec, type, pfx, "Playback", "Volume", 0, val) -#define add_pb_sw_ctrl(spec, type, pfx, val) \ - add_control_with_pfx(spec, type, pfx, "Playback", "Switch", 0, val) -#define __add_pb_vol_ctrl(spec, type, pfx, cidx, val) \ - add_control_with_pfx(spec, type, pfx, "Playback", "Volume", cidx, val) -#define __add_pb_sw_ctrl(spec, type, pfx, cidx, val) \ - add_control_with_pfx(spec, type, pfx, "Playback", "Switch", cidx, val) - -static const char * const channel_name[4] = { - "Front", "Surround", "CLFE", "Side" -}; - -static const char *alc_get_line_out_pfx(struct alc_spec *spec, int ch, - bool can_be_master, int *index) -{ - struct auto_pin_cfg *cfg = &spec->autocfg; - - *index = 0; - if (cfg->line_outs == 1 && !spec->multi_ios && - !cfg->hp_outs && !cfg->speaker_outs && can_be_master) - return spec->vmaster_mute.hook ? "PCM" : "Master"; - - /* if there is really a single DAC used in the whole output paths, - * use it master (or "PCM" if a vmaster hook is present) - */ - if (spec->multiout.num_dacs == 1 && !spec->mixer_nid && - !spec->multiout.hp_out_nid[0] && !spec->multiout.extra_out_nid[0]) - return spec->vmaster_mute.hook ? "PCM" : "Master"; - - switch (cfg->line_out_type) { - case AUTO_PIN_SPEAKER_OUT: - if (cfg->line_outs == 1) - return "Speaker"; - if (cfg->line_outs == 2) - return ch ? "Bass Speaker" : "Speaker"; - break; - case AUTO_PIN_HP_OUT: - /* for multi-io case, only the primary out */ - if (ch && spec->multi_ios) - break; - *index = ch; - return "Headphone"; - default: - if (cfg->line_outs == 1 && !spec->multi_ios) - return "PCM"; - break; - } - if (ch >= ARRAY_SIZE(channel_name)) { - snd_BUG(); - return "PCM"; - } - - return channel_name[ch]; -} - -#ifdef CONFIG_PM -/* add the powersave loopback-list entry */ -static void add_loopback_list(struct alc_spec *spec, hda_nid_t mix, int idx) -{ - struct hda_amp_list *list; - - if (spec->num_loopbacks >= ARRAY_SIZE(spec->loopback_list) - 1) - return; - list = spec->loopback_list + spec->num_loopbacks; - list->nid = mix; - list->dir = HDA_INPUT; - list->idx = idx; - spec->num_loopbacks++; - spec->loopback.amplist = spec->loopback_list; -} -#else -#define add_loopback_list(spec, mix, idx) /* NOP */ -#endif - -/* create input playback/capture controls for the given pin */ -static int new_analog_input(struct hda_codec *codec, hda_nid_t pin, - const char *ctlname, int ctlidx, - hda_nid_t mix_nid) -{ - struct alc_spec *spec = codec->spec; - struct nid_path *path; - unsigned int val; - int err, idx; - - if (!nid_has_volume(codec, mix_nid, HDA_INPUT) && - !nid_has_mute(codec, mix_nid, HDA_INPUT)) - return 0; /* no need for analog loopback */ - - path = add_new_nid_path(codec, pin, mix_nid, 2); - if (!path) - return -EINVAL; - - idx = path->idx[path->depth - 1]; - if (nid_has_volume(codec, mix_nid, HDA_INPUT)) { - val = HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT); - err = __add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, ctlname, ctlidx, val); - if (err < 0) - return err; - path->ctls[NID_PATH_VOL_CTL] = val; - } - - if (nid_has_mute(codec, mix_nid, HDA_INPUT)) { - val = HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT); - err = __add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, ctlname, ctlidx, val); - if (err < 0) - return err; - path->ctls[NID_PATH_MUTE_CTL] = val; - } - - path->active = true; - add_loopback_list(spec, mix_nid, idx); - return 0; -} - -static int alc_is_input_pin(struct hda_codec *codec, hda_nid_t nid) -{ - unsigned int pincap = snd_hda_query_pin_caps(codec, nid); - return (pincap & AC_PINCAP_IN) != 0; -} - -/* check whether the given two widgets can be connected */ -static bool is_reachable_path(struct hda_codec *codec, - hda_nid_t from_nid, hda_nid_t to_nid) -{ - if (!from_nid || !to_nid) - return false; - return snd_hda_get_conn_index(codec, to_nid, from_nid, true) >= 0; -} - -/* Parse the codec tree and retrieve ADCs */ -static int alc_auto_fill_adc_nids(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - hda_nid_t nid; - hda_nid_t *adc_nids = spec->adc_nids; - int max_nums = ARRAY_SIZE(spec->adc_nids); - int i, nums = 0; - - nid = codec->start_nid; - for (i = 0; i < codec->num_nodes; i++, nid++) { - unsigned int caps = get_wcaps(codec, nid); - int type = get_wcaps_type(caps); - - if (type != AC_WID_AUD_IN || (caps & AC_WCAP_DIGITAL)) - continue; - adc_nids[nums] = nid; - if (++nums >= max_nums) - break; - } - spec->num_adc_nids = nums; - return nums; -} - -/* filter out invalid adc_nids that don't give all active input pins; - * if needed, check whether dynamic ADC-switching is available - */ -static int check_dyn_adc_switch(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - struct hda_input_mux *imux = &spec->input_mux; - hda_nid_t adc_nids[ARRAY_SIZE(spec->adc_nids)]; - int i, n, nums; - hda_nid_t pin, adc; - - again: - nums = 0; - for (n = 0; n < spec->num_adc_nids; n++) { - adc = spec->adc_nids[n]; - for (i = 0; i < imux->num_items; i++) { - pin = spec->imux_pins[i]; - if (!is_reachable_path(codec, pin, adc)) - break; - } - if (i >= imux->num_items) - adc_nids[nums++] = adc; - } - - if (!nums) { - if (spec->shared_mic_hp) { - spec->shared_mic_hp = 0; - imux->num_items = 1; - goto again; - } - - /* check whether ADC-switch is possible */ - for (i = 0; i < imux->num_items; i++) { - pin = spec->imux_pins[i]; - for (n = 0; n < spec->num_adc_nids; n++) { - adc = spec->adc_nids[n]; - if (is_reachable_path(codec, pin, adc)) { - spec->dyn_adc_idx[i] = n; - break; - } - } - } - - snd_printdd("realtek: enabling ADC switching\n"); - spec->dyn_adc_switch = 1; - } else if (nums != spec->num_adc_nids) { - memcpy(spec->adc_nids, adc_nids, nums * sizeof(hda_nid_t)); - spec->num_adc_nids = nums; - } - - if (imux->num_items == 1 || spec->shared_mic_hp) { - snd_printdd("realtek: reducing to a single ADC\n"); - spec->num_adc_nids = 1; /* reduce to a single ADC */ - } - - /* single index for individual volumes ctls */ - if (!spec->dyn_adc_switch && spec->multi_cap_vol) - spec->num_adc_nids = 1; - - return 0; -} - -/* templates for capture controls */ -static const struct snd_kcontrol_new cap_src_temp = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Input Source", - .info = alc_mux_enum_info, - .get = alc_mux_enum_get, - .put = alc_mux_enum_put, -}; - -static const struct snd_kcontrol_new cap_vol_temp = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Capture Volume", - .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | - SNDRV_CTL_ELEM_ACCESS_TLV_READ | - SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK), - .info = alc_cap_vol_info, - .get = alc_cap_vol_get, - .put = alc_cap_vol_put, - .tlv = { .c = alc_cap_vol_tlv }, -}; - -static const struct snd_kcontrol_new cap_sw_temp = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Capture Switch", - .info = alc_cap_sw_info, - .get = alc_cap_sw_get, - .put = alc_cap_sw_put, -}; - -static int parse_capvol_in_path(struct hda_codec *codec, struct nid_path *path) -{ - hda_nid_t nid; - int i, depth; - - path->ctls[NID_PATH_VOL_CTL] = path->ctls[NID_PATH_MUTE_CTL] = 0; - for (depth = 0; depth < 3; depth++) { - if (depth >= path->depth) - return -EINVAL; - i = path->depth - depth - 1; - nid = path->path[i]; - if (!path->ctls[NID_PATH_VOL_CTL]) { - if (nid_has_volume(codec, nid, HDA_OUTPUT)) - path->ctls[NID_PATH_VOL_CTL] = - HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT); - else if (nid_has_volume(codec, nid, HDA_INPUT)) { - int idx = path->idx[i]; - if (!depth && codec->single_adc_amp) - idx = 0; - path->ctls[NID_PATH_VOL_CTL] = - HDA_COMPOSE_AMP_VAL(nid, 3, idx, HDA_INPUT); - } - } - if (!path->ctls[NID_PATH_MUTE_CTL]) { - if (nid_has_mute(codec, nid, HDA_OUTPUT)) - path->ctls[NID_PATH_MUTE_CTL] = - HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT); - else if (nid_has_mute(codec, nid, HDA_INPUT)) { - int idx = path->idx[i]; - if (!depth && codec->single_adc_amp) - idx = 0; - path->ctls[NID_PATH_MUTE_CTL] = - HDA_COMPOSE_AMP_VAL(nid, 3, idx, HDA_INPUT); - } - } - } - return 0; -} - -static unsigned int amp_val_replace_channels(unsigned int val, unsigned int chs); - -static int add_single_cap_ctl(struct hda_codec *codec, const char *label, - int idx, bool is_switch, unsigned int ctl, - bool inv_dmic) -{ - struct alc_spec *spec = codec->spec; - char tmpname[44]; - int type = is_switch ? ALC_CTL_WIDGET_MUTE : ALC_CTL_WIDGET_VOL; - const char *sfx = is_switch ? "Switch" : "Volume"; - unsigned int chs = inv_dmic ? 1 : 3; - int err; - - if (!ctl) - return 0; - - if (label) - snprintf(tmpname, sizeof(tmpname), - "%s Capture %s", label, sfx); - else - snprintf(tmpname, sizeof(tmpname), - "Capture %s", sfx); - err = add_control(spec, type, tmpname, idx, - amp_val_replace_channels(ctl, chs)); - if (err < 0 || chs == 3) - return err; - - /* Make independent right kcontrol */ - if (label) - snprintf(tmpname, sizeof(tmpname), - "Inverted %s Capture %s", label, sfx); - else - snprintf(tmpname, sizeof(tmpname), - "Inverted Capture %s", sfx); - return add_control(spec, type, tmpname, idx, - amp_val_replace_channels(ctl, 2)); -} - -static bool is_inv_dmic_pin(struct hda_codec *codec, hda_nid_t nid) -{ - struct alc_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - unsigned int val; - int i; - - if (!spec->inv_dmic_split) - return false; - for (i = 0; i < cfg->num_inputs; i++) { - if (cfg->inputs[i].pin != nid) - continue; - if (cfg->inputs[i].type != AUTO_PIN_MIC) - return false; - val = snd_hda_codec_get_pincfg(codec, nid); - return snd_hda_get_input_pin_attr(val) == INPUT_PIN_ATTR_INT; - } - return false; -} - -/* create single (and simple) capture volume and switch controls */ -static int create_single_cap_vol_ctl(struct hda_codec *codec, int idx, - unsigned int vol_ctl, unsigned int sw_ctl, - bool inv_dmic) -{ - int err; - err = add_single_cap_ctl(codec, NULL, idx, false, vol_ctl, inv_dmic); - if (err < 0) - return err; - err = add_single_cap_ctl(codec, NULL, idx, true, sw_ctl, inv_dmic); - if (err < 0) - return err; - return 0; -} - -/* create bound capture volume and switch controls */ -static int create_bind_cap_vol_ctl(struct hda_codec *codec, int idx, - unsigned int vol_ctl, unsigned int sw_ctl) -{ - struct alc_spec *spec = codec->spec; - struct snd_kcontrol_new *knew; - - if (vol_ctl) { - knew = alc_kcontrol_new(spec, NULL, &cap_vol_temp); - if (!knew) - return -ENOMEM; - knew->index = idx; - knew->private_value = vol_ctl; - knew->subdevice = HDA_SUBDEV_AMP_FLAG; - } - if (sw_ctl) { - knew = alc_kcontrol_new(spec, NULL, &cap_sw_temp); - if (!knew) - return -ENOMEM; - knew->index = idx; - knew->private_value = sw_ctl; - knew->subdevice = HDA_SUBDEV_AMP_FLAG; - } - return 0; -} - -/* return the vol ctl when used first in the imux list */ -static unsigned int get_first_cap_ctl(struct hda_codec *codec, int idx, int type) -{ - struct alc_spec *spec = codec->spec; - struct nid_path *path; - unsigned int ctl; - int i; - - path = get_nid_path(codec, spec->imux_pins[idx], - get_adc_nid(codec, 0, idx)); - if (!path) - return 0; - ctl = path->ctls[type]; - if (!ctl) - return 0; - for (i = 0; i < idx - 1; i++) { - path = get_nid_path(codec, spec->imux_pins[i], - get_adc_nid(codec, 0, i)); - if (path && path->ctls[type] == ctl) - return 0; - } - return ctl; -} - -/* create individual capture volume and switch controls per input */ -static int create_multi_cap_vol_ctl(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - struct hda_input_mux *imux = &spec->input_mux; - int i, err, type, type_idx = 0; - const char *prev_label = NULL; - - for (i = 0; i < imux->num_items; i++) { - const char *label; - bool inv_dmic; - label = hda_get_autocfg_input_label(codec, &spec->autocfg, i); - if (prev_label && !strcmp(label, prev_label)) - type_idx++; - else - type_idx = 0; - prev_label = label; - inv_dmic = is_inv_dmic_pin(codec, spec->imux_pins[i]); - - for (type = 0; type < 2; type++) { - err = add_single_cap_ctl(codec, label, type_idx, type, - get_first_cap_ctl(codec, i, type), - inv_dmic); - if (err < 0) - return err; - } - } - return 0; -} - -static int create_capture_mixers(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - struct hda_input_mux *imux = &spec->input_mux; - int i, n, nums, err; - - if (spec->dyn_adc_switch) - nums = 1; - else - nums = spec->num_adc_nids; - - if (!spec->auto_mic && imux->num_items > 1) { - struct snd_kcontrol_new *knew; - knew = alc_kcontrol_new(spec, NULL, &cap_src_temp); - if (!knew) - return -ENOMEM; - knew->count = nums; - } - - for (n = 0; n < nums; n++) { - bool multi = false; - bool inv_dmic = false; - int vol, sw; - - vol = sw = 0; - for (i = 0; i < imux->num_items; i++) { - struct nid_path *path; - path = get_nid_path(codec, spec->imux_pins[i], - get_adc_nid(codec, n, i)); - if (!path) - continue; - parse_capvol_in_path(codec, path); - if (!vol) - vol = path->ctls[NID_PATH_VOL_CTL]; - else if (vol != path->ctls[NID_PATH_VOL_CTL]) - multi = true; - if (!sw) - sw = path->ctls[NID_PATH_MUTE_CTL]; - else if (sw != path->ctls[NID_PATH_MUTE_CTL]) - multi = true; - if (is_inv_dmic_pin(codec, spec->imux_pins[i])) - inv_dmic = true; - } - - if (!multi) - err = create_single_cap_vol_ctl(codec, n, vol, sw, - inv_dmic); - else if (!spec->multi_cap_vol) - err = create_bind_cap_vol_ctl(codec, n, vol, sw); - else - err = create_multi_cap_vol_ctl(codec); - if (err < 0) - return err; - } - - return 0; -} - -/* create playback/capture controls for input pins */ -static int alc_auto_create_input_ctls(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - const struct auto_pin_cfg *cfg = &spec->autocfg; - hda_nid_t mixer = spec->mixer_nid; - struct hda_input_mux *imux = &spec->input_mux; - int num_adcs; - int i, c, err, type_idx = 0; - const char *prev_label = NULL; - - num_adcs = alc_auto_fill_adc_nids(codec); - if (num_adcs < 0) - return 0; - - for (i = 0; i < cfg->num_inputs; i++) { - hda_nid_t pin; - const char *label; - bool imux_added; - - pin = cfg->inputs[i].pin; - if (!alc_is_input_pin(codec, pin)) - continue; - - label = hda_get_autocfg_input_label(codec, cfg, i); - if (spec->shared_mic_hp && !strcmp(label, "Misc")) - label = "Headphone Mic"; - if (prev_label && !strcmp(label, prev_label)) - type_idx++; - else - type_idx = 0; - prev_label = label; - - if (mixer) { - if (is_reachable_path(codec, pin, mixer)) { - err = new_analog_input(codec, pin, - label, type_idx, mixer); - if (err < 0) - return err; - } - } - - imux_added = false; - for (c = 0; c < num_adcs; c++) { - struct nid_path *path; - hda_nid_t adc = spec->adc_nids[c]; - - if (!is_reachable_path(codec, pin, adc)) - continue; - path = snd_array_new(&spec->paths); - if (!path) - return -ENOMEM; - memset(path, 0, sizeof(*path)); - if (!parse_nid_path(codec, pin, adc, 2, path)) { - snd_printd(KERN_ERR - "invalid input path 0x%x -> 0x%x\n", - pin, adc); - spec->paths.used--; - continue; - } - - if (!imux_added) { - spec->imux_pins[imux->num_items] = pin; - snd_hda_add_imux_item(imux, label, - imux->num_items, NULL); - imux_added = true; - } - } - } - - return 0; -} - -/* create a shared input with the headphone out */ -static int alc_auto_create_shared_input(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - unsigned int defcfg; - hda_nid_t nid; - - /* only one internal input pin? */ - if (cfg->num_inputs != 1) - return 0; - defcfg = snd_hda_codec_get_pincfg(codec, cfg->inputs[0].pin); - if (snd_hda_get_input_pin_attr(defcfg) != INPUT_PIN_ATTR_INT) - return 0; - - if (cfg->hp_outs == 1 && cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) - nid = cfg->hp_pins[0]; /* OK, we have a single HP-out */ - else if (cfg->line_outs == 1 && cfg->line_out_type == AUTO_PIN_HP_OUT) - nid = cfg->line_out_pins[0]; /* OK, we have a single line-out */ - else - return 0; /* both not available */ - - if (!(snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_IN)) - return 0; /* no input */ - - cfg->inputs[1].pin = nid; - cfg->inputs[1].type = AUTO_PIN_MIC; - cfg->num_inputs = 2; - spec->shared_mic_hp = 1; - snd_printdd("realtek: Enable shared I/O jack on NID 0x%x\n", nid); - return 0; -} - -static int get_pin_type(int line_out_type) -{ - if (line_out_type == AUTO_PIN_HP_OUT) - return PIN_HP; - else - return PIN_OUT; -} - -static void alc_auto_init_analog_input(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - int i; - - for (i = 0; i < cfg->num_inputs; i++) { - hda_nid_t nid = cfg->inputs[i].pin; - if (alc_is_input_pin(codec, nid)) - alc_set_input_pin(codec, nid, cfg->inputs[i].type); - - /* mute loopback inputs */ - if (spec->mixer_nid) { - struct nid_path *path; - path = get_nid_path(codec, nid, spec->mixer_nid); - if (path) - activate_path(codec, path, path->active, false); - } - } -} - -static bool alc_is_dac_already_used(struct hda_codec *codec, hda_nid_t nid) -{ - struct alc_spec *spec = codec->spec; - int i; - - for (i = 0; i < spec->paths.used; i++) { - struct nid_path *path = snd_array_elem(&spec->paths, i); - if (path->path[0] == nid) - return true; - } - return false; -} - -/* look for an empty DAC slot */ -static hda_nid_t alc_auto_look_for_dac(struct hda_codec *codec, hda_nid_t pin, - bool is_digital) -{ - struct alc_spec *spec = codec->spec; - bool cap_digital; - int i; - - for (i = 0; i < spec->num_all_dacs; i++) { - hda_nid_t nid = spec->all_dacs[i]; - if (!nid || alc_is_dac_already_used(codec, nid)) - continue; - cap_digital = !!(get_wcaps(codec, nid) & AC_WCAP_DIGITAL); - if (is_digital != cap_digital) - continue; - if (is_reachable_path(codec, nid, pin)) - return nid; - } - return 0; -} - -/* called recursively */ -static bool __parse_nid_path(struct hda_codec *codec, - hda_nid_t from_nid, hda_nid_t to_nid, - int with_aa_mix, struct nid_path *path, int depth) -{ - struct alc_spec *spec = codec->spec; - hda_nid_t conn[16]; - int i, nums; - - if (to_nid == spec->mixer_nid) { - if (!with_aa_mix) - return false; - with_aa_mix = 2; /* mark aa-mix is included */ - } - - nums = snd_hda_get_connections(codec, to_nid, conn, ARRAY_SIZE(conn)); - for (i = 0; i < nums; i++) { - if (conn[i] != from_nid) { - /* special case: when from_nid is 0, - * try to find an empty DAC - */ - if (from_nid || - get_wcaps_type(get_wcaps(codec, conn[i])) != AC_WID_AUD_OUT || - alc_is_dac_already_used(codec, conn[i])) - continue; - } - /* aa-mix is requested but not included? */ - if (!(spec->mixer_nid && with_aa_mix == 1)) - goto found; - } - if (depth >= MAX_NID_PATH_DEPTH) - return false; - for (i = 0; i < nums; i++) { - unsigned int type; - type = get_wcaps_type(get_wcaps(codec, conn[i])); - if (type == AC_WID_AUD_OUT || type == AC_WID_AUD_IN || - type == AC_WID_PIN) - continue; - if (__parse_nid_path(codec, from_nid, conn[i], - with_aa_mix, path, depth + 1)) - goto found; - } - return false; - - found: - path->path[path->depth] = conn[i]; - path->idx[path->depth + 1] = i; - if (nums > 1 && get_wcaps_type(get_wcaps(codec, to_nid)) != AC_WID_AUD_MIX) - path->multi[path->depth + 1] = 1; - path->depth++; - return true; -} - -/* parse the widget path from the given nid to the target nid; - * when @from_nid is 0, try to find an empty DAC; - * when @with_aa_mix is 0, paths with spec->mixer_nid are excluded. - * when @with_aa_mix is 1, paths without spec->mixer_nid are excluded. - * when @with_aa_mix is 2, no special handling about spec->mixer_nid. - */ -static bool parse_nid_path(struct hda_codec *codec, hda_nid_t from_nid, - hda_nid_t to_nid, int with_aa_mix, - struct nid_path *path) -{ - if (__parse_nid_path(codec, from_nid, to_nid, with_aa_mix, path, 1)) { - path->path[path->depth] = to_nid; - path->depth++; -#if 0 - snd_printdd("path: depth=%d, %02x/%02x/%02x/%02x/%02x\n", - path->depth, path->path[0], path->path[1], - path->path[2], path->path[3], path->path[4]); -#endif - return true; - } - return false; -} - -static hda_nid_t get_dac_if_single(struct hda_codec *codec, hda_nid_t pin) -{ - struct alc_spec *spec = codec->spec; - int i; - hda_nid_t nid_found = 0; - - for (i = 0; i < spec->num_all_dacs; i++) { - hda_nid_t nid = spec->all_dacs[i]; - if (!nid || alc_is_dac_already_used(codec, nid)) - continue; - if (is_reachable_path(codec, nid, pin)) { - if (nid_found) - return 0; - nid_found = nid; - } - } - return nid_found; -} - -static bool is_ctl_used(struct hda_codec *codec, unsigned int val, int type) -{ - struct alc_spec *spec = codec->spec; - int i; - - for (i = 0; i < spec->paths.used; i++) { - struct nid_path *path = snd_array_elem(&spec->paths, i); - if (path->ctls[type] == val) - return true; - } - return false; -} - -/* badness definition */ -enum { - /* No primary DAC is found for the main output */ - BAD_NO_PRIMARY_DAC = 0x10000, - /* No DAC is found for the extra output */ - BAD_NO_DAC = 0x4000, - /* No possible multi-ios */ - BAD_MULTI_IO = 0x103, - /* No individual DAC for extra output */ - BAD_NO_EXTRA_DAC = 0x102, - /* No individual DAC for extra surrounds */ - BAD_NO_EXTRA_SURR_DAC = 0x101, - /* Primary DAC shared with main surrounds */ - BAD_SHARED_SURROUND = 0x100, - /* Primary DAC shared with main CLFE */ - BAD_SHARED_CLFE = 0x10, - /* Primary DAC shared with extra surrounds */ - BAD_SHARED_EXTRA_SURROUND = 0x10, - /* Volume widget is shared */ - BAD_SHARED_VOL = 0x10, -}; - -static hda_nid_t alc_look_for_out_mute_nid(struct hda_codec *codec, - struct nid_path *path); -static hda_nid_t alc_look_for_out_vol_nid(struct hda_codec *codec, - struct nid_path *path); - -static struct nid_path *add_new_nid_path(struct hda_codec *codec, - hda_nid_t from_nid, hda_nid_t to_nid, - int with_aa_mix) -{ - struct alc_spec *spec = codec->spec; - struct nid_path *path; - - if (from_nid && to_nid && !is_reachable_path(codec, from_nid, to_nid)) - return NULL; - - path = snd_array_new(&spec->paths); - if (!path) - return NULL; - memset(path, 0, sizeof(*path)); - if (parse_nid_path(codec, from_nid, to_nid, with_aa_mix, path)) - return path; - /* push back */ - spec->paths.used--; - return NULL; -} - -/* get the path between the given NIDs; - * passing 0 to either @pin or @dac behaves as a wildcard - */ -static struct nid_path * -get_nid_path(struct hda_codec *codec, hda_nid_t from_nid, hda_nid_t to_nid) -{ - struct alc_spec *spec = codec->spec; - int i; - - for (i = 0; i < spec->paths.used; i++) { - struct nid_path *path = snd_array_elem(&spec->paths, i); - if (path->depth <= 0) - continue; - if ((!from_nid || path->path[0] == from_nid) && - (!to_nid || path->path[path->depth - 1] == to_nid)) - return path; - } - return NULL; -} - -/* look for widgets in the path between the given NIDs appropriate for - * volume and mute controls, and assign the values to ctls[]. - * - * When no appropriate widget is found in the path, the badness value - * is incremented depending on the situation. The function returns the - * total badness for both volume and mute controls. - */ -static int assign_out_path_ctls(struct hda_codec *codec, hda_nid_t pin, - hda_nid_t dac) -{ - struct nid_path *path = get_nid_path(codec, dac, pin); - hda_nid_t nid; - unsigned int val; - int badness = 0; - - if (!path) - return BAD_SHARED_VOL * 2; - nid = alc_look_for_out_vol_nid(codec, path); - if (nid) { - val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT); - if (is_ctl_used(codec, val, NID_PATH_VOL_CTL)) - badness += BAD_SHARED_VOL; - else - path->ctls[NID_PATH_VOL_CTL] = val; - } else - badness += BAD_SHARED_VOL; - nid = alc_look_for_out_mute_nid(codec, path); - if (nid) { - unsigned int wid_type = get_wcaps_type(get_wcaps(codec, nid)); - if (wid_type == AC_WID_PIN || wid_type == AC_WID_AUD_OUT || - nid_has_mute(codec, nid, HDA_OUTPUT)) - val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT); - else - val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT); - if (is_ctl_used(codec, val, NID_PATH_MUTE_CTL)) - badness += BAD_SHARED_VOL; - else - path->ctls[NID_PATH_MUTE_CTL] = val; - } else - badness += BAD_SHARED_VOL; - return badness; -} - -struct badness_table { - int no_primary_dac; /* no primary DAC */ - int no_dac; /* no secondary DACs */ - int shared_primary; /* primary DAC is shared with main output */ - int shared_surr; /* secondary DAC shared with main or primary */ - int shared_clfe; /* third DAC shared with main or primary */ - int shared_surr_main; /* secondary DAC sahred with main/DAC0 */ -}; - -static struct badness_table main_out_badness = { - .no_primary_dac = BAD_NO_PRIMARY_DAC, - .no_dac = BAD_NO_DAC, - .shared_primary = BAD_NO_PRIMARY_DAC, - .shared_surr = BAD_SHARED_SURROUND, - .shared_clfe = BAD_SHARED_CLFE, - .shared_surr_main = BAD_SHARED_SURROUND, -}; - -static struct badness_table extra_out_badness = { - .no_primary_dac = BAD_NO_DAC, - .no_dac = BAD_NO_DAC, - .shared_primary = BAD_NO_EXTRA_DAC, - .shared_surr = BAD_SHARED_EXTRA_SURROUND, - .shared_clfe = BAD_SHARED_EXTRA_SURROUND, - .shared_surr_main = BAD_NO_EXTRA_SURR_DAC, -}; - -/* try to assign DACs to pins and return the resultant badness */ -static int alc_auto_fill_dacs(struct hda_codec *codec, int num_outs, - const hda_nid_t *pins, hda_nid_t *dacs, - const struct badness_table *bad) -{ - struct alc_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - int i, j; - int badness = 0; - hda_nid_t dac; - - if (!num_outs) - return 0; - - for (i = 0; i < num_outs; i++) { - hda_nid_t pin = pins[i]; - if (!dacs[i]) - dacs[i] = alc_auto_look_for_dac(codec, pin, false); - if (!dacs[i] && !i) { - for (j = 1; j < num_outs; j++) { - if (is_reachable_path(codec, dacs[j], pin)) { - dacs[0] = dacs[j]; - dacs[j] = 0; - break; - } - } - } - dac = dacs[i]; - if (!dac) { - if (is_reachable_path(codec, dacs[0], pin)) - dac = dacs[0]; - else if (cfg->line_outs > i && - is_reachable_path(codec, spec->private_dac_nids[i], pin)) - dac = spec->private_dac_nids[i]; - if (dac) { - if (!i) - badness += bad->shared_primary; - else if (i == 1) - badness += bad->shared_surr; - else - badness += bad->shared_clfe; - } else if (is_reachable_path(codec, spec->private_dac_nids[0], pin)) { - dac = spec->private_dac_nids[0]; - badness += bad->shared_surr_main; - } else if (!i) - badness += bad->no_primary_dac; - else - badness += bad->no_dac; - } - if (!add_new_nid_path(codec, dac, pin, 0)) - dac = dacs[i] = 0; - if (dac) - badness += assign_out_path_ctls(codec, pin, dac); - } - - return badness; -} - -static int alc_auto_fill_multi_ios(struct hda_codec *codec, - hda_nid_t reference_pin, - bool hardwired, int offset); - -static bool alc_map_singles(struct hda_codec *codec, int outs, - const hda_nid_t *pins, hda_nid_t *dacs) -{ - int i; - bool found = false; - for (i = 0; i < outs; i++) { - hda_nid_t dac; - if (dacs[i]) - continue; - dac = get_dac_if_single(codec, pins[i]); - if (!dac) - continue; - if (add_new_nid_path(codec, dac, pins[i], 0)) { - dacs[i] = dac; - found = true; - } - } - return found; -} - -/* fill in the dac_nids table from the parsed pin configuration */ -static int fill_and_eval_dacs(struct hda_codec *codec, - bool fill_hardwired, - bool fill_mio_first) -{ - struct alc_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - int i, err, badness; - - /* set num_dacs once to full for alc_auto_look_for_dac() */ - spec->multiout.num_dacs = cfg->line_outs; - spec->multiout.dac_nids = spec->private_dac_nids; - memset(spec->private_dac_nids, 0, sizeof(spec->private_dac_nids)); - memset(spec->multiout.hp_out_nid, 0, sizeof(spec->multiout.hp_out_nid)); - memset(spec->multiout.extra_out_nid, 0, sizeof(spec->multiout.extra_out_nid)); - spec->multi_ios = 0; - snd_array_free(&spec->paths); - badness = 0; - - /* fill hard-wired DACs first */ - if (fill_hardwired) { - bool mapped; - do { - mapped = alc_map_singles(codec, cfg->line_outs, - cfg->line_out_pins, - spec->private_dac_nids); - mapped |= alc_map_singles(codec, cfg->hp_outs, - cfg->hp_pins, - spec->multiout.hp_out_nid); - mapped |= alc_map_singles(codec, cfg->speaker_outs, - cfg->speaker_pins, - spec->multiout.extra_out_nid); - if (fill_mio_first && cfg->line_outs == 1 && - cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) { - err = alc_auto_fill_multi_ios(codec, cfg->line_out_pins[0], true, 0); - if (!err) - mapped = true; - } - } while (mapped); - } - - badness += alc_auto_fill_dacs(codec, cfg->line_outs, cfg->line_out_pins, - spec->private_dac_nids, - &main_out_badness); - - /* re-count num_dacs and squash invalid entries */ - spec->multiout.num_dacs = 0; - for (i = 0; i < cfg->line_outs; i++) { - if (spec->private_dac_nids[i]) - spec->multiout.num_dacs++; - else { - memmove(spec->private_dac_nids + i, - spec->private_dac_nids + i + 1, - sizeof(hda_nid_t) * (cfg->line_outs - i - 1)); - spec->private_dac_nids[cfg->line_outs - 1] = 0; - } - } - - if (fill_mio_first && - cfg->line_outs == 1 && cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) { - /* try to fill multi-io first */ - err = alc_auto_fill_multi_ios(codec, cfg->line_out_pins[0], false, 0); - if (err < 0) - return err; - /* we don't count badness at this stage yet */ - } - - if (cfg->line_out_type != AUTO_PIN_HP_OUT) { - err = alc_auto_fill_dacs(codec, cfg->hp_outs, cfg->hp_pins, - spec->multiout.hp_out_nid, - &extra_out_badness); - if (err < 0) - return err; - badness += err; - } - if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) { - err = alc_auto_fill_dacs(codec, cfg->speaker_outs, - cfg->speaker_pins, - spec->multiout.extra_out_nid, - &extra_out_badness); - if (err < 0) - return err; - badness += err; - } - if (cfg->line_outs == 1 && cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) { - err = alc_auto_fill_multi_ios(codec, cfg->line_out_pins[0], false, 0); - if (err < 0) - return err; - badness += err; - } - if (cfg->hp_outs && cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) { - /* try multi-ios with HP + inputs */ - int offset = 0; - if (cfg->line_outs >= 3) - offset = 1; - err = alc_auto_fill_multi_ios(codec, cfg->hp_pins[0], false, - offset); - if (err < 0) - return err; - badness += err; - } - - if (spec->multi_ios == 2) { - for (i = 0; i < 2; i++) - spec->private_dac_nids[spec->multiout.num_dacs++] = - spec->multi_io[i].dac; - spec->ext_channel_count = 2; - } else if (spec->multi_ios) { - spec->multi_ios = 0; - badness += BAD_MULTI_IO; - } - - return badness; -} - -#define DEBUG_BADNESS - -#ifdef DEBUG_BADNESS -#define debug_badness snd_printdd -#else -#define debug_badness(...) -#endif - -static void debug_show_configs(struct alc_spec *spec, struct auto_pin_cfg *cfg) -{ - debug_badness("multi_outs = %x/%x/%x/%x : %x/%x/%x/%x\n", - cfg->line_out_pins[0], cfg->line_out_pins[1], - cfg->line_out_pins[2], cfg->line_out_pins[2], - spec->multiout.dac_nids[0], - spec->multiout.dac_nids[1], - spec->multiout.dac_nids[2], - spec->multiout.dac_nids[3]); - if (spec->multi_ios > 0) - debug_badness("multi_ios(%d) = %x/%x : %x/%x\n", - spec->multi_ios, - spec->multi_io[0].pin, spec->multi_io[1].pin, - spec->multi_io[0].dac, spec->multi_io[1].dac); - debug_badness("hp_outs = %x/%x/%x/%x : %x/%x/%x/%x\n", - cfg->hp_pins[0], cfg->hp_pins[1], - cfg->hp_pins[2], cfg->hp_pins[2], - spec->multiout.hp_out_nid[0], - spec->multiout.hp_out_nid[1], - spec->multiout.hp_out_nid[2], - spec->multiout.hp_out_nid[3]); - debug_badness("spk_outs = %x/%x/%x/%x : %x/%x/%x/%x\n", - cfg->speaker_pins[0], cfg->speaker_pins[1], - cfg->speaker_pins[2], cfg->speaker_pins[3], - spec->multiout.extra_out_nid[0], - spec->multiout.extra_out_nid[1], - spec->multiout.extra_out_nid[2], - spec->multiout.extra_out_nid[3]); -} - -/* find all available DACs of the codec */ -static void alc_fill_all_nids(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - int i; - hda_nid_t nid = codec->start_nid; - - spec->num_all_dacs = 0; - memset(spec->all_dacs, 0, sizeof(spec->all_dacs)); - for (i = 0; i < codec->num_nodes; i++, nid++) { - if (get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_AUD_OUT) - continue; - if (spec->num_all_dacs >= ARRAY_SIZE(spec->all_dacs)) { - snd_printk(KERN_ERR "hda: Too many DACs!\n"); - break; - } - spec->all_dacs[spec->num_all_dacs++] = nid; - } -} - -static int alc_auto_fill_dac_nids(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - struct auto_pin_cfg *best_cfg; - int best_badness = INT_MAX; - int badness; - bool fill_hardwired = true, fill_mio_first = true; - bool best_wired = true, best_mio = true; - bool hp_spk_swapped = false; - - alc_fill_all_nids(codec); - - best_cfg = kmalloc(sizeof(*best_cfg), GFP_KERNEL); - if (!best_cfg) - return -ENOMEM; - *best_cfg = *cfg; - - for (;;) { - badness = fill_and_eval_dacs(codec, fill_hardwired, - fill_mio_first); - if (badness < 0) { - kfree(best_cfg); - return badness; - } - debug_badness("==> lo_type=%d, wired=%d, mio=%d, badness=0x%x\n", - cfg->line_out_type, fill_hardwired, fill_mio_first, - badness); - debug_show_configs(spec, cfg); - if (badness < best_badness) { - best_badness = badness; - *best_cfg = *cfg; - best_wired = fill_hardwired; - best_mio = fill_mio_first; - } - if (!badness) - break; - fill_mio_first = !fill_mio_first; - if (!fill_mio_first) - continue; - fill_hardwired = !fill_hardwired; - if (!fill_hardwired) - continue; - if (hp_spk_swapped) - break; - hp_spk_swapped = true; - if (cfg->speaker_outs > 0 && - cfg->line_out_type == AUTO_PIN_HP_OUT) { - cfg->hp_outs = cfg->line_outs; - memcpy(cfg->hp_pins, cfg->line_out_pins, - sizeof(cfg->hp_pins)); - cfg->line_outs = cfg->speaker_outs; - memcpy(cfg->line_out_pins, cfg->speaker_pins, - sizeof(cfg->speaker_pins)); - cfg->speaker_outs = 0; - memset(cfg->speaker_pins, 0, sizeof(cfg->speaker_pins)); - cfg->line_out_type = AUTO_PIN_SPEAKER_OUT; - fill_hardwired = true; - continue; - } - if (cfg->hp_outs > 0 && - cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) { - cfg->speaker_outs = cfg->line_outs; - memcpy(cfg->speaker_pins, cfg->line_out_pins, - sizeof(cfg->speaker_pins)); - cfg->line_outs = cfg->hp_outs; - memcpy(cfg->line_out_pins, cfg->hp_pins, - sizeof(cfg->hp_pins)); - cfg->hp_outs = 0; - memset(cfg->hp_pins, 0, sizeof(cfg->hp_pins)); - cfg->line_out_type = AUTO_PIN_HP_OUT; - fill_hardwired = true; - continue; - } - break; - } - - if (badness) { - *cfg = *best_cfg; - fill_and_eval_dacs(codec, best_wired, best_mio); - } - debug_badness("==> Best config: lo_type=%d, wired=%d, mio=%d\n", - cfg->line_out_type, best_wired, best_mio); - debug_show_configs(spec, cfg); - - if (cfg->line_out_pins[0]) { - struct nid_path *path = get_nid_path(codec, - spec->multiout.dac_nids[0], - cfg->line_out_pins[0]); - if (path) - spec->vmaster_nid = alc_look_for_out_vol_nid(codec, path); - } - - kfree(best_cfg); - return 0; -} - -/* replace the channels in the composed amp value with the given number */ -static unsigned int amp_val_replace_channels(unsigned int val, unsigned int chs) -{ - val &= ~(0x3U << 16); - val |= chs << 16; - return val; -} - -static int alc_auto_add_vol_ctl(struct hda_codec *codec, - const char *pfx, int cidx, - unsigned int chs, - struct nid_path *path) -{ - unsigned int val; - if (!path) - return 0; - val = path->ctls[NID_PATH_VOL_CTL]; - if (!val) - return 0; - val = amp_val_replace_channels(val, chs); - return __add_pb_vol_ctrl(codec->spec, ALC_CTL_WIDGET_VOL, pfx, cidx, val); -} - -/* return the channel bits suitable for the given path->ctls[] */ -static int get_default_ch_nums(struct hda_codec *codec, struct nid_path *path, - int type) -{ - int chs = 1; /* mono (left only) */ - if (path) { - hda_nid_t nid = get_amp_nid_(path->ctls[type]); - if (nid && (get_wcaps(codec, nid) & AC_WCAP_STEREO)) - chs = 3; /* stereo */ - } - return chs; -} - -static int alc_auto_add_stereo_vol(struct hda_codec *codec, - const char *pfx, int cidx, - struct nid_path *path) -{ - int chs = get_default_ch_nums(codec, path, NID_PATH_VOL_CTL); - return alc_auto_add_vol_ctl(codec, pfx, cidx, chs, path); -} - -/* create a mute-switch for the given mixer widget; - * if it has multiple sources (e.g. DAC and loopback), create a bind-mute - */ -static int alc_auto_add_sw_ctl(struct hda_codec *codec, - const char *pfx, int cidx, - unsigned int chs, - struct nid_path *path) -{ - unsigned int val; - int type = ALC_CTL_WIDGET_MUTE; - - if (!path) - return 0; - val = path->ctls[NID_PATH_MUTE_CTL]; - if (!val) - return 0; - val = amp_val_replace_channels(val, chs); - if (get_amp_direction_(val) == HDA_INPUT) { - hda_nid_t nid = get_amp_nid_(val); - int nums = snd_hda_get_num_conns(codec, nid); - if (nums > 1) { - type = ALC_CTL_BIND_MUTE; - val |= nums << 19; - } - } - return __add_pb_sw_ctrl(codec->spec, type, pfx, cidx, val); -} - -static int alc_auto_add_stereo_sw(struct hda_codec *codec, const char *pfx, - int cidx, struct nid_path *path) -{ - int chs = get_default_ch_nums(codec, path, NID_PATH_MUTE_CTL); - return alc_auto_add_sw_ctl(codec, pfx, cidx, chs, path); -} - -static hda_nid_t alc_look_for_out_mute_nid(struct hda_codec *codec, - struct nid_path *path) -{ - int i; - - for (i = path->depth - 1; i >= 0; i--) { - if (nid_has_mute(codec, path->path[i], HDA_OUTPUT)) - return path->path[i]; - if (i != path->depth - 1 && i != 0 && - nid_has_mute(codec, path->path[i], HDA_INPUT)) - return path->path[i]; - } - return 0; -} - -static hda_nid_t alc_look_for_out_vol_nid(struct hda_codec *codec, - struct nid_path *path) -{ - int i; - - for (i = path->depth - 1; i >= 0; i--) { - if (nid_has_volume(codec, path->path[i], HDA_OUTPUT)) - return path->path[i]; - } - return 0; -} - -/* add playback controls from the parsed DAC table */ -static int alc_auto_create_multi_out_ctls(struct hda_codec *codec, - const struct auto_pin_cfg *cfg) -{ - struct alc_spec *spec = codec->spec; - int i, err, noutputs; - - noutputs = cfg->line_outs; - if (spec->multi_ios > 0 && cfg->line_outs < 3) - noutputs += spec->multi_ios; - - for (i = 0; i < noutputs; i++) { - const char *name; - int index; - hda_nid_t dac, pin; - struct nid_path *path; - - dac = spec->multiout.dac_nids[i]; - if (!dac) - continue; - if (i >= cfg->line_outs) { - pin = spec->multi_io[i - 1].pin; - index = 0; - name = channel_name[i]; - } else { - pin = cfg->line_out_pins[i]; - name = alc_get_line_out_pfx(spec, i, true, &index); - } - - path = get_nid_path(codec, dac, pin); - if (!path) - continue; - if (!name || !strcmp(name, "CLFE")) { - /* Center/LFE */ - err = alc_auto_add_vol_ctl(codec, "Center", 0, 1, path); - if (err < 0) - return err; - err = alc_auto_add_vol_ctl(codec, "LFE", 0, 2, path); - if (err < 0) - return err; - err = alc_auto_add_sw_ctl(codec, "Center", 0, 1, path); - if (err < 0) - return err; - err = alc_auto_add_sw_ctl(codec, "LFE", 0, 2, path); - if (err < 0) - return err; - } else { - err = alc_auto_add_stereo_vol(codec, name, index, path); - if (err < 0) - return err; - err = alc_auto_add_stereo_sw(codec, name, index, path); - if (err < 0) - return err; - } - } - return 0; -} - -static int alc_auto_create_extra_out(struct hda_codec *codec, hda_nid_t pin, - hda_nid_t dac, const char *pfx, - int cidx) -{ - struct nid_path *path; - int err; - - path = get_nid_path(codec, dac, pin); - if (!path) - return 0; - /* bind volume control will be created in the case of dac = 0 */ - if (dac) { - err = alc_auto_add_stereo_vol(codec, pfx, cidx, path); - if (err < 0) - return err; - } - err = alc_auto_add_stereo_sw(codec, pfx, cidx, path); - if (err < 0) - return err; - return 0; -} - -static struct hda_bind_ctls *new_bind_ctl(struct hda_codec *codec, - unsigned int nums, - struct hda_ctl_ops *ops) -{ - struct alc_spec *spec = codec->spec; - struct hda_bind_ctls **ctlp, *ctl; - ctlp = snd_array_new(&spec->bind_ctls); - if (!ctlp) - return NULL; - ctl = kzalloc(sizeof(*ctl) + sizeof(long) * (nums + 1), GFP_KERNEL); - *ctlp = ctl; - if (ctl) - ctl->ops = ops; - return ctl; -} - -/* add playback controls for speaker and HP outputs */ -static int alc_auto_create_extra_outs(struct hda_codec *codec, int num_pins, - const hda_nid_t *pins, - const hda_nid_t *dacs, - const char *pfx) -{ - struct alc_spec *spec = codec->spec; - struct hda_bind_ctls *ctl; - char name[32]; - int i, n, err; - - if (!num_pins || !pins[0]) - return 0; - - if (num_pins == 1) { - hda_nid_t dac = *dacs; - if (!dac) - dac = spec->multiout.dac_nids[0]; - return alc_auto_create_extra_out(codec, *pins, dac, pfx, 0); - } - - for (i = 0; i < num_pins; i++) { - hda_nid_t dac; - if (dacs[num_pins - 1]) - dac = dacs[i]; /* with individual volumes */ - else - dac = 0; - if (num_pins == 2 && i == 1 && !strcmp(pfx, "Speaker")) { - err = alc_auto_create_extra_out(codec, pins[i], dac, - "Bass Speaker", 0); - } else if (num_pins >= 3) { - snprintf(name, sizeof(name), "%s %s", - pfx, channel_name[i]); - err = alc_auto_create_extra_out(codec, pins[i], dac, - name, 0); - } else { - err = alc_auto_create_extra_out(codec, pins[i], dac, - pfx, i); - } - if (err < 0) - return err; - } - if (dacs[num_pins - 1]) - return 0; - - /* Let's create a bind-controls for volumes */ - ctl = new_bind_ctl(codec, num_pins, &snd_hda_bind_vol); - if (!ctl) - return -ENOMEM; - n = 0; - for (i = 0; i < num_pins; i++) { - hda_nid_t vol; - struct nid_path *path; - if (!pins[i] || !dacs[i]) - continue; - path = get_nid_path(codec, dacs[i], pins[i]); - if (!path) - continue; - vol = alc_look_for_out_vol_nid(codec, path); - if (vol) - ctl->values[n++] = - HDA_COMPOSE_AMP_VAL(vol, 3, 0, HDA_OUTPUT); - } - if (n) { - snprintf(name, sizeof(name), "%s Playback Volume", pfx); - err = add_control(spec, ALC_CTL_BIND_VOL, name, 0, (long)ctl); - if (err < 0) - return err; - } - return 0; -} - -static int alc_auto_create_hp_out(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - return alc_auto_create_extra_outs(codec, spec->autocfg.hp_outs, - spec->autocfg.hp_pins, - spec->multiout.hp_out_nid, - "Headphone"); -} - -static int alc_auto_create_speaker_out(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - return alc_auto_create_extra_outs(codec, spec->autocfg.speaker_outs, - spec->autocfg.speaker_pins, - spec->multiout.extra_out_nid, - "Speaker"); -} - -/* check whether a control with the given (nid, dir, idx) was assigned */ -static bool is_ctl_associated(struct hda_codec *codec, hda_nid_t nid, - int dir, int idx) -{ - struct alc_spec *spec = codec->spec; - int i, type; - - for (i = 0; i < spec->paths.used; i++) { - struct nid_path *p = snd_array_elem(&spec->paths, i); - if (p->depth <= 0) - continue; - for (type = 0; type < NID_PATH_NUM_CTLS; type++) { - unsigned int val = p->ctls[type]; - if (get_amp_nid_(val) == nid && - get_amp_direction_(val) == dir && - get_amp_index_(val) == idx) - return true; - } - } - return false; -} - -/* can have the amp-in capability? */ -static bool has_amp_in(struct hda_codec *codec, struct nid_path *path, int idx) -{ - hda_nid_t nid = path->path[idx]; - unsigned int caps = get_wcaps(codec, nid); - unsigned int type = get_wcaps_type(caps); - - if (!(caps & AC_WCAP_IN_AMP)) - return false; - if (type == AC_WID_PIN && idx > 0) /* only for input pins */ - return false; - return true; -} - -/* can have the amp-out capability? */ -static bool has_amp_out(struct hda_codec *codec, struct nid_path *path, int idx) -{ - hda_nid_t nid = path->path[idx]; - unsigned int caps = get_wcaps(codec, nid); - unsigned int type = get_wcaps_type(caps); - - if (!(caps & AC_WCAP_OUT_AMP)) - return false; - if (type == AC_WID_PIN && !idx) /* only for output pins */ - return false; - return true; -} - -/* check whether the given (nid,dir,idx) is active */ -static bool is_active_nid(struct hda_codec *codec, hda_nid_t nid, - unsigned int idx, unsigned int dir) -{ - struct alc_spec *spec = codec->spec; - int i, n; - - for (n = 0; n < spec->paths.used; n++) { - struct nid_path *path = snd_array_elem(&spec->paths, n); - if (!path->active) - continue; - for (i = 0; i < path->depth; i++) { - if (path->path[i] == nid) { - if (dir == HDA_OUTPUT || path->idx[i] == idx) - return true; - break; - } - } - } - return false; -} - -/* get the default amp value for the target state */ -static int get_amp_val_to_activate(struct hda_codec *codec, hda_nid_t nid, - int dir, bool enable) -{ - unsigned int caps; - unsigned int val = 0; - - caps = query_amp_caps(codec, nid, dir); - if (caps & AC_AMPCAP_NUM_STEPS) { - /* set to 0dB */ - if (enable) - val = (caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT; - } - if (caps & AC_AMPCAP_MUTE) { - if (!enable) - val |= HDA_AMP_MUTE; - } - return val; -} - -/* initialize the amp value (only at the first time) */ -static void init_amp(struct hda_codec *codec, hda_nid_t nid, int dir, int idx) -{ - int val = get_amp_val_to_activate(codec, nid, dir, false); - snd_hda_codec_amp_init_stereo(codec, nid, dir, idx, 0xff, val); -} - -static void activate_amp(struct hda_codec *codec, hda_nid_t nid, int dir, - int idx, bool enable) -{ - int val; - if (is_ctl_associated(codec, nid, dir, idx) || - is_active_nid(codec, nid, dir, idx)) - return; - val = get_amp_val_to_activate(codec, nid, dir, enable); - snd_hda_codec_amp_stereo(codec, nid, dir, idx, 0xff, val); -} - -static void activate_amp_out(struct hda_codec *codec, struct nid_path *path, - int i, bool enable) -{ - hda_nid_t nid = path->path[i]; - init_amp(codec, nid, HDA_OUTPUT, 0); - activate_amp(codec, nid, HDA_OUTPUT, 0, enable); -} - -static void activate_amp_in(struct hda_codec *codec, struct nid_path *path, - int i, bool enable, bool add_aamix) -{ - struct alc_spec *spec = codec->spec; - hda_nid_t conn[16]; - int n, nums, idx; - int type; - hda_nid_t nid = path->path[i]; - - nums = snd_hda_get_connections(codec, nid, conn, ARRAY_SIZE(conn)); - type = get_wcaps_type(get_wcaps(codec, nid)); - if (type == AC_WID_PIN || - (type == AC_WID_AUD_IN && codec->single_adc_amp)) { - nums = 1; - idx = 0; - } else - idx = path->idx[i]; - - for (n = 0; n < nums; n++) - init_amp(codec, nid, HDA_INPUT, n); - - if (is_ctl_associated(codec, nid, HDA_INPUT, idx)) - return; - - /* here is a little bit tricky in comparison with activate_amp_out(); - * when aa-mixer is available, we need to enable the path as well - */ - for (n = 0; n < nums; n++) { - if (n != idx && (!add_aamix || conn[n] != spec->mixer_nid)) - continue; - activate_amp(codec, nid, HDA_INPUT, n, enable); - } -} - -static void activate_path(struct hda_codec *codec, struct nid_path *path, - bool enable, bool add_aamix) -{ - int i; - - if (!enable) - path->active = false; - - for (i = path->depth - 1; i >= 0; i--) { - if (enable && path->multi[i]) - snd_hda_codec_write_cache(codec, path->path[i], 0, - AC_VERB_SET_CONNECT_SEL, - path->idx[i]); - if (has_amp_in(codec, path, i)) - activate_amp_in(codec, path, i, enable, add_aamix); - if (has_amp_out(codec, path, i)) - activate_amp_out(codec, path, i, enable); - } - - if (enable) - path->active = true; -} - -/* configure the path from the given dac to the pin as the proper output */ -static void alc_auto_set_output_and_unmute(struct hda_codec *codec, - hda_nid_t pin, int pin_type, - hda_nid_t dac) -{ - struct nid_path *path; - - snd_hda_set_pin_ctl_cache(codec, pin, pin_type); - path = get_nid_path(codec, dac, pin); - if (!path) - return; - if (path->active) - return; - activate_path(codec, path, true, true); -} - -static void alc_auto_init_multi_out(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - int pin_type = get_pin_type(spec->autocfg.line_out_type); - int i; +} - for (i = 0; i <= HDA_SIDE; i++) { - hda_nid_t nid = spec->autocfg.line_out_pins[i]; - if (nid) - alc_auto_set_output_and_unmute(codec, nid, pin_type, - spec->multiout.dac_nids[i]); - } -} +#ifdef CONFIG_SND_HDA_INPUT_BEEP +/* additional beep mixers; the actual parameters are overwritten at build */ +static const struct snd_kcontrol_new alc_beep_mixer[] = { + HDA_CODEC_VOLUME("Beep Playback Volume", 0, 0, HDA_INPUT), + HDA_CODEC_MUTE_BEEP("Beep Playback Switch", 0, 0, HDA_INPUT), + { } /* end */ +}; +#endif -static void alc_auto_init_extra_out(struct hda_codec *codec) +static int alc_build_controls(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - int i; - hda_nid_t pin, dac; + int i, err; - for (i = 0; i < spec->autocfg.hp_outs; i++) { - if (spec->autocfg.line_out_type == AUTO_PIN_HP_OUT) - break; - pin = spec->autocfg.hp_pins[i]; - if (!pin) - break; - dac = spec->multiout.hp_out_nid[i]; - if (!dac) { - if (i > 0 && spec->multiout.hp_out_nid[0]) - dac = spec->multiout.hp_out_nid[0]; - else - dac = spec->multiout.dac_nids[0]; - } - alc_auto_set_output_and_unmute(codec, pin, PIN_HP, dac); + err = snd_hda_gen_build_controls(codec); + if (err < 0) + return err; + + for (i = 0; i < spec->num_mixers; i++) { + err = snd_hda_add_new_ctls(codec, spec->mixers[i]); + if (err < 0) + return err; } - for (i = 0; i < spec->autocfg.speaker_outs; i++) { - if (spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT) - break; - pin = spec->autocfg.speaker_pins[i]; - if (!pin) - break; - dac = spec->multiout.extra_out_nid[i]; - if (!dac) { - if (i > 0 && spec->multiout.extra_out_nid[0]) - dac = spec->multiout.extra_out_nid[0]; - else - dac = spec->multiout.dac_nids[0]; + +#ifdef CONFIG_SND_HDA_INPUT_BEEP + /* create beep controls if needed */ + if (spec->beep_amp) { + const struct snd_kcontrol_new *knew; + for (knew = alc_beep_mixer; knew->name; knew++) { + struct snd_kcontrol *kctl; + kctl = snd_ctl_new1(knew, codec); + if (!kctl) + return -ENOMEM; + kctl->private_value = spec->beep_amp; + err = snd_hda_ctl_add(codec, 0, kctl); + if (err < 0) + return err; } - alc_auto_set_output_and_unmute(codec, pin, PIN_OUT, dac); } -} +#endif -/* check whether the given pin can be a multi-io pin */ -static bool can_be_multiio_pin(struct hda_codec *codec, - unsigned int location, hda_nid_t nid) -{ - unsigned int defcfg, caps; - - defcfg = snd_hda_codec_get_pincfg(codec, nid); - if (get_defcfg_connect(defcfg) != AC_JACK_PORT_COMPLEX) - return false; - if (location && get_defcfg_location(defcfg) != location) - return false; - caps = snd_hda_query_pin_caps(codec, nid); - if (!(caps & AC_PINCAP_OUT)) - return false; - return true; + alc_apply_fixup(codec, ALC_FIXUP_ACT_BUILD); + return 0; } + /* - * multi-io helper - * - * When hardwired is set, try to fill ony hardwired pins, and returns - * zero if any pins are filled, non-zero if nothing found. - * When hardwired is off, try to fill possible input pins, and returns - * the badness value. + * Common callbacks */ -static int alc_auto_fill_multi_ios(struct hda_codec *codec, - hda_nid_t reference_pin, - bool hardwired, int offset) + +static int alc_init(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - int type, i, j, dacs, num_pins, old_pins; - unsigned int defcfg = snd_hda_codec_get_pincfg(codec, reference_pin); - unsigned int location = get_defcfg_location(defcfg); - int badness = 0; - - old_pins = spec->multi_ios; - if (old_pins >= 2) - goto end_fill; - - num_pins = 0; - for (type = AUTO_PIN_LINE_IN; type >= AUTO_PIN_MIC; type--) { - for (i = 0; i < cfg->num_inputs; i++) { - if (cfg->inputs[i].type != type) - continue; - if (can_be_multiio_pin(codec, location, - cfg->inputs[i].pin)) - num_pins++; - } - } - if (num_pins < 2) - goto end_fill; - - dacs = spec->multiout.num_dacs; - for (type = AUTO_PIN_LINE_IN; type >= AUTO_PIN_MIC; type--) { - for (i = 0; i < cfg->num_inputs; i++) { - hda_nid_t nid = cfg->inputs[i].pin; - hda_nid_t dac = 0; - - if (cfg->inputs[i].type != type) - continue; - if (!can_be_multiio_pin(codec, location, nid)) - continue; - for (j = 0; j < spec->multi_ios; j++) { - if (nid == spec->multi_io[j].pin) - break; - } - if (j < spec->multi_ios) - continue; - - if (offset && offset + spec->multi_ios < dacs) { - dac = spec->private_dac_nids[offset + spec->multi_ios]; - if (!is_reachable_path(codec, dac, nid)) - dac = 0; - } - if (hardwired) - dac = get_dac_if_single(codec, nid); - else if (!dac) - dac = alc_auto_look_for_dac(codec, nid, false); - if (!dac) { - badness++; - continue; - } - if (!add_new_nid_path(codec, dac, nid, 0)) { - badness++; - continue; - } - spec->multi_io[spec->multi_ios].pin = nid; - spec->multi_io[spec->multi_ios].dac = dac; - spec->multi_ios++; - if (spec->multi_ios >= 2) - break; - } - } - end_fill: - if (badness) - badness = BAD_MULTI_IO; - if (old_pins == spec->multi_ios) { - if (hardwired) - return 1; /* nothing found */ - else - return badness; /* no badness if nothing found */ - } - if (!hardwired && spec->multi_ios < 2) { - /* cancel newly assigned paths */ - spec->paths.used -= spec->multi_ios - old_pins; - spec->multi_ios = old_pins; - return badness; - } - /* assign volume and mute controls */ - for (i = old_pins; i < spec->multi_ios; i++) - badness += assign_out_path_ctls(codec, spec->multi_io[i].pin, - spec->multi_io[i].dac); + if (spec->init_hook) + spec->init_hook(codec); - return badness; -} + alc_fix_pll(codec); + alc_auto_init_amp(codec, spec->init_amp); -static int alc_auto_ch_mode_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct alc_spec *spec = codec->spec; + snd_hda_gen_init(codec); + + alc_apply_fixup(codec, ALC_FIXUP_ACT_INIT); - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = spec->multi_ios + 1; - if (uinfo->value.enumerated.item > spec->multi_ios) - uinfo->value.enumerated.item = spec->multi_ios; - sprintf(uinfo->value.enumerated.name, "%dch", - (uinfo->value.enumerated.item + 1) * 2); return 0; } -static int alc_auto_ch_mode_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +#ifdef CONFIG_PM +static int alc_check_power_status(struct hda_codec *codec, hda_nid_t nid) { - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct alc_spec *spec = codec->spec; - ucontrol->value.enumerated.item[0] = (spec->ext_channel_count - 1) / 2; - return 0; + return snd_hda_check_amp_list_power(codec, &spec->gen.loopback, nid); } +#endif -static int alc_set_multi_io(struct hda_codec *codec, int idx, bool output) +static inline void alc_shutup(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - hda_nid_t nid = spec->multi_io[idx].pin; - struct nid_path *path; - - path = get_nid_path(codec, spec->multi_io[idx].dac, nid); - if (!path) - return -EINVAL; - - if (path->active == output) - return 0; - if (output) { - snd_hda_set_pin_ctl_cache(codec, nid, PIN_OUT); - activate_path(codec, path, true, true); - } else { - activate_path(codec, path, false, true); - snd_hda_set_pin_ctl_cache(codec, nid, - spec->multi_io[idx].ctl_in); - } - return 0; + if (spec && spec->shutup) + spec->shutup(codec); + snd_hda_shutup_pins(codec); } -static int alc_auto_ch_mode_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +static void alc_free(struct hda_codec *codec) { - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct alc_spec *spec = codec->spec; - int i, ch; - ch = ucontrol->value.enumerated.item[0]; - if (ch < 0 || ch > spec->multi_ios) - return -EINVAL; - if (ch == (spec->ext_channel_count - 1) / 2) - return 0; - spec->ext_channel_count = (ch + 1) * 2; - for (i = 0; i < spec->multi_ios; i++) - alc_set_multi_io(codec, i, i < ch); - spec->multiout.max_channels = max(spec->ext_channel_count, - spec->const_channel_count); - if (spec->need_dac_fix) - spec->multiout.num_dacs = spec->multiout.max_channels / 2; - return 1; + if (!spec) + return; + + snd_hda_gen_spec_free(&spec->gen); + snd_hda_detach_beep_device(codec); + kfree(spec); } -static const struct snd_kcontrol_new alc_auto_channel_mode_enum = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Channel Mode", - .info = alc_auto_ch_mode_info, - .get = alc_auto_ch_mode_get, - .put = alc_auto_ch_mode_put, -}; +#ifdef CONFIG_PM +static void alc_power_eapd(struct hda_codec *codec) +{ + alc_auto_setup_eapd(codec, false); +} -static int alc_auto_add_multi_channel_mode(struct hda_codec *codec) +static int alc_suspend(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - - if (spec->multi_ios > 0) { - if (!alc_kcontrol_new(spec, NULL, &alc_auto_channel_mode_enum)) - return -ENOMEM; - } + alc_shutup(codec); + if (spec && spec->power_hook) + spec->power_hook(codec); return 0; } +#endif -static void alc_auto_init_multi_io(struct hda_codec *codec) +#ifdef CONFIG_PM +static int alc_resume(struct hda_codec *codec) { - struct alc_spec *spec = codec->spec; - int i; - - for (i = 0; i < spec->multi_ios; i++) { - hda_nid_t pin = spec->multi_io[i].pin; - struct nid_path *path; - path = get_nid_path(codec, spec->multi_io[i].dac, pin); - if (!path) - continue; - if (!spec->multi_io[i].ctl_in) - spec->multi_io[i].ctl_in = - snd_hda_codec_update_cache(codec, pin, 0, - AC_VERB_GET_PIN_WIDGET_CONTROL, 0); - activate_path(codec, path, path->active, true); - } + msleep(150); /* to avoid pop noise */ + codec->patch_ops.init(codec); + snd_hda_codec_resume_amp(codec); + snd_hda_codec_resume_cache(codec); + alc_inv_dmic_sync(codec, true); + hda_call_check_power_status(codec, 0x01); + return 0; } +#endif /* - * initialize ADC paths */ -static void alc_auto_init_input_src(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - struct hda_input_mux *imux = &spec->input_mux; - struct nid_path *path; - int i, c, nums; - - if (spec->dyn_adc_switch) - nums = 1; - else - nums = spec->num_adc_nids; - - for (c = 0; c < nums; c++) { - for (i = 0; i < imux->num_items; i++) { - path = get_nid_path(codec, spec->imux_pins[i], - get_adc_nid(codec, c, i)); - if (path) { - bool active = path->active; - if (i == spec->cur_mux[c]) - active = true; - activate_path(codec, path, active, false); - } - } - } +static const struct hda_codec_ops alc_patch_ops = { + .build_controls = alc_build_controls, + .build_pcms = snd_hda_gen_build_pcms, + .init = alc_init, + .free = alc_free, + .unsol_event = snd_hda_jack_unsol_event, +#ifdef CONFIG_PM + .resume = alc_resume, +#endif +#ifdef CONFIG_PM + .suspend = alc_suspend, + .check_power_status = alc_check_power_status, +#endif + .reboot_notify = alc_shutup, +}; - alc_inv_dmic_sync(codec, true); - if (spec->shared_mic_hp) - update_shared_mic_hp(codec, spec->cur_mux[0]); -} -/* add mic boosts if needed */ -static int alc_auto_add_mic_boost(struct hda_codec *codec) +/* replace the codec chip_name with the given string */ +static int alc_codec_rename(struct hda_codec *codec, const char *name) { - struct alc_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - int i, err; - int type_idx = 0; - hda_nid_t nid; - const char *prev_label = NULL; - - for (i = 0; i < cfg->num_inputs; i++) { - if (cfg->inputs[i].type > AUTO_PIN_MIC) - break; - nid = cfg->inputs[i].pin; - if (get_wcaps(codec, nid) & AC_WCAP_IN_AMP) { - const char *label; - char boost_label[32]; - struct nid_path *path; - unsigned int val; - - label = hda_get_autocfg_input_label(codec, cfg, i); - if (spec->shared_mic_hp && !strcmp(label, "Misc")) - label = "Headphone Mic"; - if (prev_label && !strcmp(label, prev_label)) - type_idx++; - else - type_idx = 0; - prev_label = label; - - snprintf(boost_label, sizeof(boost_label), - "%s Boost Volume", label); - val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT); - err = add_control(spec, ALC_CTL_WIDGET_VOL, - boost_label, type_idx, val); - if (err < 0) - return err; - - path = get_nid_path(codec, nid, 0); - if (path) - path->ctls[NID_PATH_BOOST_CTL] = val; - } + kfree(codec->chip_name); + codec->chip_name = kstrdup(name, GFP_KERNEL); + if (!codec->chip_name) { + alc_free(codec); + return -ENOMEM; } return 0; } /* - * standard auto-parser initializations + * Rename codecs appropriately from COEF value */ -static void alc_auto_init_std(struct hda_codec *codec) +struct alc_codec_rename_table { + unsigned int vendor_id; + unsigned short coef_mask; + unsigned short coef_bits; + const char *name; +}; + +static struct alc_codec_rename_table rename_tbl[] = { + { 0x10ec0269, 0xfff0, 0x3010, "ALC277" }, + { 0x10ec0269, 0xf0f0, 0x2010, "ALC259" }, + { 0x10ec0269, 0xf0f0, 0x3010, "ALC258" }, + { 0x10ec0269, 0x00f0, 0x0010, "ALC269VB" }, + { 0x10ec0269, 0xffff, 0xa023, "ALC259" }, + { 0x10ec0269, 0xffff, 0x6023, "ALC281X" }, + { 0x10ec0269, 0x00f0, 0x0020, "ALC269VC" }, + { 0x10ec0269, 0x00f0, 0x0030, "ALC269VD" }, + { 0x10ec0887, 0x00f0, 0x0030, "ALC887-VD" }, + { 0x10ec0888, 0x00f0, 0x0030, "ALC888-VD" }, + { 0x10ec0888, 0xf0f0, 0x3020, "ALC886" }, + { 0x10ec0899, 0x2000, 0x2000, "ALC899" }, + { 0x10ec0892, 0xffff, 0x8020, "ALC661" }, + { 0x10ec0892, 0xffff, 0x8011, "ALC661" }, + { 0x10ec0892, 0xffff, 0x4011, "ALC656" }, + { } /* terminator */ +}; + +static int alc_codec_rename_from_preset(struct hda_codec *codec) { - alc_auto_init_multi_out(codec); - alc_auto_init_extra_out(codec); - alc_auto_init_multi_io(codec); - alc_auto_init_analog_input(codec); - alc_auto_init_input_src(codec); - alc_auto_init_digital(codec); - /* call init functions of standard auto-mute helpers */ - alc_hp_automute(codec, NULL); - alc_line_automute(codec, NULL); - alc_mic_automute(codec, NULL); + const struct alc_codec_rename_table *p; + + for (p = rename_tbl; p->vendor_id; p++) { + if (p->vendor_id != codec->vendor_id) + continue; + if ((alc_get_coef0(codec) & p->coef_mask) == p->coef_bits) + return alc_codec_rename(codec, p->name); + } + return 0; } + /* * Digital-beep handlers */ @@ -4436,102 +978,20 @@ static int alc_parse_auto_config(struct hda_codec *codec, const hda_nid_t *ssid_nids) { struct alc_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; + struct auto_pin_cfg *cfg = &spec->gen.autocfg; int err; err = snd_hda_parse_pin_defcfg(codec, cfg, ignore_nids, spec->parse_flags); if (err < 0) return err; - if (!cfg->line_outs) { - if (cfg->dig_outs || cfg->dig_in_pin) { - spec->multiout.max_channels = 2; - spec->no_analog = 1; - goto dig_only; - } - return 0; /* can't find valid BIOS pin config */ - } - - if (!spec->no_primary_hp && - cfg->line_out_type == AUTO_PIN_SPEAKER_OUT && - cfg->line_outs <= cfg->hp_outs) { - /* use HP as primary out */ - cfg->speaker_outs = cfg->line_outs; - memcpy(cfg->speaker_pins, cfg->line_out_pins, - sizeof(cfg->speaker_pins)); - cfg->line_outs = cfg->hp_outs; - memcpy(cfg->line_out_pins, cfg->hp_pins, sizeof(cfg->hp_pins)); - cfg->hp_outs = 0; - memset(cfg->hp_pins, 0, sizeof(cfg->hp_pins)); - cfg->line_out_type = AUTO_PIN_HP_OUT; - } - - err = alc_auto_fill_dac_nids(codec); - if (err < 0) - return err; - err = alc_auto_add_multi_channel_mode(codec); - if (err < 0) - return err; - err = alc_auto_create_multi_out_ctls(codec, cfg); - if (err < 0) - return err; - err = alc_auto_create_hp_out(codec); - if (err < 0) - return err; - err = alc_auto_create_speaker_out(codec); - if (err < 0) - return err; - err = alc_auto_create_shared_input(codec); - if (err < 0) - return err; - err = alc_auto_create_input_ctls(codec); - if (err < 0) - return err; - - /* check the multiple speaker pins */ - if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) - spec->const_channel_count = cfg->line_outs * 2; - else - spec->const_channel_count = cfg->speaker_outs * 2; - - if (spec->multi_ios > 0) - spec->multiout.max_channels = max(spec->ext_channel_count, - spec->const_channel_count); - else - spec->multiout.max_channels = spec->multiout.num_dacs * 2; - - dig_only: - alc_auto_parse_digital(codec); if (ssid_nids) alc_ssid_check(codec, ssid_nids); - if (!spec->no_analog) { - err = alc_init_automute(codec); - if (err < 0) - return err; - - err = check_dyn_adc_switch(codec); - if (err < 0) - return err; - - if (!spec->shared_mic_hp) { - err = alc_init_auto_mic(codec); - if (err < 0) - return err; - } - - err = create_capture_mixers(codec); - if (err < 0) - return err; - - err = alc_auto_add_mic_boost(codec); - if (err < 0) - return err; - } - - if (spec->kctls.list) - add_mixer(spec, spec->kctls.list); + err = snd_hda_gen_parse_auto_config(codec, cfg); + if (err < 0) + return err; return 1; } @@ -4545,11 +1005,12 @@ static int alc_alloc_spec(struct hda_codec *codec, hda_nid_t mixer_nid) if (!spec) return -ENOMEM; codec->spec = spec; + snd_hda_gen_spec_init(&spec->gen); + spec->gen.mixer_nid = mixer_nid; + spec->gen.own_eapd_ctl = 1; codec->single_adc_amp = 1; - spec->mixer_nid = mixer_nid; - snd_array_init(&spec->kctls, sizeof(struct snd_kcontrol_new), 32); - snd_array_init(&spec->bind_ctls, sizeof(struct hda_bind_ctls *), 8); - snd_array_init(&spec->paths, sizeof(struct nid_path), 8); + /* FIXME: do we need this for all Realtek codec models? */ + codec->spdif_status_reset = 1; err = alc_codec_rename_from_preset(codec); if (err < 0) { @@ -4945,7 +1406,7 @@ static int patch_alc880(struct hda_codec *codec) return err; spec = codec->spec; - spec->need_dac_fix = 1; + spec->gen.need_dac_fix = 1; alc_pick_fixup(codec, alc880_fixup_models, alc880_fixup_tbl, alc880_fixups); @@ -4956,7 +1417,7 @@ static int patch_alc880(struct hda_codec *codec) if (err < 0) goto error; - if (!spec->no_analog) { + if (!spec->gen.no_analog) { err = snd_hda_attach_beep_device(codec, 0x1); if (err < 0) goto error; @@ -5005,7 +1466,7 @@ static void alc260_gpio1_automute(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, - spec->hp_jack_present); + spec->gen.hp_jack_present); } static void alc260_fixup_gpio1_toggle(struct hda_codec *codec, @@ -5016,12 +1477,12 @@ static void alc260_fixup_gpio1_toggle(struct hda_codec *codec, /* although the machine has only one output pin, we need to * toggle GPIO1 according to the jack state */ - spec->automute_hook = alc260_gpio1_automute; - spec->detect_hp = 1; - spec->automute_speaker = 1; - spec->autocfg.hp_pins[0] = 0x0f; /* copy it for automute */ - snd_hda_jack_detect_enable_callback(codec, 0x0f, ALC_HP_EVENT, - alc_hp_automute); + spec->gen.automute_hook = alc260_gpio1_automute; + spec->gen.detect_hp = 1; + spec->gen.automute_speaker = 1; + spec->gen.autocfg.hp_pins[0] = 0x0f; /* copy it for automute */ + snd_hda_jack_detect_enable_callback(codec, 0x0f, HDA_GEN_HP_EVENT, + snd_hda_gen_hp_automute); snd_hda_add_verbs(codec, alc_gpio1_init_verbs); } } @@ -5147,7 +1608,7 @@ static int patch_alc260(struct hda_codec *codec) if (err < 0) goto error; - if (!spec->no_analog) { + if (!spec->gen.no_analog) { err = snd_hda_attach_beep_device(codec, 0x1); if (err < 0) goto error; @@ -5304,7 +1765,7 @@ static void alc889_fixup_mbp_vref(struct hda_codec *codec, AC_VERB_GET_PIN_WIDGET_CONTROL, 0); val |= AC_PINCTL_VREF_80; snd_hda_set_pin_ctl(codec, nids[i], val); - spec->keep_vref_in_automute = 1; + spec->gen.keep_vref_in_automute = 1; break; } } @@ -5326,7 +1787,7 @@ static void alc889_fixup_imac91_vref(struct hda_codec *codec, val |= AC_PINCTL_VREF_50; snd_hda_set_pin_ctl(codec, nids[i], val); } - spec->keep_vref_in_automute = 1; + spec->gen.keep_vref_in_automute = 1; } /* Don't take HP output as primary @@ -5337,7 +1798,7 @@ static void alc882_fixup_no_primary_hp(struct hda_codec *codec, { struct alc_spec *spec = codec->spec; if (action == ALC_FIXUP_ACT_PRE_PROBE) - spec->no_primary_hp = 1; + spec->gen.no_primary_hp = 1; } static const struct alc_fixup alc882_fixups[] = { @@ -5656,7 +2117,7 @@ static int patch_alc882(struct hda_codec *codec) if (err < 0) goto error; - if (!spec->no_analog && has_cdefine_beep(codec)) { + if (!spec->gen.no_analog && has_cdefine_beep(codec)) { err = snd_hda_attach_beep_device(codec, 0x1); if (err < 0) goto error; @@ -5782,7 +2243,7 @@ static int patch_alc262(struct hda_codec *codec) return err; spec = codec->spec; - spec->shared_mic_vref_pin = 0x18; + spec->gen.shared_mic_vref_pin = 0x18; #if 0 /* pshou 07/11/05 set a zero PCM sample to DAC when FIFO is @@ -5809,7 +2270,7 @@ static int patch_alc262(struct hda_codec *codec) if (err < 0) goto error; - if (!spec->no_analog && has_cdefine_beep(codec)) { + if (!spec->gen.no_analog && has_cdefine_beep(codec)) { err = snd_hda_attach_beep_device(codec, 0x1); if (err < 0) goto error; @@ -5897,7 +2358,8 @@ static int alc268_parse_auto_config(struct hda_codec *codec) struct alc_spec *spec = codec->spec; int err = alc_parse_auto_config(codec, NULL, alc268_ssids); if (err > 0) { - if (!spec->no_analog && spec->autocfg.speaker_pins[0] != 0x1d) { + if (!spec->gen.no_analog && + spec->gen.autocfg.speaker_pins[0] != 0x1d) { add_mixer(spec, alc268_beep_mixer); snd_hda_add_verbs(codec, alc268_beep_init_verbs); } @@ -5963,6 +2425,35 @@ static int patch_alc268(struct hda_codec *codec) /* * ALC269 */ + +static int playback_pcm_open(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct hda_gen_spec *spec = codec->spec; + return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream, + hinfo); +} + +static int playback_pcm_prepare(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + unsigned int stream_tag, + unsigned int format, + struct snd_pcm_substream *substream) +{ + struct hda_gen_spec *spec = codec->spec; + return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, + stream_tag, format, substream); +} + +static int playback_pcm_cleanup(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct hda_gen_spec *spec = codec->spec; + return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout); +} + static const struct hda_pcm_stream alc269_44k_pcm_analog_playback = { .substreams = 1, .channels_min = 2, @@ -5970,9 +2461,9 @@ static const struct hda_pcm_stream alc269_44k_pcm_analog_playback = { .rates = SNDRV_PCM_RATE_44100, /* fixed rate */ /* NID is set in alc_build_pcms */ .ops = { - .open = alc_playback_pcm_open, - .prepare = alc_playback_pcm_prepare, - .cleanup = alc_playback_pcm_cleanup + .open = playback_pcm_open, + .prepare = playback_pcm_prepare, + .cleanup = playback_pcm_cleanup }, }; @@ -6127,8 +2618,8 @@ static void alc269_fixup_pcm_44k(struct hda_codec *codec, /* Due to a hardware problem on Lenovo Ideadpad, we need to * fix the sample rate of analog I/O to 44.1kHz */ - spec->stream_analog_playback = &alc269_44k_pcm_analog_playback; - spec->stream_analog_capture = &alc269_44k_pcm_analog_capture; + spec->gen.stream_analog_playback = &alc269_44k_pcm_analog_playback; + spec->gen.stream_analog_capture = &alc269_44k_pcm_analog_capture; } static void alc269_fixup_stereo_dmic(struct hda_codec *codec, @@ -6149,7 +2640,7 @@ static void alc269_fixup_stereo_dmic(struct hda_codec *codec, static void alc269_quanta_automute(struct hda_codec *codec) { - update_outputs(codec); + snd_hda_gen_update_outputs(codec); snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_COEF_INDEX, 0x0c); @@ -6168,7 +2659,7 @@ static void alc269_fixup_quanta_mute(struct hda_codec *codec, struct alc_spec *spec = codec->spec; if (action != ALC_FIXUP_ACT_PROBE) return; - spec->automute_hook = alc269_quanta_automute; + spec->gen.automute_hook = alc269_quanta_automute; } /* update mute-LED according to the speaker mute state via mic1 VREF pin */ @@ -6185,7 +2676,7 @@ static void alc269_fixup_mic1_mute(struct hda_codec *codec, { struct alc_spec *spec = codec->spec; if (action == ALC_FIXUP_ACT_PROBE) - spec->vmaster_mute.hook = alc269_fixup_mic1_mute_hook; + spec->gen.vmaster_mute.hook = alc269_fixup_mic1_mute_hook; } /* update mute-LED according to the speaker mute state via mic2 VREF pin */ @@ -6201,7 +2692,7 @@ static void alc269_fixup_mic2_mute(struct hda_codec *codec, { struct alc_spec *spec = codec->spec; if (action == ALC_FIXUP_ACT_PROBE) - spec->vmaster_mute.hook = alc269_fixup_mic2_mute_hook; + spec->gen.vmaster_mute.hook = alc269_fixup_mic2_mute_hook; } static void alc271_hp_gate_mic_jack(struct hda_codec *codec, @@ -6210,11 +2701,12 @@ static void alc271_hp_gate_mic_jack(struct hda_codec *codec, { struct alc_spec *spec = codec->spec; - if (snd_BUG_ON(!spec->am_entry[1].pin || !spec->autocfg.hp_pins[0])) + if (snd_BUG_ON(!spec->gen.am_entry[1].pin || + !spec->gen.autocfg.hp_pins[0])) return; if (action == ALC_FIXUP_ACT_PROBE) - snd_hda_jack_set_gating_jack(codec, spec->am_entry[1].pin, - spec->autocfg.hp_pins[0]); + snd_hda_jack_set_gating_jack(codec, spec->gen.am_entry[1].pin, + spec->gen.autocfg.hp_pins[0]); } enum { @@ -6560,7 +3052,7 @@ static int patch_alc269(struct hda_codec *codec) return err; spec = codec->spec; - spec->shared_mic_vref_pin = 0x18; + spec->gen.shared_mic_vref_pin = 0x18; alc_pick_fixup(codec, alc269_fixup_models, alc269_fixup_tbl, alc269_fixups); @@ -6615,7 +3107,7 @@ static int patch_alc269(struct hda_codec *codec) if (err < 0) goto error; - if (!spec->no_analog && has_cdefine_beep(codec)) { + if (!spec->gen.no_analog && has_cdefine_beep(codec)) { err = snd_hda_attach_beep_device(codec, 0x1); if (err < 0) goto error; @@ -6671,7 +3163,7 @@ static void alc861_fixup_asus_amp_vref_0f(struct hda_codec *codec, val |= AC_PINCTL_IN_EN; val |= AC_PINCTL_VREF_50; snd_hda_set_pin_ctl(codec, 0x0f, val); - spec->keep_vref_in_automute = 1; + spec->gen.keep_vref_in_automute = 1; } /* suppress the jack-detection */ @@ -6738,7 +3230,7 @@ static int patch_alc861(struct hda_codec *codec) if (err < 0) goto error; - if (!spec->no_analog) { + if (!spec->gen.no_analog) { err = snd_hda_attach_beep_device(codec, 0x23); if (err < 0) goto error; @@ -6833,7 +3325,7 @@ static int patch_alc861vd(struct hda_codec *codec) if (err < 0) goto error; - if (!spec->no_analog) { + if (!spec->gen.no_analog) { err = snd_hda_attach_beep_device(codec, 0x23); if (err < 0) goto error; @@ -7232,7 +3724,7 @@ static int patch_alc662(struct hda_codec *codec) if (err < 0) goto error; - if (!spec->no_analog && has_cdefine_beep(codec)) { + if (!spec->gen.no_analog && has_cdefine_beep(codec)) { err = snd_hda_attach_beep_device(codec, 0x1); if (err < 0) goto error; -- cgit v1.2.3 From 624d914d091a5eebb4d648bd6b4ae6481171ae5c Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 19 Dec 2012 17:41:52 +0100 Subject: ALSA: hda - Use "Capture Source" for single sources In general we prefer "Capture Source" to "Input Source". The latter was chosen in many places just because "Capture Source" label doesn't work well with the current alsa-lib mixer abstraction when multiple instances are present. But when we know that there is a single input-source element, we can safely choose "Capture Source" label. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 6914d70df09a..f8d1d03cd219 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -2172,7 +2172,9 @@ static int create_capture_mixers(struct hda_codec *codec) if (!spec->auto_mic && imux->num_items > 1) { struct snd_kcontrol_new *knew; - knew = snd_hda_gen_add_kctl(spec, NULL, &cap_src_temp); + const char *name; + name = nums > 1 ? "Input Source" : "Capture Source"; + knew = snd_hda_gen_add_kctl(spec, name, &cap_src_temp); if (!knew) return -ENOMEM; knew->count = nums; -- cgit v1.2.3 From 5fdaecdb0d8fd8131d2adb9ca0ae2b77707b36ca Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 20 Dec 2012 10:45:55 +0100 Subject: ALSA: hda - Allow one chance for zero NID in connection list The commit [2e9bf24: ALSA: hda_codec: Check for invalid zero connections] trims the whole connection list when an invalid value is reported by the hardware. But some codecs (at least AD1986A) may give a zero NID in the middle of the connection list, so dropping the whole list isn't good for such cases. In this patch, as a workaround, allow a single zero NID in the read connection list. If it hits zero twice, it's handled as an error, so that we can avoid "too many connections" errors. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_codec.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index e7749dee7c40..ef0a0eebec83 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -424,6 +424,7 @@ int snd_hda_get_raw_connections(struct hda_codec *codec, hda_nid_t nid, unsigned int shift, num_elems, mask; unsigned int wcaps; hda_nid_t prev_nid; + int null_count = 0; if (snd_BUG_ON(!conn_list || max_conns <= 0)) return -EINVAL; @@ -474,7 +475,7 @@ int snd_hda_get_raw_connections(struct hda_codec *codec, hda_nid_t nid, } range_val = !!(parm & (1 << (shift-1))); /* ranges */ val = parm & mask; - if (val == 0) { + if (val == 0 && null_count++) { /* no second chance */ snd_printk(KERN_WARNING "hda_codec: " "invalid CONNECT_LIST verb %x[%i]:%x\n", nid, i, parm); -- cgit v1.2.3 From de1e37b7d0dc3f1b8d0f84f5ff64ef8eebdf1e9f Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 20 Dec 2012 11:00:21 +0100 Subject: ALSA: hda - Clear dirty flag upon cache write When verbs or amps are actually written to the hardware, we can clear dirty flag so that the later snd_hda_codec_resume_*() calls can skip these verbs / amps. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_codec.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index ef0a0eebec83..23c821428a43 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -1848,6 +1848,7 @@ static int codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch, bool init_only) { struct hda_amp_info *info; + unsigned int cache_only; if (snd_BUG_ON(mask & ~0xff)) mask &= 0xff; @@ -1865,10 +1866,9 @@ static int codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch, return 0; } info->vol[ch] = val; - if (codec->cached_write) - info->head.dirty = 1; + cache_only = info->head.dirty = codec->cached_write; mutex_unlock(&codec->hash_mutex); - if (!codec->cached_write) + if (!cache_only) put_vol_mute(codec, info, nid, ch, direction, idx, val); return 1; } @@ -3450,8 +3450,10 @@ int snd_hda_codec_write_cache(struct hda_codec *codec, hda_nid_t nid, int err; struct hda_cache_head *c; u32 key; + unsigned int cache_only; - if (!codec->cached_write) { + cache_only = codec->cached_write; + if (!cache_only) { err = snd_hda_codec_write(codec, nid, direct, verb, parm); if (err < 0) return err; @@ -3465,8 +3467,7 @@ int snd_hda_codec_write_cache(struct hda_codec *codec, hda_nid_t nid, c = get_alloc_hash(&codec->cmd_cache, key); if (c) { c->val = parm; - if (codec->cached_write) - c->dirty = 1; + c->dirty = cache_only; } mutex_unlock(&codec->bus->cmd_mutex); return 0; -- cgit v1.2.3 From aa88a3553eebdcc3ce6801aabb4ed0223bfa198e Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 20 Dec 2012 11:02:00 +0100 Subject: ALSA: hda - Clear cached_write flag in snd_hda_codec_resume_*() These functions are supposed to be called at finishing the cached sequential writes, so clear the flag properly for lazy developers who often forget details. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_codec.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 23c821428a43..3207e5c57141 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -1955,6 +1955,7 @@ void snd_hda_codec_resume_amp(struct hda_codec *codec) int i; mutex_lock(&codec->hash_mutex); + codec->cached_write = 0; for (i = 0; i < codec->amp_cache.buf.used; i++) { struct hda_amp_info *buffer; u32 key; @@ -3520,6 +3521,7 @@ void snd_hda_codec_resume_cache(struct hda_codec *codec) int i; mutex_lock(&codec->hash_mutex); + codec->cached_write = 0; for (i = 0; i < codec->cmd_cache.buf.used; i++) { struct hda_cache_head *buffer; u32 key; -- cgit v1.2.3 From 3bcce5c0d931bf623adc5974200e4d7636b10bef Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 20 Dec 2012 11:17:17 +0100 Subject: ALSA: hda - Check CORB overflow Add an overflow check of CORB in HD-audio controller and codec drivers so that flood of sequential writes would work properly. In the controller side, add a check of CORB read-pointer to make returning -EAGAIN when it's full. Meanwhile in the codec side, when -EAGAIN error is received, it retries the write after flushing the pending verbs (calling get_response() essentially does it). Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_codec.c | 10 ++++++++-- sound/pci/hda/hda_intel.c | 11 +++++++++-- 2 files changed, 17 insertions(+), 4 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 3207e5c57141..afc3ccd998f8 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -222,8 +222,14 @@ static int codec_exec_verb(struct hda_codec *codec, unsigned int cmd, again: snd_hda_power_up(codec); mutex_lock(&bus->cmd_mutex); - trace_hda_send_cmd(codec, cmd); - err = bus->ops.command(bus, cmd); + for (;;) { + trace_hda_send_cmd(codec, cmd); + err = bus->ops.command(bus, cmd); + if (err != -EAGAIN) + break; + /* process pending verbs */ + bus->ops.get_response(bus, codec->addr); + } if (!err && res) { *res = bus->ops.get_response(bus, codec->addr); trace_hda_get_response(codec, *res); diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 0b6aebacc56b..0430436b003d 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -797,7 +797,7 @@ static int azx_corb_send_cmd(struct hda_bus *bus, u32 val) { struct azx *chip = bus->private_data; unsigned int addr = azx_command_addr(val); - unsigned int wp; + unsigned int wp, rp; spin_lock_irq(&chip->reg_lock); @@ -806,11 +806,18 @@ static int azx_corb_send_cmd(struct hda_bus *bus, u32 val) if (wp == 0xffff) { /* something wrong, controller likely turned to D3 */ spin_unlock_irq(&chip->reg_lock); - return -1; + return -EIO; } wp++; wp %= ICH6_MAX_CORB_ENTRIES; + rp = azx_readw(chip, CORBRP); + if (wp == rp) { + /* oops, it's full */ + spin_unlock_irq(&chip->reg_lock); + return -EAGAIN; + } + chip->rirb.cmds[addr]++; chip->corb.buf[wp] = cpu_to_le32(val); azx_writel(chip, CORBWP, wp); -- cgit v1.2.3 From c4f3ebed3c5bc59c88eb2ccda825a12686b58341 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 20 Dec 2012 11:33:20 +0100 Subject: ALSA: hda - Flush dirty amp caches before writing inv_dmic fix The inverted dmic fix overwrites the right channel amp value, but it would work only when the amp values have been already actually written. Put snd_hda_codec_resume_amp() before the amp write for flushing caches. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 896bc2cc9293..caf6fa5e6a70 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -633,6 +633,9 @@ static void alc_inv_dmic_sync_adc(struct hda_codec *codec, int adc_idx) parm = AC_AMP_SET_RIGHT | (dir == HDA_OUTPUT ? AC_AMP_SET_OUTPUT : AC_AMP_SET_INPUT); + /* flush all cached amps at first */ + snd_hda_codec_resume_amp(codec); + /* we care only right channel */ val = snd_hda_codec_amp_read(codec, nid, 1, dir, 0); if (val & 0x80) /* if already muted, we don't need to touch */ -- cgit v1.2.3 From 0c3d47b007dcc0ec7c26a1a654a281076f2f8545 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 20 Dec 2012 11:41:54 +0100 Subject: ALSA: hda - Add snd_hda_codec_flush_*_cache() aliases It makes easier to understand although these are identical with snd_hda_codec_resume_*() functions. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_codec.h | 4 ++++ sound/pci/hda/hda_local.h | 4 ++++ sound/pci/hda/patch_realtek.c | 2 +- 3 files changed, 9 insertions(+), 1 deletion(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index a1cb28ff3968..2d9a51cb4a8e 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -970,6 +970,10 @@ int snd_hda_codec_update_cache(struct hda_codec *codec, hda_nid_t nid, int direct, unsigned int verb, unsigned int parm); void snd_hda_codec_resume_cache(struct hda_codec *codec); +/* it's alias but a bit clearer meaning */ +#define snd_hda_codec_flush_cmd_cache(codec) \ + snd_hda_codec_resume_cache(codec) + /* the struct for codec->pin_configs */ struct hda_pincfg { hda_nid_t nid; diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index de12dcc5accc..fec0e2d72894 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -139,6 +139,10 @@ int snd_hda_codec_amp_init_stereo(struct hda_codec *codec, hda_nid_t nid, int dir, int idx, int mask, int val); void snd_hda_codec_resume_amp(struct hda_codec *codec); +/* it's alias but a bit clearer meaning */ +#define snd_hda_codec_flush_amp_cache(codec) \ + snd_hda_codec_resume_amp(codec) + void snd_hda_set_vmaster_tlv(struct hda_codec *codec, hda_nid_t nid, int dir, unsigned int *tlv); struct snd_kcontrol *snd_hda_find_mixer_ctl(struct hda_codec *codec, diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index caf6fa5e6a70..a6547570c854 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -634,7 +634,7 @@ static void alc_inv_dmic_sync_adc(struct hda_codec *codec, int adc_idx) (dir == HDA_OUTPUT ? AC_AMP_SET_OUTPUT : AC_AMP_SET_INPUT); /* flush all cached amps at first */ - snd_hda_codec_resume_amp(codec); + snd_hda_codec_flush_amp_cache(codec); /* we care only right channel */ val = snd_hda_codec_amp_read(codec, nid, 1, dir, 0); -- cgit v1.2.3 From 84e3908dc837b9d3314a92d8fda12a0ce3946714 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 20 Dec 2012 11:43:47 +0100 Subject: ALSA: hda - Add missing amp cache flush for bound capture vol/sw ctls The bound capture volume and switch controls use the cached amp updates, but it's missing the flushing at the end. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index f8d1d03cd219..3e818b6aa5b0 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -1919,6 +1919,7 @@ static int cap_put_caller(struct snd_kcontrol *kcontrol, error: codec->cached_write = 0; mutex_unlock(&codec->control_mutex); + snd_hda_codec_flush_amp_cache(codec); if (err >= 0 && spec->cap_sync_hook) spec->cap_sync_hook(codec); return err; -- cgit v1.2.3 From 47d46abba2d2046c6bfa37eeb583269d43e659ad Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 20 Dec 2012 11:48:54 +0100 Subject: ALSA: hda - Add / fix comments about capture vol/sw controls in hda_generic.c A bit of details won't hurt. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 3e818b6aa5b0..f5eb57cdac2d 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -1877,10 +1877,6 @@ static int mux_enum_put(struct snd_kcontrol *kcontrol, ucontrol->value.enumerated.item[0]); } -/* - * capture volume and capture switch ctls - */ - static const struct snd_kcontrol_new cap_src_temp = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Input Source", @@ -1889,9 +1885,14 @@ static const struct snd_kcontrol_new cap_src_temp = { .put = mux_enum_put, }; +/* + * capture volume and capture switch ctls + */ + typedef int (*put_call_t)(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); +/* call the given amp update function for all amps in the imux list at once */ static int cap_put_caller(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol, put_call_t func, int type) @@ -1905,6 +1906,10 @@ static int cap_put_caller(struct snd_kcontrol *kcontrol, imux = &spec->input_mux; adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); mutex_lock(&codec->control_mutex); + /* we use the cache-only update at first since multiple input paths + * may shared the same amp; by updating only caches, the redundant + * writes to hardware can be reduced. + */ codec->cached_write = 1; for (i = 0; i < imux->num_items; i++) { path = snd_hda_get_nid_path(codec, spec->imux_pins[i], @@ -1919,7 +1924,7 @@ static int cap_put_caller(struct snd_kcontrol *kcontrol, error: codec->cached_write = 0; mutex_unlock(&codec->control_mutex); - snd_hda_codec_flush_amp_cache(codec); + snd_hda_codec_flush_amp_cache(codec); /* flush the updates */ if (err >= 0 && spec->cap_sync_hook) spec->cap_sync_hook(codec); return err; -- cgit v1.2.3 From 3bbcd274c295011e9377053ff46f725ad5e7dbc0 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 20 Dec 2012 11:50:58 +0100 Subject: ALSA: hda - Do sequential writes in snd_hda_gen_init() This would reduce the number of actually executed verbs. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index f5eb57cdac2d..6fb454eda97f 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -3540,6 +3540,8 @@ int snd_hda_gen_init(struct hda_codec *codec) snd_hda_apply_verbs(codec); + codec->cached_write = 1; + init_multi_out(codec); init_extra_out(codec); init_multi_io(codec); @@ -3552,6 +3554,9 @@ int snd_hda_gen_init(struct hda_codec *codec) snd_hda_gen_line_automute(codec, NULL); snd_hda_gen_mic_autoswitch(codec, NULL); + snd_hda_codec_flush_amp_cache(codec); + snd_hda_codec_flush_cmd_cache(codec); + if (spec->vmaster_mute.sw_kctl && spec->vmaster_mute.hook) snd_hda_sync_vmaster_hook(&spec->vmaster_mute); -- cgit v1.2.3 From 8565f052c5f696ba095a078ea7dbac32460012be Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 20 Dec 2012 12:54:18 +0100 Subject: ALSA: hda - Fix wrong dirty check in snd_hda_codec_resume_amp() The dirty entry has to be checked at the beginning in the loop, not in the inner loop for channels. This caused a regression that the right channel isn't properly written. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_codec.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index afc3ccd998f8..febadc9ed593 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -1969,6 +1969,9 @@ void snd_hda_codec_resume_amp(struct hda_codec *codec) unsigned int idx, dir, ch; buffer = snd_array_elem(&codec->amp_cache.buf, i); + if (!buffer->head.dirty) + continue; + buffer->head.dirty = 0; key = buffer->head.key; if (!key) continue; @@ -1978,9 +1981,6 @@ void snd_hda_codec_resume_amp(struct hda_codec *codec) for (ch = 0; ch < 2; ch++) { if (!(buffer->head.val & INFO_AMP_VOL(ch))) continue; - if (!buffer->head.dirty) - continue; - buffer->head.dirty = 0; mutex_unlock(&codec->hash_mutex); put_vol_mute(codec, buffer, nid, ch, dir, idx, buffer->vol[ch]); -- cgit v1.2.3 From 2ce4886abc61193a8b9dfbb8b08e3f8dff463671 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 20 Dec 2012 12:58:12 +0100 Subject: ALSA: hda - Avoid access of amp cache element outside mutex The access to a cache array element could be invalid outside the mutex, so copy locally for the later references. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_codec.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index febadc9ed593..5689393f8da7 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -1807,7 +1807,7 @@ update_amp_hash(struct hda_codec *codec, hda_nid_t nid, int ch, /* * write the current volume in info to the h/w */ -static void put_vol_mute(struct hda_codec *codec, struct hda_amp_info *info, +static void put_vol_mute(struct hda_codec *codec, unsigned int amp_caps, hda_nid_t nid, int ch, int direction, int index, int val) { @@ -1816,8 +1816,8 @@ static void put_vol_mute(struct hda_codec *codec, struct hda_amp_info *info, parm = ch ? AC_AMP_SET_RIGHT : AC_AMP_SET_LEFT; parm |= direction == HDA_OUTPUT ? AC_AMP_SET_OUTPUT : AC_AMP_SET_INPUT; parm |= index << AC_AMP_SET_INDEX_SHIFT; - if ((val & HDA_AMP_MUTE) && !(info->amp_caps & AC_AMPCAP_MUTE) && - (info->amp_caps & AC_AMPCAP_MIN_MUTE)) + if ((val & HDA_AMP_MUTE) && !(amp_caps & AC_AMPCAP_MUTE) && + (amp_caps & AC_AMPCAP_MIN_MUTE)) ; /* set the zero value as a fake mute */ else parm |= val; @@ -1854,6 +1854,7 @@ static int codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch, bool init_only) { struct hda_amp_info *info; + unsigned int caps; unsigned int cache_only; if (snd_BUG_ON(mask & ~0xff)) @@ -1873,9 +1874,10 @@ static int codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch, } info->vol[ch] = val; cache_only = info->head.dirty = codec->cached_write; + caps = info->amp_caps; mutex_unlock(&codec->hash_mutex); if (!cache_only) - put_vol_mute(codec, info, nid, ch, direction, idx, val); + put_vol_mute(codec, caps, nid, ch, direction, idx, val); return 1; } @@ -1967,23 +1969,25 @@ void snd_hda_codec_resume_amp(struct hda_codec *codec) u32 key; hda_nid_t nid; unsigned int idx, dir, ch; + struct hda_amp_info info; buffer = snd_array_elem(&codec->amp_cache.buf, i); if (!buffer->head.dirty) continue; buffer->head.dirty = 0; - key = buffer->head.key; + info = *buffer; + key = info.head.key; if (!key) continue; nid = key & 0xff; idx = (key >> 16) & 0xff; dir = (key >> 24) & 0xff; for (ch = 0; ch < 2; ch++) { - if (!(buffer->head.val & INFO_AMP_VOL(ch))) + if (!(info.head.val & INFO_AMP_VOL(ch))) continue; mutex_unlock(&codec->hash_mutex); - put_vol_mute(codec, buffer, nid, ch, dir, idx, - buffer->vol[ch]); + put_vol_mute(codec, info.amp_caps, nid, ch, dir, idx, + info.vol[ch]); mutex_lock(&codec->hash_mutex); } } -- cgit v1.2.3 From d94ddd85b1e044c030d62c62f95ae700776888f8 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 20 Dec 2012 14:42:42 +0100 Subject: ALSA: hda - Increase the max depth of widget connections Old codecs like AD1986A tend to have long paths as they were just made to be the way like AC97. The current max depth 5 can be too short for such devices. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_codec.c | 2 +- sound/pci/hda/hda_generic.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 5689393f8da7..f4a0f9d3fd84 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -591,7 +591,7 @@ int snd_hda_get_conn_index(struct hda_codec *codec, hda_nid_t mux, return i; if (!recursive) return -1; - if (recursive > 5) { + if (recursive > 10) { snd_printd("hda_codec: too deep connection for 0x%x\n", nid); return -1; } diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h index 417ab65a323e..89ad877e48c8 100644 --- a/sound/pci/hda/hda_generic.h +++ b/sound/pci/hda/hda_generic.h @@ -37,7 +37,7 @@ struct hda_multi_io { * vol_ctl and mute_ctl contains the NIDs for the assigned mixers */ -#define MAX_NID_PATH_DEPTH 5 +#define MAX_NID_PATH_DEPTH 10 enum { NID_PATH_VOL_CTL, -- cgit v1.2.3 From 406b285da3a04381d46d0f5f5e53c3de0362738c Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 20 Dec 2012 14:46:57 +0100 Subject: ALSA: hda - Begin HDA_GEN_* event tag from 1 ... to distinguish from the invalid event type. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h index 89ad877e48c8..b59889911937 100644 --- a/sound/pci/hda/hda_generic.h +++ b/sound/pci/hda/hda_generic.h @@ -14,7 +14,7 @@ /* unsol event tags */ enum { - HDA_GEN_HP_EVENT, HDA_GEN_FRONT_EVENT, HDA_GEN_MIC_EVENT, + HDA_GEN_HP_EVENT = 1, HDA_GEN_FRONT_EVENT, HDA_GEN_MIC_EVENT, HDA_GEN_LAST_EVENT = HDA_GEN_MIC_EVENT }; -- cgit v1.2.3 From fd25a97a97c78e7f09b0b3069a40bf7671654366 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 20 Dec 2012 14:57:18 +0100 Subject: ALSA: hda - Add spec->vmaster_mute_enum flag to generic parser Add a flag to indicate whether the vmaster mute hook enum is exposed or not. Conexant codecs may want not to expose the control depending on the model. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 3 ++- sound/pci/hda/hda_generic.h | 1 + sound/pci/hda/patch_realtek.c | 8 ++++++-- 3 files changed, 9 insertions(+), 3 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 6fb454eda97f..a5c4bc05d16f 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -2966,7 +2966,8 @@ int snd_hda_gen_build_controls(struct hda_codec *codec) if (err < 0) return err; if (spec->vmaster_mute.hook) - snd_hda_add_vmaster_hook(codec, &spec->vmaster_mute, true); + snd_hda_add_vmaster_hook(codec, &spec->vmaster_mute, + spec->vmaster_mute_enum); } free_kctls(spec); /* no longer needed */ diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h index b59889911937..a406cd4cf072 100644 --- a/sound/pci/hda/hda_generic.h +++ b/sound/pci/hda/hda_generic.h @@ -151,6 +151,7 @@ struct hda_gen_spec { unsigned int multi_cap_vol:1; /* allow multiple capture xxx volumes */ unsigned int inv_dmic_split:1; /* inverted dmic w/a for conexant */ unsigned int own_eapd_ctl:1; /* set EAPD by own function */ + unsigned int vmaster_mute_enum:1; /* add vmaster mute mode enum */ /* for virtual master */ hda_nid_t vmaster_nid; diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index a6547570c854..fee21625e509 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -2678,8 +2678,10 @@ static void alc269_fixup_mic1_mute(struct hda_codec *codec, const struct alc_fixup *fix, int action) { struct alc_spec *spec = codec->spec; - if (action == ALC_FIXUP_ACT_PROBE) + if (action == ALC_FIXUP_ACT_PROBE) { spec->gen.vmaster_mute.hook = alc269_fixup_mic1_mute_hook; + spec->gen.vmaster_mute_enum = 1; + } } /* update mute-LED according to the speaker mute state via mic2 VREF pin */ @@ -2694,8 +2696,10 @@ static void alc269_fixup_mic2_mute(struct hda_codec *codec, const struct alc_fixup *fix, int action) { struct alc_spec *spec = codec->spec; - if (action == ALC_FIXUP_ACT_PROBE) + if (action == ALC_FIXUP_ACT_PROBE) { spec->gen.vmaster_mute.hook = alc269_fixup_mic2_mute_hook; + spec->gen.vmaster_mute_enum = 1; + } } static void alc271_hp_gate_mic_jack(struct hda_codec *codec, -- cgit v1.2.3 From 973e4972f9f0fe8f854451f7559c847a8cdc8bc7 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 20 Dec 2012 15:16:09 +0100 Subject: ALSA: hda - Clear unsol enable bits on unused pins in generic parser For preliminary works to migrate the generic parser for Conexant codecs: the same function is ported to hda_generic.c. But now it looks through the jack detect table so that it can cover better. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index a5c4bc05d16f..b7b8d7eee7c6 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -3532,6 +3532,23 @@ static void init_digital(struct hda_codec *codec) snd_hda_set_pin_ctl(codec, pin, PIN_IN); } +/* clear unsol-event tags on unused pins; Conexant codecs seem to leave + * invalid unsol tags by some reason + */ +static void clear_unsol_on_unused_pins(struct hda_codec *codec) +{ + int i; + + for (i = 0; i < codec->init_pins.used; i++) { + struct hda_pincfg *pin = snd_array_elem(&codec->init_pins, i); + hda_nid_t nid = pin->nid; + if (is_jack_detectable(codec, nid) && + !snd_hda_jack_tbl_get(codec, nid)) + snd_hda_codec_update_cache(codec, nid, 0, + AC_VERB_SET_UNSOLICITED_ENABLE, 0); + } +} + int snd_hda_gen_init(struct hda_codec *codec) { struct hda_gen_spec *spec = codec->spec; @@ -3550,6 +3567,8 @@ int snd_hda_gen_init(struct hda_codec *codec) init_input_src(codec); init_digital(codec); + clear_unsol_on_unused_pins(codec); + /* call init functions of standard auto-mute helpers */ snd_hda_gen_hp_automute(codec, NULL); snd_hda_gen_line_automute(codec, NULL); -- cgit v1.2.3 From db23fd193d20e222449bdca71c4cc95dfadd6eaf Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 20 Dec 2012 15:27:24 +0100 Subject: ALSA: hda - Refactor init_extra_out() in hda_generic.c Just a small clean up by splitting a function. No functional changes at all. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 49 ++++++++++++++++++++++----------------------- 1 file changed, 24 insertions(+), 25 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index b7b8d7eee7c6..96c779b6ca25 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -3389,45 +3389,44 @@ static void init_multi_out(struct hda_codec *codec) } } -/* initialize hp and speaker paths */ -static void init_extra_out(struct hda_codec *codec) + +static void __init_extra_out(struct hda_codec *codec, int num_outs, + hda_nid_t *pins, hda_nid_t *dacs, int type) { struct hda_gen_spec *spec = codec->spec; int i; hda_nid_t pin, dac; - for (i = 0; i < spec->autocfg.hp_outs; i++) { - if (spec->autocfg.line_out_type == AUTO_PIN_HP_OUT) - break; - pin = spec->autocfg.hp_pins[i]; - if (!pin) - break; - dac = spec->multiout.hp_out_nid[i]; - if (!dac) { - if (i > 0 && spec->multiout.hp_out_nid[0]) - dac = spec->multiout.hp_out_nid[0]; - else - dac = spec->multiout.dac_nids[0]; - } - set_output_and_unmute(codec, pin, PIN_HP, dac); - } - for (i = 0; i < spec->autocfg.speaker_outs; i++) { - if (spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT) - break; - pin = spec->autocfg.speaker_pins[i]; + for (i = 0; i < num_outs; i++) { + pin = pins[i]; if (!pin) break; - dac = spec->multiout.extra_out_nid[i]; + dac = dacs[i]; if (!dac) { - if (i > 0 && spec->multiout.extra_out_nid[0]) - dac = spec->multiout.extra_out_nid[0]; + if (i > 0 && dacs[0]) + dac = dacs[0]; else dac = spec->multiout.dac_nids[0]; } - set_output_and_unmute(codec, pin, PIN_OUT, dac); + set_output_and_unmute(codec, pin, type, dac); } } +/* initialize hp and speaker paths */ +static void init_extra_out(struct hda_codec *codec) +{ + struct hda_gen_spec *spec = codec->spec; + + if (spec->autocfg.line_out_type != AUTO_PIN_HP_OUT) + __init_extra_out(codec, spec->autocfg.hp_outs, + spec->autocfg.hp_pins, + spec->multiout.hp_out_nid, PIN_HP); + if (spec->autocfg.line_out_type != AUTO_PIN_SPEAKER_OUT) + __init_extra_out(codec, spec->autocfg.speaker_outs, + spec->autocfg.speaker_pins, + spec->multiout.extra_out_nid, PIN_OUT); +} + /* initialize multi-io paths */ static void init_multi_io(struct hda_codec *codec) { -- cgit v1.2.3 From 64049c81df38aa5f550cc0fae9c74c6bd5c31e58 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 20 Dec 2012 15:29:21 +0100 Subject: ALSA: hda - Fix initialization of primary outputs in hda_generic.c There were some old codes that look not stable enough, which was derived from the old Realtek code. The initialization for primary output in init_multi_out() needs to consider the case of shared DAC. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 96c779b6ca25..a133fcfd2fb4 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -3372,6 +3372,7 @@ static void set_output_and_unmute(struct hda_codec *codec, hda_nid_t pin, static void init_multi_out(struct hda_codec *codec) { struct hda_gen_spec *spec = codec->spec; + hda_nid_t nid, dac; int pin_type; int i; @@ -3380,12 +3381,14 @@ static void init_multi_out(struct hda_codec *codec) else pin_type = PIN_OUT; - for (i = 0; i <= HDA_SIDE; i++) { - hda_nid_t nid = spec->autocfg.line_out_pins[i]; - if (nid) - set_output_and_unmute(codec, nid, pin_type, - spec->multiout.dac_nids[i]); - + for (i = 0; i < spec->autocfg.line_outs; i++) { + nid = spec->autocfg.line_out_pins[i]; + if (nid) { + dac = spec->multiout.dac_nids[i]; + if (!dac) + dac = spec->multiout.dac_nids[0]; + set_output_and_unmute(codec, nid, pin_type, dac); + } } } -- cgit v1.2.3 From d5a9f1bb38354ef4786d7fd761d75594bc33f7d2 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 20 Dec 2012 15:36:30 +0100 Subject: ALSA: hda - Dynamically turn on/off EAPD in generic codec driver When spec->own_eapd_ctl isn't set, try to turn on/off EAPD on demand for each pin. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index a133fcfd2fb4..46b00e0756c5 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -523,6 +523,18 @@ void snd_hda_activate_path(struct hda_codec *codec, struct nid_path *path, } EXPORT_SYMBOL_HDA(snd_hda_activate_path); +/* turn on/off EAPD on the given pin */ +static void set_pin_eapd(struct hda_codec *codec, hda_nid_t pin, bool enable) +{ + struct hda_gen_spec *spec = codec->spec; + if (spec->own_eapd_ctl || + !(snd_hda_query_pin_caps(codec, pin) & AC_PINCAP_EAPD)) + return; + snd_hda_codec_update_cache(codec, pin, 0, + AC_VERB_SET_EAPD_BTLENABLE, + enable ? 0x02 : 0x00); +} + /* * Helper functions for creating mixer ctl elements @@ -1485,7 +1497,9 @@ static int set_multi_io(struct hda_codec *codec, int idx, bool output) if (output) { snd_hda_set_pin_ctl_cache(codec, nid, PIN_OUT); snd_hda_activate_path(codec, path, true, true); + set_pin_eapd(codec, nid, true); } else { + set_pin_eapd(codec, nid, false); snd_hda_activate_path(codec, path, false, true); snd_hda_set_pin_ctl_cache(codec, nid, spec->multi_io[idx].ctl_in); @@ -2418,6 +2432,7 @@ static void do_automute(struct hda_codec *codec, int num_pins, hda_nid_t *pins, val = 0; val |= pin_bits; snd_hda_set_pin_ctl(codec, nid, val); + set_pin_eapd(codec, nid, !mute); } } @@ -3351,7 +3366,6 @@ EXPORT_SYMBOL_HDA(snd_hda_gen_build_pcms); static void set_output_and_unmute(struct hda_codec *codec, hda_nid_t pin, int pin_type, hda_nid_t dac) { - struct hda_gen_spec *spec = codec->spec; struct nid_path *path; snd_hda_set_pin_ctl_cache(codec, pin, pin_type); @@ -3361,11 +3375,7 @@ static void set_output_and_unmute(struct hda_codec *codec, hda_nid_t pin, if (path->active) return; snd_hda_activate_path(codec, path, true, true); - - if (!spec->own_eapd_ctl && - (snd_hda_query_pin_caps(codec, pin) & AC_PINCAP_EAPD)) - snd_hda_codec_update_cache(codec, pin, 0, - AC_VERB_SET_EAPD_BTLENABLE, 0x02); + set_pin_eapd(codec, pin, true); } /* initialize primary output paths */ -- cgit v1.2.3 From 7594aa33963eb4a795ca346ec6d7c0dfaa2485a2 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 20 Dec 2012 15:38:40 +0100 Subject: ALSA: hda - Use cached version for changing pins in hda_generic.c There is no reason to avoid snd_hda_set_pin_ctl_cache() there. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 46b00e0756c5..f4b5043a3176 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -1572,11 +1572,12 @@ static void update_shared_mic_hp(struct hda_codec *codec, bool set_as_mic) const hda_nid_t vref_pin = spec->shared_mic_vref_pin; unsigned int vref_val = snd_hda_get_default_vref(codec, vref_pin); if (vref_val != AC_PINCTL_VREF_HIZ) - snd_hda_set_pin_ctl(codec, vref_pin, PIN_IN | (set_as_mic ? vref_val : 0)); + snd_hda_set_pin_ctl_cache(codec, vref_pin, + PIN_IN | (set_as_mic ? vref_val : 0)); } val = set_as_mic ? val | PIN_IN : PIN_HP; - snd_hda_set_pin_ctl(codec, pin, val); + snd_hda_set_pin_ctl_cache(codec, pin, val); spec->automute_speaker = !set_as_mic; call_update_outputs(codec); @@ -2431,7 +2432,7 @@ static void do_automute(struct hda_codec *codec, int num_pins, hda_nid_t *pins, } else val = 0; val |= pin_bits; - snd_hda_set_pin_ctl(codec, nid, val); + snd_hda_set_pin_ctl_cache(codec, nid, val); set_pin_eapd(codec, nid, !mute); } } @@ -3467,7 +3468,7 @@ static void set_input_pin(struct hda_codec *codec, hda_nid_t nid, unsigned int val = PIN_IN; if (auto_pin_type == AUTO_PIN_MIC) val |= snd_hda_get_default_vref(codec, nid); - snd_hda_set_pin_ctl(codec, nid, val); + snd_hda_set_pin_ctl_cache(codec, nid, val); } /* set up input pins and loopback paths */ @@ -3541,7 +3542,7 @@ static void init_digital(struct hda_codec *codec) } pin = spec->autocfg.dig_in_pin; if (pin) - snd_hda_set_pin_ctl(codec, pin, PIN_IN); + snd_hda_set_pin_ctl_cache(codec, pin, PIN_IN); } /* clear unsol-event tags on unused pins; Conexant codecs seem to leave -- cgit v1.2.3 From f873e536b6354214f80776382c3779b75e9e145f Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 20 Dec 2012 16:58:39 +0100 Subject: ALSA: hda - Fix PCM name string for generic parser When a PCM name string is generated from the chip name, it might become strange like "CX20549 (Venice) Analog". In this patch, the parser tries to drop the invalid words like "(Venice)" in the PCM name string. Also, when the name string is given beforehand by the caller, respect it and use it as is. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 32 +++++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index f4b5043a3176..d4cb9df6e5b3 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -24,6 +24,8 @@ #include #include #include +#include +#include #include #include #include "hda_codec.h" @@ -3230,6 +3232,25 @@ static const struct hda_pcm_stream dyn_adc_pcm_analog_capture = { }, }; +static void fill_pcm_stream_name(char *str, size_t len, const char *sfx, + const char *chip_name) +{ + char *p; + + if (*str) + return; + strlcpy(str, chip_name, len); + + /* drop non-alnum chars after a space */ + for (p = strchr(str, ' '); p; p = strchr(p + 1, ' ')) { + if (!isalnum(p[1])) { + *p = 0; + break; + } + } + strlcat(str, sfx, len); +} + /* build PCM streams based on the parsed results */ int snd_hda_gen_build_pcms(struct hda_codec *codec) { @@ -3245,8 +3266,9 @@ int snd_hda_gen_build_pcms(struct hda_codec *codec) if (spec->no_analog) goto skip_analog; - snprintf(spec->stream_name_analog, sizeof(spec->stream_name_analog), - "%s Analog", codec->chip_name); + fill_pcm_stream_name(spec->stream_name_analog, + sizeof(spec->stream_name_analog), + " Analog", codec->chip_name); info->name = spec->stream_name_analog; if (spec->multiout.num_dacs > 0) { @@ -3286,9 +3308,9 @@ int snd_hda_gen_build_pcms(struct hda_codec *codec) skip_analog: /* SPDIF for stream index #1 */ if (spec->multiout.dig_out_nid || spec->dig_in_nid) { - snprintf(spec->stream_name_digital, - sizeof(spec->stream_name_digital), - "%s Digital", codec->chip_name); + fill_pcm_stream_name(spec->stream_name_digital, + sizeof(spec->stream_name_digital), + " Digital", codec->chip_name); codec->num_pcms = 2; codec->slave_dig_outs = spec->multiout.slave_dig_outs; info = spec->pcm_rec + 1; -- cgit v1.2.3 From 545502de54c5ec31222915e5b977a86d603cec86 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 20 Dec 2012 17:05:20 +0100 Subject: ALSA: hda - Drop spec->channel_mode field from hda_gen_spec It's never used in the generic parser. It was there from the old Realtek code, which has been dropped quite ago, too. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 10 ---------- sound/pci/hda/hda_generic.h | 2 -- 2 files changed, 12 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index d4cb9df6e5b3..43acf3d80661 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -3258,7 +3258,6 @@ int snd_hda_gen_build_pcms(struct hda_codec *codec) struct hda_pcm *info = spec->pcm_rec; const struct hda_pcm_stream *p; bool have_multi_adcs; - int i; codec->num_pcms = 1; codec->pcm_info = info; @@ -3296,15 +3295,6 @@ int snd_hda_gen_build_pcms(struct hda_codec *codec) info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0]; } - if (spec->channel_mode) { - info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = 0; - for (i = 0; i < spec->num_channel_mode; i++) { - if (spec->channel_mode[i].channels > info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max) { - info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = spec->channel_mode[i].channels; - } - } - } - skip_analog: /* SPDIF for stream index #1 */ if (spec->multiout.dig_out_nid || spec->dig_in_nid) { diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h index a406cd4cf072..6365140924af 100644 --- a/sound/pci/hda/hda_generic.h +++ b/sound/pci/hda/hda_generic.h @@ -101,8 +101,6 @@ struct hda_gen_spec { unsigned int cur_mux[3]; /* channel model */ - const struct hda_channel_mode *channel_mode; - int num_channel_mode; int const_channel_count; /* min. channel count (for speakers) */ int ext_channel_count; /* current channel count for multi-io */ -- cgit v1.2.3 From 0c8c0f56e6e575f3722a83991f615f4a67c12ea9 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 20 Dec 2012 17:54:22 +0100 Subject: ALSA: hda - Add more debug prints about new paths Add a better debug print code to show the new assigned paths in generic parser. It appears only with CONFIG_SND_DEBUG_VERBOSE=y. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 46 +++++++++++++++++++++++++++++++++++---------- 1 file changed, 36 insertions(+), 10 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 43acf3d80661..8af5324a608c 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -186,6 +186,21 @@ static bool is_ctl_associated(struct hda_codec *codec, hda_nid_t nid, is_ctl_used(codec, val, NID_PATH_MUTE_CTL); } +static void print_nid_path(const char *pfx, struct nid_path *path) +{ + char buf[40]; + int i; + + + buf[0] = 0; + for (i = 0; i < path->depth; i++) { + char tmp[4]; + sprintf(tmp, ":%02x", path->path[i]); + strlcat(buf, tmp, sizeof(buf)); + } + snd_printdd("%s path: depth=%d %s\n", pfx, path->depth, buf); +} + /* called recursively */ static bool __parse_nid_path(struct hda_codec *codec, hda_nid_t from_nid, hda_nid_t to_nid, @@ -252,11 +267,6 @@ bool snd_hda_parse_nid_path(struct hda_codec *codec, hda_nid_t from_nid, if (__parse_nid_path(codec, from_nid, to_nid, with_aa_mix, path, 1)) { path->path[path->depth] = to_nid; path->depth++; -#if 0 - snd_printdd("path: depth=%d, %02x/%02x/%02x/%02x/%02x\n", - path->depth, path->path[0], path->path[1], - path->path[2], path->path[3], path->path[4]); -#endif return true; } return false; @@ -816,6 +826,7 @@ static int try_assign_dacs(struct hda_codec *codec, int num_outs, return 0; for (i = 0; i < num_outs; i++) { + struct nid_path *path; hda_nid_t pin = pins[i]; if (!dacs[i]) dacs[i] = look_for_dac(codec, pin, false); @@ -850,8 +861,11 @@ static int try_assign_dacs(struct hda_codec *codec, int num_outs, else badness += bad->no_dac; } - if (!snd_hda_add_new_path(codec, dac, pin, 0)) + path = snd_hda_add_new_path(codec, dac, pin, 0); + if (!path) dac = dacs[i] = 0; + else + print_nid_path("output", path); if (dac) badness += assign_out_path_ctls(codec, pin, dac); } @@ -935,6 +949,7 @@ static int fill_multi_ios(struct hda_codec *codec, dacs = spec->multiout.num_dacs; for (type = AUTO_PIN_LINE_IN; type >= AUTO_PIN_MIC; type--) { for (i = 0; i < cfg->num_inputs; i++) { + struct nid_path *path; hda_nid_t nid = cfg->inputs[i].pin; hda_nid_t dac = 0; @@ -962,10 +977,12 @@ static int fill_multi_ios(struct hda_codec *codec, badness++; continue; } - if (!snd_hda_add_new_path(codec, dac, nid, 0)) { + path = snd_hda_add_new_path(codec, dac, nid, 0); + if (!path) { badness++; continue; } + print_nid_path("multiio", path); spec->multi_io[spec->multi_ios].pin = nid; spec->multi_io[spec->multi_ios].dac = dac; spec->multi_ios++; @@ -1004,15 +1021,18 @@ static bool map_singles(struct hda_codec *codec, int outs, int i; bool found = false; for (i = 0; i < outs; i++) { + struct nid_path *path; hda_nid_t dac; if (dacs[i]) continue; dac = get_dac_if_single(codec, pins[i]); if (!dac) continue; - if (snd_hda_add_new_path(codec, dac, pins[i], 0)) { + path = snd_hda_add_new_path(codec, dac, pins[i], 0); + if (path) { dacs[i] = dac; found = true; + print_nid_path("output", path); } } return found; @@ -1268,6 +1288,7 @@ static int parse_output_paths(struct hda_codec *codec) } if (badness) { + debug_badness("==> restoring best_cfg\n"); *cfg = *best_cfg; fill_and_eval_dacs(codec, best_wired, best_mio); } @@ -1659,6 +1680,7 @@ static int new_analog_input(struct hda_codec *codec, hda_nid_t pin, path = snd_hda_add_new_path(codec, pin, mix_nid, 2); if (!path) return -EINVAL; + print_nid_path("loopback", path); idx = path->idx[path->depth - 1]; if (nid_has_volume(codec, mix_nid, HDA_INPUT)) { @@ -1836,6 +1858,7 @@ static int create_input_ctls(struct hda_codec *codec) spec->paths.used--; continue; } + print_nid_path("input", path); if (!imux_added) { spec->imux_pins[imux->num_items] = pin; @@ -2295,6 +2318,7 @@ static int parse_mic_boost(struct hda_codec *codec) static void parse_digital(struct hda_codec *codec) { struct hda_gen_spec *spec = codec->spec; + struct nid_path *path; int i, nums; hda_nid_t dig_nid; @@ -2305,8 +2329,10 @@ static void parse_digital(struct hda_codec *codec) dig_nid = look_for_dac(codec, pin, true); if (!dig_nid) continue; - if (!snd_hda_add_new_path(codec, dig_nid, pin, 2)) + path = snd_hda_add_new_path(codec, dig_nid, pin, 2); + if (!path) continue; + print_nid_path("digout", path); if (!nums) { spec->multiout.dig_out_nid = dig_nid; spec->dig_out_type = spec->autocfg.dig_out_type[0]; @@ -2322,7 +2348,6 @@ static void parse_digital(struct hda_codec *codec) if (spec->autocfg.dig_in_pin) { dig_nid = codec->start_nid; for (i = 0; i < codec->num_nodes; i++, dig_nid++) { - struct nid_path *path; unsigned int wcaps = get_wcaps(codec, dig_nid); if (get_wcaps_type(wcaps) != AC_WID_AUD_IN) continue; @@ -2332,6 +2357,7 @@ static void parse_digital(struct hda_codec *codec) spec->autocfg.dig_in_pin, dig_nid, 2); if (path) { + print_nid_path("digin", path); path->active = true; spec->dig_in_nid = dig_nid; break; -- cgit v1.2.3 From 708122e8366543d448f7f8f6bc4510cf1c2034ce Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 20 Dec 2012 17:56:57 +0100 Subject: ALSA: hda - Fix typos in debug_show_configs() It never showed the 4th line out and headphone pins since quite ago. Oh well. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 8af5324a608c..b341450653e2 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -1164,7 +1164,7 @@ static void debug_show_configs(struct hda_gen_spec *spec, struct auto_pin_cfg *c { debug_badness("multi_outs = %x/%x/%x/%x : %x/%x/%x/%x\n", cfg->line_out_pins[0], cfg->line_out_pins[1], - cfg->line_out_pins[2], cfg->line_out_pins[2], + cfg->line_out_pins[2], cfg->line_out_pins[3], spec->multiout.dac_nids[0], spec->multiout.dac_nids[1], spec->multiout.dac_nids[2], @@ -1176,7 +1176,7 @@ static void debug_show_configs(struct hda_gen_spec *spec, struct auto_pin_cfg *c spec->multi_io[0].dac, spec->multi_io[1].dac); debug_badness("hp_outs = %x/%x/%x/%x : %x/%x/%x/%x\n", cfg->hp_pins[0], cfg->hp_pins[1], - cfg->hp_pins[2], cfg->hp_pins[2], + cfg->hp_pins[2], cfg->hp_pins[3], spec->multiout.hp_out_nid[0], spec->multiout.hp_out_nid[1], spec->multiout.hp_out_nid[2], -- cgit v1.2.3 From 4ac0eefa761f62b07d4b96884cf1acc625d7063f Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 20 Dec 2012 18:10:51 +0100 Subject: ALSA: hda - Define HDA_PARSE_* for snd_hda_parse_nid_path() argument ... instead of numbers. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 28 +++++++++++++++------------- sound/pci/hda/hda_generic.h | 7 +++++++ 2 files changed, 22 insertions(+), 13 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index b341450653e2..18b5fae546d0 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -211,9 +211,9 @@ static bool __parse_nid_path(struct hda_codec *codec, int i, nums; if (to_nid == spec->mixer_nid) { - if (!with_aa_mix) + if (with_aa_mix == HDA_PARSE_NO_AAMIX) return false; - with_aa_mix = 2; /* mark aa-mix is included */ + with_aa_mix = HDA_PARSE_ALL; /* mark aa-mix is included */ } nums = snd_hda_get_connections(codec, to_nid, conn, ARRAY_SIZE(conn)); @@ -228,7 +228,7 @@ static bool __parse_nid_path(struct hda_codec *codec, continue; } /* aa-mix is requested but not included? */ - if (!(spec->mixer_nid && with_aa_mix == 1)) + if (!(spec->mixer_nid && with_aa_mix == HDA_PARSE_ONLY_AAMIX)) goto found; } if (depth >= MAX_NID_PATH_DEPTH) @@ -256,9 +256,11 @@ static bool __parse_nid_path(struct hda_codec *codec, /* parse the widget path from the given nid to the target nid; * when @from_nid is 0, try to find an empty DAC; - * when @with_aa_mix is 0, paths with spec->mixer_nid are excluded. - * when @with_aa_mix is 1, paths without spec->mixer_nid are excluded. - * when @with_aa_mix is 2, no special handling about spec->mixer_nid. + * when @with_aa_mix is HDA_PARSE_NO_AAMIX, paths with spec->mixer_nid are + * excluded, only the paths that don't go through the mixer will be chosen. + * when @with_aa_mix is HDA_PARSE_ONLY_AAMIX, only the paths going through + * spec->mixer_nid will be chosen. + * when @with_aa_mix is HDA_PARSE_ALL, no special handling about mixer widget. */ bool snd_hda_parse_nid_path(struct hda_codec *codec, hda_nid_t from_nid, hda_nid_t to_nid, int with_aa_mix, @@ -861,7 +863,7 @@ static int try_assign_dacs(struct hda_codec *codec, int num_outs, else badness += bad->no_dac; } - path = snd_hda_add_new_path(codec, dac, pin, 0); + path = snd_hda_add_new_path(codec, dac, pin, HDA_PARSE_NO_AAMIX); if (!path) dac = dacs[i] = 0; else @@ -977,7 +979,7 @@ static int fill_multi_ios(struct hda_codec *codec, badness++; continue; } - path = snd_hda_add_new_path(codec, dac, nid, 0); + path = snd_hda_add_new_path(codec, dac, nid, HDA_PARSE_NO_AAMIX); if (!path) { badness++; continue; @@ -1028,7 +1030,7 @@ static bool map_singles(struct hda_codec *codec, int outs, dac = get_dac_if_single(codec, pins[i]); if (!dac) continue; - path = snd_hda_add_new_path(codec, dac, pins[i], 0); + path = snd_hda_add_new_path(codec, dac, pins[i], HDA_PARSE_NO_AAMIX); if (path) { dacs[i] = dac; found = true; @@ -1677,7 +1679,7 @@ static int new_analog_input(struct hda_codec *codec, hda_nid_t pin, !nid_has_mute(codec, mix_nid, HDA_INPUT)) return 0; /* no need for analog loopback */ - path = snd_hda_add_new_path(codec, pin, mix_nid, 2); + path = snd_hda_add_new_path(codec, pin, mix_nid, HDA_PARSE_ALL); if (!path) return -EINVAL; print_nid_path("loopback", path); @@ -1851,7 +1853,7 @@ static int create_input_ctls(struct hda_codec *codec) if (!path) return -ENOMEM; memset(path, 0, sizeof(*path)); - if (!snd_hda_parse_nid_path(codec, pin, adc, 2, path)) { + if (!snd_hda_parse_nid_path(codec, pin, adc, HDA_PARSE_ALL, path)) { snd_printd(KERN_ERR "invalid input path 0x%x -> 0x%x\n", pin, adc); @@ -2329,7 +2331,7 @@ static void parse_digital(struct hda_codec *codec) dig_nid = look_for_dac(codec, pin, true); if (!dig_nid) continue; - path = snd_hda_add_new_path(codec, dig_nid, pin, 2); + path = snd_hda_add_new_path(codec, dig_nid, pin, HDA_PARSE_ALL); if (!path) continue; print_nid_path("digout", path); @@ -2355,7 +2357,7 @@ static void parse_digital(struct hda_codec *codec) continue; path = snd_hda_add_new_path(codec, spec->autocfg.dig_in_pin, - dig_nid, 2); + dig_nid, HDA_PARSE_ALL); if (path) { print_nid_path("digin", path); path->active = true; diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h index 6365140924af..85d138fc10b3 100644 --- a/sound/pci/hda/hda_generic.h +++ b/sound/pci/hda/hda_generic.h @@ -180,6 +180,13 @@ int snd_hda_gen_init(struct hda_codec *codec); struct nid_path *snd_hda_get_nid_path(struct hda_codec *codec, hda_nid_t from_nid, hda_nid_t to_nid); + +enum { + HDA_PARSE_NO_AAMIX, + HDA_PARSE_ONLY_AAMIX, + HDA_PARSE_ALL, +}; + bool snd_hda_parse_nid_path(struct hda_codec *codec, hda_nid_t from_nid, hda_nid_t to_nid, int with_aa_mix, struct nid_path *path); -- cgit v1.2.3 From b3a8c74522ae7c992cd916c3bd4b685d742c0e2a Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 20 Dec 2012 18:29:16 +0100 Subject: ALSA: hda - Allow aamix in the primary output path Allow the path including the loopback mixer widget in the primary output channel as an alternative in the generic codec parser. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 18b5fae546d0..f3c6ace2c87f 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -864,6 +864,10 @@ static int try_assign_dacs(struct hda_codec *codec, int num_outs, badness += bad->no_dac; } path = snd_hda_add_new_path(codec, dac, pin, HDA_PARSE_NO_AAMIX); + if (!path && i > 0 && spec->mixer_nid) { + /* try with aamix */ + path = snd_hda_add_new_path(codec, dac, pin, HDA_PARSE_ALL); + } if (!path) dac = dacs[i] = 0; else @@ -1020,6 +1024,7 @@ static int fill_multi_ios(struct hda_codec *codec, static bool map_singles(struct hda_codec *codec, int outs, const hda_nid_t *pins, hda_nid_t *dacs) { + struct hda_gen_spec *spec = codec->spec; int i; bool found = false; for (i = 0; i < outs; i++) { @@ -1031,6 +1036,8 @@ static bool map_singles(struct hda_codec *codec, int outs, if (!dac) continue; path = snd_hda_add_new_path(codec, dac, pins[i], HDA_PARSE_NO_AAMIX); + if (!path && i > 0 && spec->mixer_nid) + path = snd_hda_add_new_path(codec, dac, pins[i], HDA_PARSE_ALL); if (path) { dacs[i] = dac; found = true; -- cgit v1.2.3 From 38cf6f1a41e40a33d80924554b356fcd5b5d2751 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 21 Dec 2012 14:09:42 +0100 Subject: ALSA: hda - Implement independent HP control Similar like the implementation in patch_analog.c and patch_via.c, the generic parser can provide the independent HP PCM stream now. It's enabled when spec->indep_hp is set by the caller while parsing. Currently no dynamic PCM switching as in patch_via.c is implemented yet. The control returns -EBUSY when the value is changed during PCM operations. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 130 +++++++++++++++++++++++++++++++++++++++++++- sound/pci/hda/hda_generic.h | 9 +++ 2 files changed, 138 insertions(+), 1 deletion(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index f3c6ace2c87f..ff15aea836da 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -41,6 +41,7 @@ int snd_hda_gen_spec_init(struct hda_gen_spec *spec) snd_array_init(&spec->kctls, sizeof(struct snd_kcontrol_new), 32); snd_array_init(&spec->bind_ctls, sizeof(struct hda_bind_ctls *), 8); snd_array_init(&spec->paths, sizeof(struct nid_path), 8); + mutex_init(&spec->pcm_mutex); return 0; } EXPORT_SYMBOL_HDA(snd_hda_gen_spec_init); @@ -1484,6 +1485,79 @@ static int create_speaker_out_ctls(struct hda_codec *codec) "Speaker"); } +/* + * independent HP controls + */ + +static int indep_hp_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + return snd_hda_enum_bool_helper_info(kcontrol, uinfo); +} + +static int indep_hp_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct hda_gen_spec *spec = codec->spec; + ucontrol->value.enumerated.item[0] = spec->indep_hp_enabled; + return 0; +} + +static int indep_hp_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct hda_gen_spec *spec = codec->spec; + unsigned int select = ucontrol->value.enumerated.item[0]; + int ret = 0; + + mutex_lock(&spec->pcm_mutex); + if (spec->active_streams) { + ret = -EBUSY; + goto unlock; + } + + if (spec->indep_hp_enabled != select) { + spec->indep_hp_enabled = select; + if (spec->indep_hp_enabled) + spec->multiout.hp_out_nid[0] = 0; + else + spec->multiout.hp_out_nid[0] = spec->alt_dac_nid; + ret = 1; + } + unlock: + mutex_unlock(&spec->pcm_mutex); + return ret; +} + +static const struct snd_kcontrol_new indep_hp_ctl = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Independent HP", + .info = indep_hp_info, + .get = indep_hp_get, + .put = indep_hp_put, +}; + + +static int create_indep_hp_ctls(struct hda_codec *codec) +{ + struct hda_gen_spec *spec = codec->spec; + + if (!spec->indep_hp) + return 0; + if (!spec->multiout.hp_out_nid[0]) { + spec->indep_hp = 0; + return 0; + } + + spec->indep_hp_enabled = false; + spec->alt_dac_nid = spec->multiout.hp_out_nid[0]; + if (!snd_hda_gen_add_kctl(spec, NULL, &indep_hp_ctl)) + return -ENOMEM; + return 0; +} + /* * channel mode enum control */ @@ -2903,6 +2977,9 @@ int snd_hda_gen_parse_auto_config(struct hda_codec *codec, if (err < 0) return err; err = create_speaker_out_ctls(codec); + if (err < 0) + return err; + err = create_indep_hp_ctls(codec); if (err < 0) return err; err = create_shared_input(codec); @@ -3057,8 +3134,16 @@ static int playback_pcm_open(struct hda_pcm_stream *hinfo, struct snd_pcm_substream *substream) { struct hda_gen_spec *spec = codec->spec; - return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream, + int err; + + mutex_lock(&spec->pcm_mutex); + err = snd_hda_multi_out_analog_open(codec, + &spec->multiout, substream, hinfo); + if (!err) + spec->active_streams |= 1 << STREAM_MULTI_OUT; + mutex_unlock(&spec->pcm_mutex); + return err; } static int playback_pcm_prepare(struct hda_pcm_stream *hinfo, @@ -3080,6 +3165,44 @@ static int playback_pcm_cleanup(struct hda_pcm_stream *hinfo, return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout); } +static int playback_pcm_close(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct hda_gen_spec *spec = codec->spec; + mutex_lock(&spec->pcm_mutex); + spec->active_streams &= ~(1 << STREAM_MULTI_OUT); + mutex_unlock(&spec->pcm_mutex); + return 0; +} + +static int alt_playback_pcm_open(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct hda_gen_spec *spec = codec->spec; + int err = 0; + + mutex_lock(&spec->pcm_mutex); + if (!spec->indep_hp_enabled) + err = -EBUSY; + else + spec->active_streams |= 1 << STREAM_INDEP_HP; + mutex_unlock(&spec->pcm_mutex); + return err; +} + +static int alt_playback_pcm_close(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct hda_gen_spec *spec = codec->spec; + mutex_lock(&spec->pcm_mutex); + spec->active_streams &= ~(1 << STREAM_INDEP_HP); + mutex_unlock(&spec->pcm_mutex); + return 0; +} + /* * Digital out */ @@ -3154,6 +3277,7 @@ static const struct hda_pcm_stream pcm_analog_playback = { /* NID is set in build_pcms */ .ops = { .open = playback_pcm_open, + .close = playback_pcm_close, .prepare = playback_pcm_prepare, .cleanup = playback_pcm_cleanup }, @@ -3171,6 +3295,10 @@ static const struct hda_pcm_stream pcm_analog_alt_playback = { .channels_min = 2, .channels_max = 2, /* NID is set in build_pcms */ + .ops = { + .open = alt_playback_pcm_open, + .close = alt_playback_pcm_close + }, }; static const struct hda_pcm_stream pcm_analog_alt_capture = { diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h index 85d138fc10b3..5c1569c69888 100644 --- a/sound/pci/hda/hda_generic.h +++ b/sound/pci/hda/hda_generic.h @@ -65,6 +65,9 @@ struct automic_entry { unsigned int attr; /* pin attribute (INPUT_PIN_ATTR_*) */ }; +/* active stream id */ +enum { STREAM_MULTI_OUT, STREAM_INDEP_HP }; + struct hda_gen_spec { char stream_name_analog[32]; /* analog PCM stream */ const struct hda_pcm_stream *stream_analog_playback; @@ -76,6 +79,10 @@ struct hda_gen_spec { const struct hda_pcm_stream *stream_digital_playback; const struct hda_pcm_stream *stream_digital_capture; + /* PCM */ + unsigned int active_streams; + struct mutex pcm_mutex; + /* playback */ struct hda_multi_out multiout; /* playback set-up * max_channels, dacs must be set @@ -150,6 +157,8 @@ struct hda_gen_spec { unsigned int inv_dmic_split:1; /* inverted dmic w/a for conexant */ unsigned int own_eapd_ctl:1; /* set EAPD by own function */ unsigned int vmaster_mute_enum:1; /* add vmaster mute mode enum */ + unsigned int indep_hp:1; /* independent HP supported */ + unsigned int indep_hp_enabled:1; /* independent HP enabled */ /* for virtual master */ hda_nid_t vmaster_nid; -- cgit v1.2.3 From ecac3ed174034755ebc0b29393fb501ffd0b2afc Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 21 Dec 2012 15:23:01 +0100 Subject: ALSA: hda - Add inv_eapd flag to struct hda_codec Add the new flag, codec->inv_eapd, indicating that the EAPD implementation is inverted. There are always broken hardware in the world. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_codec.h | 1 + sound/pci/hda/hda_generic.c | 2 ++ 2 files changed, 3 insertions(+) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index 2d9a51cb4a8e..369ffafae8a9 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -866,6 +866,7 @@ struct hda_codec { unsigned int pins_shutup:1; /* pins are shut up */ unsigned int no_trigger_sense:1; /* don't trigger at pin-sensing */ unsigned int no_jack_detect:1; /* Machine has no jack-detection */ + unsigned int inv_eapd:1; /* broken h/w: inverted EAPD control */ unsigned int pcm_format_first:1; /* PCM format must be set first */ unsigned int epss:1; /* supporting EPSS? */ unsigned int cached_write:1; /* write only to caches */ diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index ff15aea836da..f5bc1853ddd8 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -545,6 +545,8 @@ static void set_pin_eapd(struct hda_codec *codec, hda_nid_t pin, bool enable) if (spec->own_eapd_ctl || !(snd_hda_query_pin_caps(codec, pin) & AC_PINCAP_EAPD)) return; + if (codec->inv_eapd) + enable = !enable; snd_hda_codec_update_cache(codec, pin, 0, AC_VERB_SET_EAPD_BTLENABLE, enable ? 0x02 : 0x00); -- cgit v1.2.3 From 9cc159c6647f505be1205eb5e3fc3b1f84e1d4fd Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 21 Dec 2012 15:31:41 +0100 Subject: ALSA: hda - Add codec->inv_jack_detect flag Yet another broken hardware workaround: there are hardware indicating the inverted jack detection bit result. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_codec.h | 1 + sound/pci/hda/hda_jack.c | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index 369ffafae8a9..9f241d1af38c 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -867,6 +867,7 @@ struct hda_codec { unsigned int no_trigger_sense:1; /* don't trigger at pin-sensing */ unsigned int no_jack_detect:1; /* Machine has no jack-detection */ unsigned int inv_eapd:1; /* broken h/w: inverted EAPD control */ + unsigned int inv_jack_detect:1; /* broken h/w: inverted detection bit */ unsigned int pcm_format_first:1; /* PCM format must be set first */ unsigned int epss:1; /* supporting EPSS? */ unsigned int cached_write:1; /* write only to caches */ diff --git a/sound/pci/hda/hda_jack.c b/sound/pci/hda/hda_jack.c index 6e9f57bbe667..6479b6501828 100644 --- a/sound/pci/hda/hda_jack.c +++ b/sound/pci/hda/hda_jack.c @@ -39,6 +39,7 @@ EXPORT_SYMBOL_HDA(is_jack_detectable); static u32 read_pin_sense(struct hda_codec *codec, hda_nid_t nid) { u32 pincap; + u32 val; if (!codec->no_trigger_sense) { pincap = snd_hda_query_pin_caps(codec, nid); @@ -46,8 +47,11 @@ static u32 read_pin_sense(struct hda_codec *codec, hda_nid_t nid) snd_hda_codec_read(codec, nid, 0, AC_VERB_SET_PIN_SENSE, 0); } - return snd_hda_codec_read(codec, nid, 0, + val = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_PIN_SENSE, 0); + if (codec->inv_jack_detect) + val ^= AC_PINSENSE_PRESENCE; + return val; } /** -- cgit v1.2.3 From ee8e765b0b6c9274e255025318cf8da1a3e30d45 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 3 Jan 2013 15:25:11 +0100 Subject: ALSA: hda - Revive snd_hda_get_conn_list() Manage the connection list cache using linked lists instead of snd_array, and revive snd_hda_get_conn_list() again, so that we don't have to keep the expanded values locally. This will reduce the stack usage by recursive call of snd_hda_get_conn_index() or parse_nid_path() of the generic parser. The list management doesn't include any mutex protection, thus the caller needs to take care of race appropriately. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_codec.c | 164 ++++++++++++++++++++++++++------------------ sound/pci/hda/hda_codec.h | 4 +- sound/pci/hda/hda_generic.c | 8 +-- 3 files changed, 106 insertions(+), 70 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index f4a0f9d3fd84..7eab3ae5ab6d 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -334,20 +334,51 @@ int snd_hda_get_sub_nodes(struct hda_codec *codec, hda_nid_t nid, } EXPORT_SYMBOL_HDA(snd_hda_get_sub_nodes); +/* connection list element */ +struct hda_conn_list { + struct list_head list; + int len; + hda_nid_t nid; + hda_nid_t conns[0]; +}; + /* look up the cached results */ -static hda_nid_t *lookup_conn_list(struct snd_array *array, hda_nid_t nid) +static struct hda_conn_list * +lookup_conn_list(struct hda_codec *codec, hda_nid_t nid) { - int i, len; - for (i = 0; i < array->used; ) { - hda_nid_t *p = snd_array_elem(array, i); - if (nid == *p) + struct hda_conn_list *p; + list_for_each_entry(p, &codec->conn_list, list) { + if (p->nid == nid) return p; - len = p[1]; - i += len + 2; } return NULL; } +static int add_conn_list(struct hda_codec *codec, hda_nid_t nid, int len, + const hda_nid_t *list) +{ + struct hda_conn_list *p; + + p = kmalloc(sizeof(*p) + len * sizeof(hda_nid_t), GFP_KERNEL); + if (!p) + return -ENOMEM; + p->len = len; + p->nid = nid; + memcpy(p->conns, list, len * sizeof(hda_nid_t)); + list_add(&p->list, &codec->conn_list); + return 0; +} + +static void remove_conn_list(struct hda_codec *codec) +{ + while (!list_empty(&codec->conn_list)) { + struct hda_conn_list *p; + p = list_first_entry(&codec->conn_list, typeof(*p), list); + list_del(&p->list); + kfree(p); + } +} + /* read the connection and add to the cache */ static int read_and_add_raw_conns(struct hda_codec *codec, hda_nid_t nid) { @@ -360,6 +391,49 @@ static int read_and_add_raw_conns(struct hda_codec *codec, hda_nid_t nid) return snd_hda_override_conn_list(codec, nid, len, list); } +/** + * snd_hda_get_conn_list - get connection list + * @codec: the HDA codec + * @nid: NID to parse + * @len: number of connection list entries + * @listp: the pointer to store NID list + * + * Parses the connection list of the given widget and stores the pointer + * to the list of NIDs. + * + * Returns the number of connections, or a negative error code. + * + * Note that the returned pointer isn't protected against the list + * modification. If snd_hda_override_conn_list() might be called + * concurrently, protect with a mutex appropriately. + */ +int snd_hda_get_conn_list(struct hda_codec *codec, hda_nid_t nid, + const hda_nid_t **listp) +{ + bool added = false; + + for (;;) { + int err; + const struct hda_conn_list *p; + + /* if the connection-list is already cached, read it */ + p = lookup_conn_list(codec, nid); + if (p) { + if (listp) + *listp = p->conns; + return p->len; + } + if (snd_BUG_ON(added)) + return -EINVAL; + + err = read_and_add_raw_conns(codec, nid); + if (err < 0) + return err; + added = true; + } +} +EXPORT_SYMBOL_HDA(snd_hda_get_conn_list); + /** * snd_hda_get_connections - copy connection list * @codec: the HDA codec @@ -375,39 +449,20 @@ static int read_and_add_raw_conns(struct hda_codec *codec, hda_nid_t nid) int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid, hda_nid_t *conn_list, int max_conns) { - struct snd_array *array = &codec->conn_lists; - int len; - hda_nid_t *p; - bool added = false; + const hda_nid_t *list; + int len = snd_hda_get_conn_list(codec, nid, &list); - again: - mutex_lock(&codec->hash_mutex); - len = -1; - /* if the connection-list is already cached, read it */ - p = lookup_conn_list(array, nid); - if (p) { - len = p[1]; - if (conn_list && len > max_conns) { + if (len > 0 && conn_list) { + if (len > max_conns) { snd_printk(KERN_ERR "hda_codec: " "Too many connections %d for NID 0x%x\n", len, nid); - mutex_unlock(&codec->hash_mutex); return -EINVAL; } - if (conn_list && len) - memcpy(conn_list, p + 2, len * sizeof(hda_nid_t)); + memcpy(conn_list, list, len * sizeof(hda_nid_t)); } - mutex_unlock(&codec->hash_mutex); - if (len >= 0) - return len; - if (snd_BUG_ON(added)) - return -EINVAL; - len = read_and_add_raw_conns(codec, nid); - if (len < 0) - return len; - added = true; - goto again; + return len; } EXPORT_SYMBOL_HDA(snd_hda_get_connections); @@ -519,15 +574,6 @@ int snd_hda_get_raw_connections(struct hda_codec *codec, hda_nid_t nid, return conns; } -static bool add_conn_list(struct snd_array *array, hda_nid_t nid) -{ - hda_nid_t *p = snd_array_new(array); - if (!p) - return false; - *p = nid; - return true; -} - /** * snd_hda_override_conn_list - add/modify the connection-list to cache * @codec: the HDA codec @@ -543,28 +589,15 @@ static bool add_conn_list(struct snd_array *array, hda_nid_t nid) int snd_hda_override_conn_list(struct hda_codec *codec, hda_nid_t nid, int len, const hda_nid_t *list) { - struct snd_array *array = &codec->conn_lists; - hda_nid_t *p; - int i, old_used; + struct hda_conn_list *p; - mutex_lock(&codec->hash_mutex); - p = lookup_conn_list(array, nid); - if (p) - *p = -1; /* invalidate the old entry */ - - old_used = array->used; - if (!add_conn_list(array, nid) || !add_conn_list(array, len)) - goto error_add; - for (i = 0; i < len; i++) - if (!add_conn_list(array, list[i])) - goto error_add; - mutex_unlock(&codec->hash_mutex); - return 0; + p = lookup_conn_list(codec, nid); + if (p) { + list_del(&p->list); + kfree(p); + } - error_add: - array->used = old_used; - mutex_unlock(&codec->hash_mutex); - return -ENOMEM; + return add_conn_list(codec, nid, len, list); } EXPORT_SYMBOL_HDA(snd_hda_override_conn_list); @@ -582,10 +615,10 @@ EXPORT_SYMBOL_HDA(snd_hda_override_conn_list); int snd_hda_get_conn_index(struct hda_codec *codec, hda_nid_t mux, hda_nid_t nid, int recursive) { - hda_nid_t conn[HDA_MAX_NUM_INPUTS]; + const hda_nid_t *conn; int i, nums; - nums = snd_hda_get_connections(codec, mux, conn, ARRAY_SIZE(conn)); + nums = snd_hda_get_conn_list(codec, mux, &conn); for (i = 0; i < nums; i++) if (conn[i] == nid) return i; @@ -1186,8 +1219,8 @@ static void snd_hda_codec_free(struct hda_codec *codec) snd_array_free(&codec->mixers); snd_array_free(&codec->nids); snd_array_free(&codec->cvt_setups); - snd_array_free(&codec->conn_lists); snd_array_free(&codec->spdif_out); + remove_conn_list(codec); codec->bus->caddr_tbl[codec->addr] = NULL; if (codec->patch_ops.free) codec->patch_ops.free(codec); @@ -1257,10 +1290,11 @@ int snd_hda_codec_new(struct hda_bus *bus, snd_array_init(&codec->init_pins, sizeof(struct hda_pincfg), 16); snd_array_init(&codec->driver_pins, sizeof(struct hda_pincfg), 16); snd_array_init(&codec->cvt_setups, sizeof(struct hda_cvt_setup), 8); - snd_array_init(&codec->conn_lists, sizeof(hda_nid_t), 64); snd_array_init(&codec->spdif_out, sizeof(struct hda_spdif_out), 16); snd_array_init(&codec->jacktbl, sizeof(struct hda_jack_tbl), 16); snd_array_init(&codec->verbs, sizeof(struct hda_verb *), 8); + INIT_LIST_HEAD(&codec->conn_list); + INIT_DELAYED_WORK(&codec->jackpoll_work, hda_jackpoll_work); #ifdef CONFIG_PM diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index 9f241d1af38c..93ec747ce904 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -831,7 +831,7 @@ struct hda_codec { struct hda_cache_rec amp_cache; /* cache for amp access */ struct hda_cache_rec cmd_cache; /* cache for other commands */ - struct snd_array conn_lists; /* connection-list array */ + struct list_head conn_list; /* linked-list of connection-list */ struct mutex spdif_mutex; struct mutex control_mutex; @@ -944,6 +944,8 @@ snd_hda_get_num_conns(struct hda_codec *codec, hda_nid_t nid) } int snd_hda_get_raw_connections(struct hda_codec *codec, hda_nid_t nid, hda_nid_t *conn_list, int max_conns); +int snd_hda_get_conn_list(struct hda_codec *codec, hda_nid_t nid, + const hda_nid_t **listp); int snd_hda_override_conn_list(struct hda_codec *codec, hda_nid_t nid, int nums, const hda_nid_t *list); int snd_hda_get_conn_index(struct hda_codec *codec, hda_nid_t mux, diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index f5bc1853ddd8..88f166e65752 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -208,7 +208,7 @@ static bool __parse_nid_path(struct hda_codec *codec, int with_aa_mix, struct nid_path *path, int depth) { struct hda_gen_spec *spec = codec->spec; - hda_nid_t conn[16]; + const hda_nid_t *conn; int i, nums; if (to_nid == spec->mixer_nid) { @@ -217,7 +217,7 @@ static bool __parse_nid_path(struct hda_codec *codec, with_aa_mix = HDA_PARSE_ALL; /* mark aa-mix is included */ } - nums = snd_hda_get_connections(codec, to_nid, conn, ARRAY_SIZE(conn)); + nums = snd_hda_get_conn_list(codec, to_nid, &conn); for (i = 0; i < nums; i++) { if (conn[i] != from_nid) { /* special case: when from_nid is 0, @@ -481,12 +481,12 @@ static void activate_amp_in(struct hda_codec *codec, struct nid_path *path, int i, bool enable, bool add_aamix) { struct hda_gen_spec *spec = codec->spec; - hda_nid_t conn[16]; + const hda_nid_t *conn; int n, nums, idx; int type; hda_nid_t nid = path->path[i]; - nums = snd_hda_get_connections(codec, nid, conn, ARRAY_SIZE(conn)); + nums = snd_hda_get_conn_list(codec, nid, &conn); type = get_wcaps_type(get_wcaps(codec, nid)); if (type == AC_WID_PIN || (type == AC_WID_AUD_IN && codec->single_adc_amp)) { -- cgit v1.2.3 From 2e03e9528d1cc15edf037c8e2ee0ae6499b0e59d Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 3 Jan 2013 15:55:06 +0100 Subject: ALSA: hda - Add hooks for HP/line/mic auto switching ... as a preliminary work for migrating patch_sigmatel.c. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 6 ++++++ sound/pci/hda/hda_generic.h | 8 ++++++++ 2 files changed, 14 insertions(+) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 88f166e65752..6ff4a0db74e9 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -2780,6 +2780,8 @@ static int check_auto_mute_availability(struct hda_codec *codec) snd_printdd("hda-codec: Enable HP auto-muting on NID 0x%x\n", nid); snd_hda_jack_detect_enable_callback(codec, nid, HDA_GEN_HP_EVENT, + spec->hp_automute_hook ? + spec->hp_automute_hook : snd_hda_gen_hp_automute); spec->detect_hp = 1; } @@ -2793,6 +2795,8 @@ static int check_auto_mute_availability(struct hda_codec *codec) snd_printdd("hda-codec: Enable Line-Out auto-muting on NID 0x%x\n", nid); snd_hda_jack_detect_enable_callback(codec, nid, HDA_GEN_FRONT_EVENT, + spec->line_automute_hook ? + spec->line_automute_hook : snd_hda_gen_line_automute); spec->detect_lo = 1; } @@ -2845,6 +2849,8 @@ static bool auto_mic_check_imux(struct hda_codec *codec) snd_hda_jack_detect_enable_callback(codec, spec->am_entry[i].pin, HDA_GEN_MIC_EVENT, + spec->mic_autoswitch_hook ? + spec->mic_autoswitch_hook : snd_hda_gen_mic_autoswitch); return true; } diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h index 5c1569c69888..1090a524755b 100644 --- a/sound/pci/hda/hda_generic.h +++ b/sound/pci/hda/hda_generic.h @@ -180,6 +180,14 @@ struct hda_gen_spec { void (*init_hook)(struct hda_codec *codec); void (*automute_hook)(struct hda_codec *codec); void (*cap_sync_hook)(struct hda_codec *codec); + + /* automute / autoswitch hooks */ + void (*hp_automute_hook)(struct hda_codec *codec, + struct hda_jack_tbl *tbl); + void (*line_automute_hook)(struct hda_codec *codec, + struct hda_jack_tbl *tbl); + void (*mic_autoswitch_hook)(struct hda_codec *codec, + struct hda_jack_tbl *tbl); }; int snd_hda_gen_spec_init(struct hda_gen_spec *spec); -- cgit v1.2.3 From 985803ca91c3039afd15a2fd32a9ef5771652cee Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 3 Jan 2013 16:30:04 +0100 Subject: ALSA: hda - Don't skip amp init for activated paths activate_amp() in the generic parser checks whether the given NID is included in any active paths and skips it if found. This was a workaround for avoiding disabling the widgets in the active paths when one path is disabled, thus it shouldn't be applied to the case for path activation. Due to this wrong check, some analog loopback paths haven't been initialized correctly. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 6ff4a0db74e9..9228175f0806 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -463,7 +463,7 @@ static void activate_amp(struct hda_codec *codec, hda_nid_t nid, int dir, { int val; if (is_ctl_associated(codec, nid, dir, idx) || - is_active_nid(codec, nid, dir, idx)) + (!enable && is_active_nid(codec, nid, dir, idx))) return; val = get_amp_val_to_activate(codec, nid, dir, enable); snd_hda_codec_amp_stereo(codec, nid, dir, idx, 0xff, val); -- cgit v1.2.3 From e1284af730792344f96e1428a6199e19699dfccc Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 3 Jan 2013 16:33:02 +0100 Subject: ALSA: hda - Initialize output paths with current active states Set path->active flag at the path creation time and let the paths initialized according to the current path->active state in set_output_and_unmute(). This allows to modify the active flag of some output paths dynamically, e.g. switching the front output route with or without aamix like patch_via.c. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 9228175f0806..3507448c8b0d 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -873,8 +873,10 @@ static int try_assign_dacs(struct hda_codec *codec, int num_outs, } if (!path) dac = dacs[i] = 0; - else + else { print_nid_path("output", path); + path->active = true; + } if (dac) badness += assign_out_path_ctls(codec, pin, dac); } @@ -1045,6 +1047,7 @@ static bool map_singles(struct hda_codec *codec, int outs, dacs[i] = dac; found = true; print_nid_path("output", path); + path->active = true; } } return found; @@ -2418,6 +2421,7 @@ static void parse_digital(struct hda_codec *codec) if (!path) continue; print_nid_path("digout", path); + path->active = true; if (!nums) { spec->multiout.dig_out_nid = dig_nid; spec->dig_out_type = spec->autocfg.dig_out_type[0]; @@ -3556,10 +3560,8 @@ static void set_output_and_unmute(struct hda_codec *codec, hda_nid_t pin, path = snd_hda_get_nid_path(codec, dac, pin); if (!path) return; - if (path->active) - return; - snd_hda_activate_path(codec, path, true, true); - set_pin_eapd(codec, pin, true); + snd_hda_activate_path(codec, path, path->active, true); + set_pin_eapd(codec, pin, path->active); } /* initialize primary output paths */ -- cgit v1.2.3 From 1e0b528696edf20ad38f494dda49c6195bee1b7f Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 4 Jan 2013 12:56:52 +0100 Subject: ALSA: hda - Avoid duplicated path creations When the paths are created in map_singles(), we don't have to re-create new paths in try_assign_dacs(). Just evaluate the badness and skip to the next item. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 3507448c8b0d..ee2c973f9125 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -833,8 +833,13 @@ static int try_assign_dacs(struct hda_codec *codec, int num_outs, for (i = 0; i < num_outs; i++) { struct nid_path *path; hda_nid_t pin = pins[i]; - if (!dacs[i]) - dacs[i] = look_for_dac(codec, pin, false); + + if (dacs[i]) { + badness += assign_out_path_ctls(codec, pin, dacs[i]); + continue; + } + + dacs[i] = look_for_dac(codec, pin, false); if (!dacs[i] && !i) { for (j = 1; j < num_outs; j++) { if (is_reachable_path(codec, dacs[j], pin)) { -- cgit v1.2.3 From f5172a7ed966493414aa58319fbb7ab0a80cf889 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 4 Jan 2013 13:19:55 +0100 Subject: ALSA: hda - Check the existing path in snd_hda_add_new_path() If the requested path has been already added, return the existing path instance instead of adding a duplicated instance. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 31 ++++++++++++++++++++++++------- sound/pci/hda/hda_generic.h | 1 + 2 files changed, 25 insertions(+), 7 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index ee2c973f9125..f9ecbe09a526 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -116,11 +116,9 @@ EXPORT_SYMBOL_HDA(snd_hda_gen_spec_free); * parsing paths */ -/* get the path between the given NIDs; - * passing 0 to either @pin or @dac behaves as a wildcard - */ -struct nid_path *snd_hda_get_nid_path(struct hda_codec *codec, - hda_nid_t from_nid, hda_nid_t to_nid) +static struct nid_path *get_nid_path(struct hda_codec *codec, + hda_nid_t from_nid, hda_nid_t to_nid, + int with_aa_mix) { struct hda_gen_spec *spec = codec->spec; int i; @@ -130,11 +128,23 @@ struct nid_path *snd_hda_get_nid_path(struct hda_codec *codec, if (path->depth <= 0) continue; if ((!from_nid || path->path[0] == from_nid) && - (!to_nid || path->path[path->depth - 1] == to_nid)) - return path; + (!to_nid || path->path[path->depth - 1] == to_nid)) { + if (with_aa_mix == HDA_PARSE_ALL || + path->with_aa_mix == with_aa_mix) + return path; + } } return NULL; } + +/* get the path between the given NIDs; + * passing 0 to either @pin or @dac behaves as a wildcard + */ +struct nid_path *snd_hda_get_nid_path(struct hda_codec *codec, + hda_nid_t from_nid, hda_nid_t to_nid) +{ + return get_nid_path(codec, from_nid, to_nid, HDA_PARSE_ALL); +} EXPORT_SYMBOL_HDA(snd_hda_get_nid_path); /* check whether the given DAC is already found in any existing paths */ @@ -248,6 +258,8 @@ static bool __parse_nid_path(struct hda_codec *codec, found: path->path[path->depth] = conn[i]; + if (conn[i] == spec->mixer_nid) + path->with_aa_mix = true; path->idx[path->depth + 1] = i; if (nums > 1 && get_wcaps_type(get_wcaps(codec, to_nid)) != AC_WID_AUD_MIX) path->multi[path->depth + 1] = 1; @@ -290,6 +302,11 @@ snd_hda_add_new_path(struct hda_codec *codec, hda_nid_t from_nid, if (from_nid && to_nid && !is_reachable_path(codec, from_nid, to_nid)) return NULL; + /* check whether the path has been already added */ + path = get_nid_path(codec, from_nid, to_nid, with_aa_mix); + if (path) + return path; + path = snd_array_new(&spec->paths); if (!path) return NULL; diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h index 1090a524755b..f1cae2e49377 100644 --- a/sound/pci/hda/hda_generic.h +++ b/sound/pci/hda/hda_generic.h @@ -53,6 +53,7 @@ struct nid_path { unsigned char multi[MAX_NID_PATH_DEPTH]; unsigned int ctls[NID_PATH_NUM_CTLS]; /* NID_PATH_XXX_CTL */ bool active; + bool with_aa_mix; }; /* mic/line-in auto switching entry */ -- cgit v1.2.3 From e22aab7dcf7c8e77a503dcde8cd2c548d0df0cdc Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 4 Jan 2013 14:50:04 +0100 Subject: ALSA: hda - Simplify the multi-io assignment with multi speakers When speakers are chosen as the the primary output during evaluation, we did some tricks to assign the possible multi-io jacks with a certain offset value to multi_out dacs. This was a workaround for the case with multiple speakers like Acer Aspire. But this is quite ugly at the same time and the resultant code is hard to understand. More badly, it works wrongly for 2.1 speakers like Apple iMac91. In this patch, instead of fiddling with the offset to multi_out dacs, simply add a certain badness number if headphone(s) + multi-ios are possible. This simplify the code a bit, and it's more robust. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 63 +++++++++++++++++++++++---------------------- 1 file changed, 32 insertions(+), 31 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index f9ecbe09a526..f9d3ea3c5a68 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -943,6 +943,28 @@ static bool can_be_multiio_pin(struct hda_codec *codec, return true; } +/* count the number of input pins that are capable to be multi-io */ +static int count_multiio_pins(struct hda_codec *codec, hda_nid_t reference_pin) +{ + struct hda_gen_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + unsigned int defcfg = snd_hda_codec_get_pincfg(codec, reference_pin); + unsigned int location = get_defcfg_location(defcfg); + int type, i; + int num_pins = 0; + + for (type = AUTO_PIN_LINE_IN; type >= AUTO_PIN_MIC; type--) { + for (i = 0; i < cfg->num_inputs; i++) { + if (cfg->inputs[i].type != type) + continue; + if (can_be_multiio_pin(codec, location, + cfg->inputs[i].pin)) + num_pins++; + } + } + return num_pins; +} + /* * multi-io helper * @@ -953,11 +975,11 @@ static bool can_be_multiio_pin(struct hda_codec *codec, */ static int fill_multi_ios(struct hda_codec *codec, hda_nid_t reference_pin, - bool hardwired, int offset) + bool hardwired) { struct hda_gen_spec *spec = codec->spec; struct auto_pin_cfg *cfg = &spec->autocfg; - int type, i, j, dacs, num_pins, old_pins; + int type, i, j, num_pins, old_pins; unsigned int defcfg = snd_hda_codec_get_pincfg(codec, reference_pin); unsigned int location = get_defcfg_location(defcfg); int badness = 0; @@ -966,20 +988,10 @@ static int fill_multi_ios(struct hda_codec *codec, if (old_pins >= 2) goto end_fill; - num_pins = 0; - for (type = AUTO_PIN_LINE_IN; type >= AUTO_PIN_MIC; type--) { - for (i = 0; i < cfg->num_inputs; i++) { - if (cfg->inputs[i].type != type) - continue; - if (can_be_multiio_pin(codec, location, - cfg->inputs[i].pin)) - num_pins++; - } - } + num_pins = count_multiio_pins(codec, reference_pin); if (num_pins < 2) goto end_fill; - dacs = spec->multiout.num_dacs; for (type = AUTO_PIN_LINE_IN; type >= AUTO_PIN_MIC; type--) { for (i = 0; i < cfg->num_inputs; i++) { struct nid_path *path; @@ -997,11 +1009,6 @@ static int fill_multi_ios(struct hda_codec *codec, if (j < spec->multi_ios) continue; - if (offset && offset + spec->multi_ios < dacs) { - dac = spec->private_dac_nids[offset + spec->multi_ios]; - if (!is_reachable_path(codec, dac, nid)) - dac = 0; - } if (hardwired) dac = get_dac_if_single(codec, nid); else if (!dac) @@ -1109,7 +1116,7 @@ static int fill_and_eval_dacs(struct hda_codec *codec, spec->multiout.extra_out_nid); if (fill_mio_first && cfg->line_outs == 1 && cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) { - err = fill_multi_ios(codec, cfg->line_out_pins[0], true, 0); + err = fill_multi_ios(codec, cfg->line_out_pins[0], true); if (!err) mapped = true; } @@ -1136,7 +1143,7 @@ static int fill_and_eval_dacs(struct hda_codec *codec, if (fill_mio_first && cfg->line_outs == 1 && cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) { /* try to fill multi-io first */ - err = fill_multi_ios(codec, cfg->line_out_pins[0], false, 0); + err = fill_multi_ios(codec, cfg->line_out_pins[0], false); if (err < 0) return err; /* we don't count badness at this stage yet */ @@ -1160,22 +1167,16 @@ static int fill_and_eval_dacs(struct hda_codec *codec, badness += err; } if (cfg->line_outs == 1 && cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) { - err = fill_multi_ios(codec, cfg->line_out_pins[0], false, 0); - if (err < 0) - return err; - badness += err; - } - if (cfg->hp_outs && cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) { - /* try multi-ios with HP + inputs */ - int offset = 0; - if (cfg->line_outs >= 3) - offset = 1; - err = fill_multi_ios(codec, cfg->hp_pins[0], false, offset); + err = fill_multi_ios(codec, cfg->line_out_pins[0], false); if (err < 0) return err; badness += err; } + if (cfg->hp_outs && cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) + if (count_multiio_pins(codec, cfg->hp_pins[0]) >= 2) + spec->multi_ios = 1; /* give badness */ + if (spec->multi_ios == 2) { for (i = 0; i < 2; i++) spec->private_dac_nids[spec->multiout.num_dacs++] = -- cgit v1.2.3 From 05453b7e97996a37db4dd7b97a788124b117dbde Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 4 Jan 2013 15:00:02 +0100 Subject: ALSA: hda - Fix multi-io pin assignment in create_multi_out_ctls() The multi-io pins are calculated with a blind assumption of cfg->line_outs = 1. This isn't always true. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index f9d3ea3c5a68..93db02121efb 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -1368,7 +1368,7 @@ static int create_multi_out_ctls(struct hda_codec *codec, if (!dac) continue; if (i >= cfg->line_outs) { - pin = spec->multi_io[i - 1].pin; + pin = spec->multi_io[i - cfg->line_outs].pin; index = 0; name = channel_name[i]; } else { -- cgit v1.2.3 From 196c17668056ed5226070d06878242c116dfece2 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 4 Jan 2013 15:01:40 +0100 Subject: ALSA: hda - Manage using output/loopback path indices Instead of search for the path with the certain route at each time, keep the path index for each output and loopback, and just use it when referred. In this implementation, the path index number begins with one, not zero (although I've been writing in C over decades). It's just to make the check for uninitialized values easier. So far, the input paths aren't handled with indices yet, but still picked up via snd_hda_get_nid_path() at each time. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 139 ++++++++++++++++++++++++++++---------------- sound/pci/hda/hda_generic.h | 9 +++ 2 files changed, 98 insertions(+), 50 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 93db02121efb..c8bf81230206 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -147,6 +147,33 @@ struct nid_path *snd_hda_get_nid_path(struct hda_codec *codec, } EXPORT_SYMBOL_HDA(snd_hda_get_nid_path); +/* get the index number corresponding to the path instance; + * the index starts from 1, for easier checking the invalid value + */ +int snd_hda_get_path_idx(struct hda_codec *codec, struct nid_path *path) +{ + struct hda_gen_spec *spec = codec->spec; + struct nid_path *array = spec->paths.list; + ssize_t idx; + + if (!spec->paths.used) + return 0; + idx = path - array; + if (idx < 0 || idx >= spec->paths.used) + return 0; + return idx + 1; +} + +/* get the path instance corresponding to the given index number */ +struct nid_path *snd_hda_get_path_from_idx(struct hda_codec *codec, int idx) +{ + struct hda_gen_spec *spec = codec->spec; + + if (idx <= 0 || idx > spec->paths.used) + return NULL; + return snd_array_elem(&spec->paths, idx - 1); +} + /* check whether the given DAC is already found in any existing paths */ static bool is_dac_already_used(struct hda_codec *codec, hda_nid_t nid) { @@ -836,6 +863,7 @@ static struct badness_table extra_out_badness = { /* try to assign DACs to pins and return the resultant badness */ static int try_assign_dacs(struct hda_codec *codec, int num_outs, const hda_nid_t *pins, hda_nid_t *dacs, + int *path_idx, const struct badness_table *bad) { struct hda_gen_spec *spec = codec->spec; @@ -862,6 +890,7 @@ static int try_assign_dacs(struct hda_codec *codec, int num_outs, if (is_reachable_path(codec, dacs[j], pin)) { dacs[0] = dacs[j]; dacs[j] = 0; + path_idx[j] = 0; break; } } @@ -898,6 +927,7 @@ static int try_assign_dacs(struct hda_codec *codec, int num_outs, else { print_nid_path("output", path); path->active = true; + path_idx[i] = snd_hda_get_path_idx(codec, path); } if (dac) badness += assign_out_path_ctls(codec, pin, dac); @@ -1025,6 +1055,8 @@ static int fill_multi_ios(struct hda_codec *codec, print_nid_path("multiio", path); spec->multi_io[spec->multi_ios].pin = nid; spec->multi_io[spec->multi_ios].dac = dac; + spec->out_paths[cfg->line_outs + spec->multi_ios] = + snd_hda_get_path_idx(codec, path); spec->multi_ios++; if (spec->multi_ios >= 2) break; @@ -1056,7 +1088,7 @@ static int fill_multi_ios(struct hda_codec *codec, /* map DACs for all pins in the list if they are single connections */ static bool map_singles(struct hda_codec *codec, int outs, - const hda_nid_t *pins, hda_nid_t *dacs) + const hda_nid_t *pins, hda_nid_t *dacs, int *path_idx) { struct hda_gen_spec *spec = codec->spec; int i; @@ -1077,6 +1109,7 @@ static bool map_singles(struct hda_codec *codec, int outs, found = true; print_nid_path("output", path); path->active = true; + path_idx[i] = snd_hda_get_path_idx(codec, path); } } return found; @@ -1107,13 +1140,16 @@ static int fill_and_eval_dacs(struct hda_codec *codec, do { mapped = map_singles(codec, cfg->line_outs, cfg->line_out_pins, - spec->private_dac_nids); + spec->private_dac_nids, + spec->out_paths); mapped |= map_singles(codec, cfg->hp_outs, cfg->hp_pins, - spec->multiout.hp_out_nid); + spec->multiout.hp_out_nid, + spec->hp_paths); mapped |= map_singles(codec, cfg->speaker_outs, cfg->speaker_pins, - spec->multiout.extra_out_nid); + spec->multiout.extra_out_nid, + spec->speaker_paths); if (fill_mio_first && cfg->line_outs == 1 && cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) { err = fill_multi_ios(codec, cfg->line_out_pins[0], true); @@ -1124,7 +1160,7 @@ static int fill_and_eval_dacs(struct hda_codec *codec, } badness += try_assign_dacs(codec, cfg->line_outs, cfg->line_out_pins, - spec->private_dac_nids, + spec->private_dac_nids, spec->out_paths, &main_out_badness); /* re-count num_dacs and squash invalid entries */ @@ -1152,6 +1188,7 @@ static int fill_and_eval_dacs(struct hda_codec *codec, if (cfg->line_out_type != AUTO_PIN_HP_OUT) { err = try_assign_dacs(codec, cfg->hp_outs, cfg->hp_pins, spec->multiout.hp_out_nid, + spec->hp_paths, &extra_out_badness); if (err < 0) return err; @@ -1161,7 +1198,8 @@ static int fill_and_eval_dacs(struct hda_codec *codec, err = try_assign_dacs(codec, cfg->speaker_outs, cfg->speaker_pins, spec->multiout.extra_out_nid, - &extra_out_badness); + spec->speaker_paths, + &extra_out_badness); if (err < 0) return err; badness += err; @@ -1336,9 +1374,7 @@ static int parse_output_paths(struct hda_codec *codec) if (cfg->line_out_pins[0]) { struct nid_path *path; - path = snd_hda_get_nid_path(codec, - spec->multiout.dac_nids[0], - cfg->line_out_pins[0]); + path = snd_hda_get_path_from_idx(codec, spec->out_paths[0]); if (path) spec->vmaster_nid = look_for_out_vol_nid(codec, path); } @@ -1361,22 +1397,20 @@ static int create_multi_out_ctls(struct hda_codec *codec, for (i = 0; i < noutputs; i++) { const char *name; int index; - hda_nid_t dac, pin; + hda_nid_t dac; struct nid_path *path; dac = spec->multiout.dac_nids[i]; if (!dac) continue; if (i >= cfg->line_outs) { - pin = spec->multi_io[i - cfg->line_outs].pin; index = 0; name = channel_name[i]; } else { - pin = cfg->line_out_pins[i]; name = get_line_out_pfx(spec, i, true, &index); } - path = snd_hda_get_nid_path(codec, dac, pin); + path = snd_hda_get_path_from_idx(codec, spec->out_paths[i]); if (!path) continue; if (!name || !strcmp(name, "CLFE")) { @@ -1406,12 +1440,13 @@ static int create_multi_out_ctls(struct hda_codec *codec, } static int create_extra_out(struct hda_codec *codec, hda_nid_t pin, - hda_nid_t dac, const char *pfx, int cidx) + hda_nid_t dac, int path_idx, + const char *pfx, int cidx) { struct nid_path *path; int err; - path = snd_hda_get_nid_path(codec, dac, pin); + path = snd_hda_get_path_from_idx(codec, path_idx); if (!path) return 0; /* bind volume control will be created in the case of dac = 0 */ @@ -1429,7 +1464,7 @@ static int create_extra_out(struct hda_codec *codec, hda_nid_t pin, /* add playback controls for speaker and HP outputs */ static int create_extra_outs(struct hda_codec *codec, int num_pins, const hda_nid_t *pins, const hda_nid_t *dacs, - const char *pfx) + const int *paths, const char *pfx) { struct hda_gen_spec *spec = codec->spec; struct hda_bind_ctls *ctl; @@ -1443,7 +1478,7 @@ static int create_extra_outs(struct hda_codec *codec, int num_pins, hda_nid_t dac = *dacs; if (!dac) dac = spec->multiout.dac_nids[0]; - return create_extra_out(codec, *pins, dac, pfx, 0); + return create_extra_out(codec, *pins, dac, paths[0], pfx, 0); } for (i = 0; i < num_pins; i++) { @@ -1453,14 +1488,16 @@ static int create_extra_outs(struct hda_codec *codec, int num_pins, else dac = 0; if (num_pins == 2 && i == 1 && !strcmp(pfx, "Speaker")) { - err = create_extra_out(codec, pins[i], dac, + err = create_extra_out(codec, pins[i], dac, paths[i], "Bass Speaker", 0); } else if (num_pins >= 3) { snprintf(name, sizeof(name), "%s %s", pfx, channel_name[i]); - err = create_extra_out(codec, pins[i], dac, name, 0); + err = create_extra_out(codec, pins[i], dac, paths[i], + name, 0); } else { - err = create_extra_out(codec, pins[i], dac, pfx, i); + err = create_extra_out(codec, pins[i], dac, paths[i], + pfx, i); } if (err < 0) return err; @@ -1478,7 +1515,7 @@ static int create_extra_outs(struct hda_codec *codec, int num_pins, struct nid_path *path; if (!pins[i] || !dacs[i]) continue; - path = snd_hda_get_nid_path(codec, dacs[i], pins[i]); + path = snd_hda_get_path_from_idx(codec, paths[i]); if (!path) continue; vol = look_for_out_vol_nid(codec, path); @@ -1501,6 +1538,7 @@ static int create_hp_out_ctls(struct hda_codec *codec) return create_extra_outs(codec, spec->autocfg.hp_outs, spec->autocfg.hp_pins, spec->multiout.hp_out_nid, + spec->hp_paths, "Headphone"); } @@ -1510,6 +1548,7 @@ static int create_speaker_out_ctls(struct hda_codec *codec) return create_extra_outs(codec, spec->autocfg.speaker_outs, spec->autocfg.speaker_pins, spec->multiout.extra_out_nid, + spec->speaker_paths, "Speaker"); } @@ -1615,13 +1654,21 @@ static int ch_mode_get(struct snd_kcontrol *kcontrol, return 0; } +static inline struct nid_path * +get_multiio_path(struct hda_codec *codec, int idx) +{ + struct hda_gen_spec *spec = codec->spec; + return snd_hda_get_path_from_idx(codec, + spec->out_paths[spec->autocfg.line_outs + idx]); +} + static int set_multi_io(struct hda_codec *codec, int idx, bool output) { struct hda_gen_spec *spec = codec->spec; hda_nid_t nid = spec->multi_io[idx].pin; struct nid_path *path; - path = snd_hda_get_nid_path(codec, spec->multi_io[idx].dac, nid); + path = get_multiio_path(codec, idx); if (!path) return -EINVAL; @@ -1775,8 +1822,8 @@ static void add_loopback_list(struct hda_gen_spec *spec, hda_nid_t mix, int idx) #endif /* create input playback/capture controls for the given pin */ -static int new_analog_input(struct hda_codec *codec, hda_nid_t pin, - const char *ctlname, int ctlidx, +static int new_analog_input(struct hda_codec *codec, int input_idx, + hda_nid_t pin, const char *ctlname, int ctlidx, hda_nid_t mix_nid) { struct hda_gen_spec *spec = codec->spec; @@ -1792,6 +1839,7 @@ static int new_analog_input(struct hda_codec *codec, hda_nid_t pin, if (!path) return -EINVAL; print_nid_path("loopback", path); + spec->loopback_paths[input_idx] = snd_hda_get_path_idx(codec, path); idx = path->idx[path->depth - 1]; if (nid_has_volume(codec, mix_nid, HDA_INPUT)) { @@ -1944,7 +1992,7 @@ static int create_input_ctls(struct hda_codec *codec) if (mixer) { if (is_reachable_path(codec, pin, mixer)) { - err = new_analog_input(codec, pin, + err = new_analog_input(codec, i, pin, label, type_idx, mixer); if (err < 0) return err; @@ -2445,6 +2493,7 @@ static void parse_digital(struct hda_codec *codec) continue; print_nid_path("digout", path); path->active = true; + spec->digout_paths[i] = snd_hda_get_path_idx(codec, path); if (!nums) { spec->multiout.dig_out_nid = dig_nid; spec->dig_out_type = spec->autocfg.dig_out_type[0]; @@ -3575,12 +3624,12 @@ EXPORT_SYMBOL_HDA(snd_hda_gen_build_pcms); /* configure the path from the given dac to the pin as the proper output */ static void set_output_and_unmute(struct hda_codec *codec, hda_nid_t pin, - int pin_type, hda_nid_t dac) + int pin_type, int path_idx) { struct nid_path *path; snd_hda_set_pin_ctl_cache(codec, pin, pin_type); - path = snd_hda_get_nid_path(codec, dac, pin); + path = snd_hda_get_path_from_idx(codec, path_idx); if (!path) return; snd_hda_activate_path(codec, path, path->active, true); @@ -3591,7 +3640,7 @@ static void set_output_and_unmute(struct hda_codec *codec, hda_nid_t pin, static void init_multi_out(struct hda_codec *codec) { struct hda_gen_spec *spec = codec->spec; - hda_nid_t nid, dac; + hda_nid_t nid; int pin_type; int i; @@ -3602,35 +3651,24 @@ static void init_multi_out(struct hda_codec *codec) for (i = 0; i < spec->autocfg.line_outs; i++) { nid = spec->autocfg.line_out_pins[i]; - if (nid) { - dac = spec->multiout.dac_nids[i]; - if (!dac) - dac = spec->multiout.dac_nids[0]; - set_output_and_unmute(codec, nid, pin_type, dac); - } + if (nid) + set_output_and_unmute(codec, nid, pin_type, + spec->out_paths[i]); } } static void __init_extra_out(struct hda_codec *codec, int num_outs, - hda_nid_t *pins, hda_nid_t *dacs, int type) + hda_nid_t *pins, int *paths, int type) { - struct hda_gen_spec *spec = codec->spec; int i; - hda_nid_t pin, dac; + hda_nid_t pin; for (i = 0; i < num_outs; i++) { pin = pins[i]; if (!pin) break; - dac = dacs[i]; - if (!dac) { - if (i > 0 && dacs[0]) - dac = dacs[0]; - else - dac = spec->multiout.dac_nids[0]; - } - set_output_and_unmute(codec, pin, type, dac); + set_output_and_unmute(codec, pin, type, paths[i]); } } @@ -3642,11 +3680,11 @@ static void init_extra_out(struct hda_codec *codec) if (spec->autocfg.line_out_type != AUTO_PIN_HP_OUT) __init_extra_out(codec, spec->autocfg.hp_outs, spec->autocfg.hp_pins, - spec->multiout.hp_out_nid, PIN_HP); + spec->hp_paths, PIN_HP); if (spec->autocfg.line_out_type != AUTO_PIN_SPEAKER_OUT) __init_extra_out(codec, spec->autocfg.speaker_outs, spec->autocfg.speaker_pins, - spec->multiout.extra_out_nid, PIN_OUT); + spec->speaker_paths, PIN_OUT); } /* initialize multi-io paths */ @@ -3658,7 +3696,7 @@ static void init_multi_io(struct hda_codec *codec) for (i = 0; i < spec->multi_ios; i++) { hda_nid_t pin = spec->multi_io[i].pin; struct nid_path *path; - path = snd_hda_get_nid_path(codec, spec->multi_io[i].dac, pin); + path = get_multiio_path(codec, i); if (!path) continue; if (!spec->multi_io[i].ctl_in) @@ -3694,7 +3732,7 @@ static void init_analog_input(struct hda_codec *codec) /* init loopback inputs */ if (spec->mixer_nid) { struct nid_path *path; - path = snd_hda_get_nid_path(codec, nid, spec->mixer_nid); + path = snd_hda_get_path_from_idx(codec, spec->loopback_paths[i]); if (path) snd_hda_activate_path(codec, path, path->active, false); @@ -3746,7 +3784,8 @@ static void init_digital(struct hda_codec *codec) pin = spec->autocfg.dig_out_pins[i]; if (!pin) continue; - set_output_and_unmute(codec, pin, PIN_OUT, 0); + set_output_and_unmute(codec, pin, PIN_OUT, + spec->digout_paths[i]); } pin = spec->autocfg.dig_in_pin; if (pin) diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h index f1cae2e49377..71d409f5de87 100644 --- a/sound/pci/hda/hda_generic.h +++ b/sound/pci/hda/hda_generic.h @@ -130,6 +130,13 @@ struct hda_gen_spec { /* path list */ struct snd_array paths; + /* path indices */ + int out_paths[AUTO_CFG_MAX_OUTS]; + int hp_paths[AUTO_CFG_MAX_OUTS]; + int speaker_paths[AUTO_CFG_MAX_OUTS]; + int digout_paths[AUTO_CFG_MAX_OUTS]; + int loopback_paths[HDA_MAX_NUM_INPUTS]; + /* auto-mic stuff */ int am_num_entries; struct automic_entry am_entry[MAX_AUTO_MIC_PINS]; @@ -198,6 +205,8 @@ int snd_hda_gen_init(struct hda_codec *codec); struct nid_path *snd_hda_get_nid_path(struct hda_codec *codec, hda_nid_t from_nid, hda_nid_t to_nid); +int snd_hda_get_path_idx(struct hda_codec *codec, struct nid_path *path); +struct nid_path *snd_hda_get_path_from_idx(struct hda_codec *codec, int idx); enum { HDA_PARSE_NO_AAMIX, -- cgit v1.2.3 From 2430d7b78ba161656a621279964421aa06e04b02 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 4 Jan 2013 15:09:42 +0100 Subject: ALSA: hda - Initialize digital-input path properly Call the path activation for the digital input pin properly, not only setting the pin control. Also add spec->digin_path for keeping the path index. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 8 +++++++- sound/pci/hda/hda_generic.h | 1 + 2 files changed, 8 insertions(+), 1 deletion(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index c8bf81230206..4e49c5e27704 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -2521,6 +2521,7 @@ static void parse_digital(struct hda_codec *codec) print_nid_path("digin", path); path->active = true; spec->dig_in_nid = dig_nid; + spec->digin_path = snd_hda_get_path_idx(codec, path); break; } } @@ -3788,8 +3789,13 @@ static void init_digital(struct hda_codec *codec) spec->digout_paths[i]); } pin = spec->autocfg.dig_in_pin; - if (pin) + if (pin) { + struct nid_path *path; snd_hda_set_pin_ctl_cache(codec, pin, PIN_IN); + path = snd_hda_get_path_from_idx(codec, spec->digin_path); + if (path) + snd_hda_activate_path(codec, path, path->active, false); + } } /* clear unsol-event tags on unused pins; Conexant codecs seem to leave diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h index 71d409f5de87..ba8de12b7125 100644 --- a/sound/pci/hda/hda_generic.h +++ b/sound/pci/hda/hda_generic.h @@ -136,6 +136,7 @@ struct hda_gen_spec { int speaker_paths[AUTO_CFG_MAX_OUTS]; int digout_paths[AUTO_CFG_MAX_OUTS]; int loopback_paths[HDA_MAX_NUM_INPUTS]; + int digin_path; /* auto-mic stuff */ int am_num_entries; -- cgit v1.2.3 From 117688a9c1023af9241810544b35c7104fbbcfc2 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 4 Jan 2013 15:41:41 +0100 Subject: ALSA: hda - Correct aamix output paths The output paths including aamix should be parsed only for the first output. The surround paths including aamix must be wrong, since it would mix all streams, i.e. all channels would be mixed into a single and multiplexed again. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 4e49c5e27704..e7574a863d21 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -918,7 +918,7 @@ static int try_assign_dacs(struct hda_codec *codec, int num_outs, badness += bad->no_dac; } path = snd_hda_add_new_path(codec, dac, pin, HDA_PARSE_NO_AAMIX); - if (!path && i > 0 && spec->mixer_nid) { + if (!path && !i && spec->mixer_nid) { /* try with aamix */ path = snd_hda_add_new_path(codec, dac, pin, HDA_PARSE_ALL); } @@ -1102,7 +1102,7 @@ static bool map_singles(struct hda_codec *codec, int outs, if (!dac) continue; path = snd_hda_add_new_path(codec, dac, pins[i], HDA_PARSE_NO_AAMIX); - if (!path && i > 0 && spec->mixer_nid) + if (!path && !i && spec->mixer_nid) path = snd_hda_add_new_path(codec, dac, pins[i], HDA_PARSE_ALL); if (path) { dacs[i] = dac; -- cgit v1.2.3 From c30aa7b24282c6c544f25f360131fceb646927e4 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 4 Jan 2013 16:42:48 +0100 Subject: ALSA: hda - Add Loopback Mixing control For codecs that have individual routes going through a loopback mixer (like VIA codecs), we need to provide an explicit switch to choose whether the output goes through mixer or directly from DAC. This patch adds the check for such paths and creates "Loopback Mixing" enum control when available. It won't influence on codecs like Realtek or others where the loopback mixer is connected independently from the primary output routes. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 106 ++++++++++++++++++++++++++++++++++++++++++++ sound/pci/hda/hda_generic.h | 4 ++ 2 files changed, 110 insertions(+) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index e7574a863d21..a34c581b6082 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -1115,6 +1115,24 @@ static bool map_singles(struct hda_codec *codec, int outs, return found; } +/* create a new path including aamix if available, and return its index */ +static int check_aamix_out_path(struct hda_codec *codec, int path_idx) +{ + struct nid_path *path; + + path = snd_hda_get_path_from_idx(codec, path_idx); + if (!path || !path->depth || path->with_aa_mix) + return 0; + path = snd_hda_add_new_path(codec, path->path[0], + path->path[path->depth - 1], + HDA_PARSE_ONLY_AAMIX); + if (!path) + return 0; + print_nid_path("output-aamix", path); + path->active = false; /* unused as default */ + return snd_hda_get_path_idx(codec, path); +} + /* fill in the dac_nids table from the parsed pin configuration */ static int fill_and_eval_dacs(struct hda_codec *codec, bool fill_hardwired, @@ -1211,6 +1229,17 @@ static int fill_and_eval_dacs(struct hda_codec *codec, badness += err; } + if (spec->mixer_nid) { + spec->aamix_out_paths[0] = + check_aamix_out_path(codec, spec->out_paths[0]); + if (cfg->line_out_type != AUTO_PIN_HP_OUT) + spec->aamix_out_paths[1] = + check_aamix_out_path(codec, spec->hp_paths[0]); + if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) + spec->aamix_out_paths[2] = + check_aamix_out_path(codec, spec->speaker_paths[0]); + } + if (cfg->hp_outs && cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) if (count_multiio_pins(codec, cfg->hp_pins[0]) >= 2) spec->multi_ios = 1; /* give badness */ @@ -1729,6 +1758,80 @@ static int create_multi_channel_mode(struct hda_codec *codec) return 0; } +/* + * aamix loopback enable/disable switch + */ + +#define loopback_mixing_info indep_hp_info + +static int loopback_mixing_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct hda_gen_spec *spec = codec->spec; + ucontrol->value.enumerated.item[0] = spec->aamix_mode; + return 0; +} + +static void update_aamix_paths(struct hda_codec *codec, bool do_mix, + int nomix_path_idx, int mix_path_idx) +{ + struct nid_path *nomix_path, *mix_path; + + nomix_path = snd_hda_get_path_from_idx(codec, nomix_path_idx); + mix_path = snd_hda_get_path_from_idx(codec, mix_path_idx); + if (!nomix_path || !mix_path) + return; + if (do_mix) { + snd_hda_activate_path(codec, nomix_path, false, true); + snd_hda_activate_path(codec, mix_path, true, true); + } else { + snd_hda_activate_path(codec, mix_path, false, true); + snd_hda_activate_path(codec, nomix_path, true, true); + } +} + +static int loopback_mixing_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct hda_gen_spec *spec = codec->spec; + unsigned int val = ucontrol->value.enumerated.item[0]; + + if (val == spec->aamix_mode) + return 0; + spec->aamix_mode = val; + update_aamix_paths(codec, val, spec->out_paths[0], + spec->aamix_out_paths[0]); + update_aamix_paths(codec, val, spec->hp_paths[0], + spec->aamix_out_paths[1]); + update_aamix_paths(codec, val, spec->speaker_paths[0], + spec->aamix_out_paths[2]); + return 1; +} + +static const struct snd_kcontrol_new loopback_mixing_enum = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Loopback Mixing", + .info = loopback_mixing_info, + .get = loopback_mixing_get, + .put = loopback_mixing_put, +}; + +static int create_loopback_mixing_ctl(struct hda_codec *codec) +{ + struct hda_gen_spec *spec = codec->spec; + + if (!spec->mixer_nid) + return 0; + if (!(spec->aamix_out_paths[0] || spec->aamix_out_paths[1] || + spec->aamix_out_paths[2])) + return 0; + if (!snd_hda_gen_add_kctl(spec, NULL, &loopback_mixing_enum)) + return -ENOMEM; + return 0; +} + /* * shared headphone/mic handling */ @@ -3065,6 +3168,9 @@ int snd_hda_gen_parse_auto_config(struct hda_codec *codec, if (err < 0) return err; err = create_indep_hp_ctls(codec); + if (err < 0) + return err; + err = create_loopback_mixing_ctl(codec); if (err < 0) return err; err = create_shared_input(codec); diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h index ba8de12b7125..d4a8f6c4e7a9 100644 --- a/sound/pci/hda/hda_generic.h +++ b/sound/pci/hda/hda_generic.h @@ -134,6 +134,7 @@ struct hda_gen_spec { int out_paths[AUTO_CFG_MAX_OUTS]; int hp_paths[AUTO_CFG_MAX_OUTS]; int speaker_paths[AUTO_CFG_MAX_OUTS]; + int aamix_out_paths[3]; int digout_paths[AUTO_CFG_MAX_OUTS]; int loopback_paths[HDA_MAX_NUM_INPUTS]; int digin_path; @@ -169,6 +170,9 @@ struct hda_gen_spec { unsigned int indep_hp:1; /* independent HP supported */ unsigned int indep_hp_enabled:1; /* independent HP enabled */ + /* loopback mixing mode */ + bool aamix_mode; + /* for virtual master */ hda_nid_t vmaster_nid; struct hda_vmaster_mute_hook vmaster_mute; -- cgit v1.2.3 From 5abd4888f662cc72c8a3039a6124256691f758d0 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 7 Jan 2013 09:43:18 +0100 Subject: ALSA: hda - Fix truncated control names ... like "Speaker Surround Playback Switch". This fix had been already applied to patch_conexant.c but was forgotten in other places, then migrated to hda_generic.c. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index a34c581b6082..0588dd6002f4 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -1497,7 +1497,7 @@ static int create_extra_outs(struct hda_codec *codec, int num_pins, { struct hda_gen_spec *spec = codec->spec; struct hda_bind_ctls *ctl; - char name[32]; + char name[44]; int i, n, err; if (!num_pins || !pins[0]) @@ -2545,7 +2545,7 @@ static int parse_mic_boost(struct hda_codec *codec) nid = cfg->inputs[i].pin; if (get_wcaps(codec, nid) & AC_WCAP_IN_AMP) { const char *label; - char boost_label[32]; + char boost_label[44]; struct nid_path *path; unsigned int val; -- cgit v1.2.3 From 7385df6134888553b5ede71cd573ffe0429e2a80 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 7 Jan 2013 09:50:52 +0100 Subject: ALSA: hda - Prefer binding the primary CLFE output When 5.1 or more multiple speakers with found but not enough DACs are available, it's better to bind such pins to the DACs of the primary outputs with the same channels rather than binding to the first DAC (i.e. the front channel). For the cases with two speaker pins, it's rather regarded as front + bass combination, thus it's more practical to still bind to the front, though. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 33 +++++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 6 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 0588dd6002f4..f4fa60481978 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -860,6 +860,27 @@ static struct badness_table extra_out_badness = { .shared_surr_main = BAD_NO_EXTRA_SURR_DAC, }; +/* get the DAC of the primary output corresponding to the given array index */ +static hda_nid_t get_primary_out(struct hda_codec *codec, int idx) +{ + struct hda_gen_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + + if (cfg->line_outs > idx) + return spec->private_dac_nids[idx]; + idx -= cfg->line_outs; + if (spec->multi_ios > idx) + return spec->multi_io[idx].dac; + return 0; +} + +/* return the DAC if it's reachable, otherwise zero */ +static inline hda_nid_t try_dac(struct hda_codec *codec, + hda_nid_t dac, hda_nid_t pin) +{ + return is_reachable_path(codec, dac, pin) ? dac : 0; +} + /* try to assign DACs to pins and return the resultant badness */ static int try_assign_dacs(struct hda_codec *codec, int num_outs, const hda_nid_t *pins, hda_nid_t *dacs, @@ -867,7 +888,6 @@ static int try_assign_dacs(struct hda_codec *codec, int num_outs, const struct badness_table *bad) { struct hda_gen_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; int i, j; int badness = 0; hda_nid_t dac; @@ -897,11 +917,12 @@ static int try_assign_dacs(struct hda_codec *codec, int num_outs, } dac = dacs[i]; if (!dac) { - if (is_reachable_path(codec, dacs[0], pin)) - dac = dacs[0]; - else if (cfg->line_outs > i && - is_reachable_path(codec, spec->private_dac_nids[i], pin)) - dac = spec->private_dac_nids[i]; + if (num_outs > 2) + dac = try_dac(codec, get_primary_out(codec, i), pin); + if (!dac) + dac = try_dac(codec, dacs[0], pin); + if (!dac) + dac = try_dac(codec, get_primary_out(codec, i), pin); if (dac) { if (!i) badness += bad->shared_primary; -- cgit v1.2.3 From ee79c69ac726269591e2855d5a8d2be02920678d Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 7 Jan 2013 09:57:42 +0100 Subject: ALSA: hda - Add missing slave names for Speaker Surround, etc Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index f4fa60481978..1a1e8e228d7e 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -3252,6 +3252,9 @@ static const char * const slave_pfxs[] = { "Front", "Surround", "Center", "LFE", "Side", "Headphone", "Speaker", "Mono", "Line Out", "CLFE", "Bass Speaker", "PCM", + "Speaker Front", "Speaker Surround", "Speaker CLFE", "Speaker Side", + "Headphone Front", "Headphone Surround", "Headphone CLFE", + "Headphone Side", NULL, }; -- cgit v1.2.3 From d4156930b21e9317a560c9e1fc1d544935a9f6f3 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 7 Jan 2013 10:08:02 +0100 Subject: ALSA: hda - Drop unneeded pin argument from set_output_and_unmute() Just a minor refactoring. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 41 +++++++++++++---------------------------- 1 file changed, 13 insertions(+), 28 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 1a1e8e228d7e..bebc3f4a47ba 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -3753,16 +3753,18 @@ EXPORT_SYMBOL_HDA(snd_hda_gen_build_pcms); * Standard auto-parser initializations */ -/* configure the path from the given dac to the pin as the proper output */ -static void set_output_and_unmute(struct hda_codec *codec, hda_nid_t pin, +/* configure the given path as a proper output */ +static void set_output_and_unmute(struct hda_codec *codec, int pin_type, int path_idx) { struct nid_path *path; + hda_nid_t pin; - snd_hda_set_pin_ctl_cache(codec, pin, pin_type); path = snd_hda_get_path_from_idx(codec, path_idx); - if (!path) + if (!path || !path->depth) return; + pin = path->path[path->depth - 1]; + snd_hda_set_pin_ctl_cache(codec, pin, pin_type); snd_hda_activate_path(codec, path, path->active, true); set_pin_eapd(codec, pin, path->active); } @@ -3771,7 +3773,6 @@ static void set_output_and_unmute(struct hda_codec *codec, hda_nid_t pin, static void init_multi_out(struct hda_codec *codec) { struct hda_gen_spec *spec = codec->spec; - hda_nid_t nid; int pin_type; int i; @@ -3780,27 +3781,18 @@ static void init_multi_out(struct hda_codec *codec) else pin_type = PIN_OUT; - for (i = 0; i < spec->autocfg.line_outs; i++) { - nid = spec->autocfg.line_out_pins[i]; - if (nid) - set_output_and_unmute(codec, nid, pin_type, - spec->out_paths[i]); - } + for (i = 0; i < spec->autocfg.line_outs; i++) + set_output_and_unmute(codec, pin_type, spec->out_paths[i]); } static void __init_extra_out(struct hda_codec *codec, int num_outs, - hda_nid_t *pins, int *paths, int type) + int *paths, int type) { int i; - hda_nid_t pin; - for (i = 0; i < num_outs; i++) { - pin = pins[i]; - if (!pin) - break; - set_output_and_unmute(codec, pin, type, paths[i]); - } + for (i = 0; i < num_outs; i++) + set_output_and_unmute(codec, type, paths[i]); } /* initialize hp and speaker paths */ @@ -3810,11 +3802,9 @@ static void init_extra_out(struct hda_codec *codec) if (spec->autocfg.line_out_type != AUTO_PIN_HP_OUT) __init_extra_out(codec, spec->autocfg.hp_outs, - spec->autocfg.hp_pins, spec->hp_paths, PIN_HP); if (spec->autocfg.line_out_type != AUTO_PIN_SPEAKER_OUT) __init_extra_out(codec, spec->autocfg.speaker_outs, - spec->autocfg.speaker_pins, spec->speaker_paths, PIN_OUT); } @@ -3911,13 +3901,8 @@ static void init_digital(struct hda_codec *codec) int i; hda_nid_t pin; - for (i = 0; i < spec->autocfg.dig_outs; i++) { - pin = spec->autocfg.dig_out_pins[i]; - if (!pin) - continue; - set_output_and_unmute(codec, pin, PIN_OUT, - spec->digout_paths[i]); - } + for (i = 0; i < spec->autocfg.dig_outs; i++) + set_output_and_unmute(codec, PIN_OUT, spec->digout_paths[i]); pin = spec->autocfg.dig_in_pin; if (pin) { struct nid_path *path; -- cgit v1.2.3 From c2c803830a5d9897344cd3ffd82daddd7f9f3864 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 7 Jan 2013 10:33:57 +0100 Subject: ALSA: hda - Drop bind-volume workaround The bind-volume workaround was introduced for simplifying the mixer abstraction in the case where one or more pins of multiple outputs lack of individual volume controls. This was essentially the case like Acer Aspire 5935, which has 5.1 speakers and 5.1 (multi-io) jacks although there are 5 DACs, so some of them must share a DAC. However, the recent code rewrite changed the DAC assignment policy to share with the same channel instead of binding to the front, thus binding the volumes for all channels makes little sense now, rather it's confusing. So in this patch, the ugly workaround is finally dropped and simply create the volume control corresponding to the parsed path position. For dual headphones or 2.1 speakers with a shared volume control, it's anyway bound to the same DAC if needed, so this change shouldn't bring any practical difference. And, as a good bonus, we can cut off the whole code handling the bind volume elements. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 114 +++++++------------------------------------- sound/pci/hda/hda_generic.h | 3 -- 2 files changed, 17 insertions(+), 100 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index bebc3f4a47ba..296628b6ffc2 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -39,7 +39,6 @@ int snd_hda_gen_spec_init(struct hda_gen_spec *spec) { snd_array_init(&spec->kctls, sizeof(struct snd_kcontrol_new), 32); - snd_array_init(&spec->bind_ctls, sizeof(struct hda_bind_ctls *), 8); snd_array_init(&spec->paths, sizeof(struct nid_path), 8); mutex_init(&spec->pcm_mutex); return 0; @@ -75,39 +74,11 @@ static void free_kctls(struct hda_gen_spec *spec) snd_array_free(&spec->kctls); } -static struct hda_bind_ctls *new_bind_ctl(struct hda_codec *codec, - unsigned int nums, - struct hda_ctl_ops *ops) -{ - struct hda_gen_spec *spec = codec->spec; - struct hda_bind_ctls **ctlp, *ctl; - ctlp = snd_array_new(&spec->bind_ctls); - if (!ctlp) - return NULL; - ctl = kzalloc(sizeof(*ctl) + sizeof(long) * (nums + 1), GFP_KERNEL); - *ctlp = ctl; - if (ctl) - ctl->ops = ops; - return ctl; -} - -static void free_bind_ctls(struct hda_gen_spec *spec) -{ - if (spec->bind_ctls.list) { - struct hda_bind_ctls **ctl = spec->bind_ctls.list; - int i; - for (i = 0; i < spec->bind_ctls.used; i++) - kfree(ctl[i]); - } - snd_array_free(&spec->bind_ctls); -} - void snd_hda_gen_spec_free(struct hda_gen_spec *spec) { if (!spec) return; free_kctls(spec); - free_bind_ctls(spec); snd_array_free(&spec->paths); } EXPORT_SYMBOL_HDA(snd_hda_gen_spec_free); @@ -1489,8 +1460,7 @@ static int create_multi_out_ctls(struct hda_codec *codec, return 0; } -static int create_extra_out(struct hda_codec *codec, hda_nid_t pin, - hda_nid_t dac, int path_idx, +static int create_extra_out(struct hda_codec *codec, int path_idx, const char *pfx, int cidx) { struct nid_path *path; @@ -1499,12 +1469,9 @@ static int create_extra_out(struct hda_codec *codec, hda_nid_t pin, path = snd_hda_get_path_from_idx(codec, path_idx); if (!path) return 0; - /* bind volume control will be created in the case of dac = 0 */ - if (dac) { - err = add_stereo_vol(codec, pfx, cidx, path); - if (err < 0) - return err; - } + err = add_stereo_vol(codec, pfx, cidx, path); + if (err < 0) + return err; err = add_stereo_sw(codec, pfx, cidx, path); if (err < 0) return err; @@ -1513,69 +1480,26 @@ static int create_extra_out(struct hda_codec *codec, hda_nid_t pin, /* add playback controls for speaker and HP outputs */ static int create_extra_outs(struct hda_codec *codec, int num_pins, - const hda_nid_t *pins, const hda_nid_t *dacs, const int *paths, const char *pfx) { - struct hda_gen_spec *spec = codec->spec; - struct hda_bind_ctls *ctl; - char name[44]; - int i, n, err; - - if (!num_pins || !pins[0]) - return 0; - - if (num_pins == 1) { - hda_nid_t dac = *dacs; - if (!dac) - dac = spec->multiout.dac_nids[0]; - return create_extra_out(codec, *pins, dac, paths[0], pfx, 0); - } + int i; for (i = 0; i < num_pins; i++) { - hda_nid_t dac; - if (dacs[num_pins - 1]) - dac = dacs[i]; /* with individual volumes */ - else - dac = 0; - if (num_pins == 2 && i == 1 && !strcmp(pfx, "Speaker")) { - err = create_extra_out(codec, pins[i], dac, paths[i], - "Bass Speaker", 0); - } else if (num_pins >= 3) { - snprintf(name, sizeof(name), "%s %s", + const char *name; + char tmp[44]; + int err, idx = 0; + + if (num_pins == 2 && i == 1 && !strcmp(pfx, "Speaker")) + name = "Bass Speaker"; + else if (num_pins >= 3) { + snprintf(tmp, sizeof(tmp), "%s %s", pfx, channel_name[i]); - err = create_extra_out(codec, pins[i], dac, paths[i], - name, 0); + name = tmp; } else { - err = create_extra_out(codec, pins[i], dac, paths[i], - pfx, i); + name = pfx; + idx = i; } - if (err < 0) - return err; - } - if (dacs[num_pins - 1]) - return 0; - - /* Let's create a bind-controls for volumes */ - ctl = new_bind_ctl(codec, num_pins, &snd_hda_bind_vol); - if (!ctl) - return -ENOMEM; - n = 0; - for (i = 0; i < num_pins; i++) { - hda_nid_t vol; - struct nid_path *path; - if (!pins[i] || !dacs[i]) - continue; - path = snd_hda_get_path_from_idx(codec, paths[i]); - if (!path) - continue; - vol = look_for_out_vol_nid(codec, path); - if (vol) - ctl->values[n++] = - HDA_COMPOSE_AMP_VAL(vol, 3, 0, HDA_OUTPUT); - } - if (n) { - snprintf(name, sizeof(name), "%s Playback Volume", pfx); - err = add_control(spec, HDA_CTL_BIND_VOL, name, 0, (long)ctl); + err = create_extra_out(codec, paths[i], name, idx); if (err < 0) return err; } @@ -1586,8 +1510,6 @@ static int create_hp_out_ctls(struct hda_codec *codec) { struct hda_gen_spec *spec = codec->spec; return create_extra_outs(codec, spec->autocfg.hp_outs, - spec->autocfg.hp_pins, - spec->multiout.hp_out_nid, spec->hp_paths, "Headphone"); } @@ -1596,8 +1518,6 @@ static int create_speaker_out_ctls(struct hda_codec *codec) { struct hda_gen_spec *spec = codec->spec; return create_extra_outs(codec, spec->autocfg.speaker_outs, - spec->autocfg.speaker_pins, - spec->multiout.extra_out_nid, spec->speaker_paths, "Speaker"); } diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h index d4a8f6c4e7a9..4c0d9add6145 100644 --- a/sound/pci/hda/hda_generic.h +++ b/sound/pci/hda/hda_generic.h @@ -186,9 +186,6 @@ struct hda_gen_spec { int multi_ios; struct hda_multi_io multi_io[4]; - /* bind volumes */ - struct snd_array bind_ctls; - /* hooks */ void (*init_hook)(struct hda_codec *codec); void (*automute_hook)(struct hda_codec *codec); -- cgit v1.2.3 From e6b85f3c9d5ea3807dee651c28d5b0d5982ae2fa Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 7 Jan 2013 11:54:34 +0100 Subject: ALSA: hda - Add pcm_playback_hook to hda_gen_spec The new hook which is called at each PCM playback ops. It can be used to control the codec-specific power-saving feature in each codec driver. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 65 +++++++++++++++++++++++++++++++++++++++++---- sound/pci/hda/hda_generic.h | 14 ++++++++++ 2 files changed, 74 insertions(+), 5 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 296628b6ffc2..28ad5e98b341 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -3261,6 +3261,16 @@ EXPORT_SYMBOL_HDA(snd_hda_gen_build_controls); * PCM definitions */ +static void call_pcm_playback_hook(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream, + int action) +{ + struct hda_gen_spec *spec = codec->spec; + if (spec->pcm_playback_hook) + spec->pcm_playback_hook(hinfo, codec, substream, action); +} + /* * Analog playback callbacks */ @@ -3275,8 +3285,11 @@ static int playback_pcm_open(struct hda_pcm_stream *hinfo, err = snd_hda_multi_out_analog_open(codec, &spec->multiout, substream, hinfo); - if (!err) + if (!err) { spec->active_streams |= 1 << STREAM_MULTI_OUT; + call_pcm_playback_hook(hinfo, codec, substream, + HDA_GEN_PCM_ACT_OPEN); + } mutex_unlock(&spec->pcm_mutex); return err; } @@ -3288,8 +3301,14 @@ static int playback_pcm_prepare(struct hda_pcm_stream *hinfo, struct snd_pcm_substream *substream) { struct hda_gen_spec *spec = codec->spec; - return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, - stream_tag, format, substream); + int err; + + err = snd_hda_multi_out_analog_prepare(codec, &spec->multiout, + stream_tag, format, substream); + if (!err) + call_pcm_playback_hook(hinfo, codec, substream, + HDA_GEN_PCM_ACT_PREPARE); + return err; } static int playback_pcm_cleanup(struct hda_pcm_stream *hinfo, @@ -3297,7 +3316,13 @@ static int playback_pcm_cleanup(struct hda_pcm_stream *hinfo, struct snd_pcm_substream *substream) { struct hda_gen_spec *spec = codec->spec; - return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout); + int err; + + err = snd_hda_multi_out_analog_cleanup(codec, &spec->multiout); + if (!err) + call_pcm_playback_hook(hinfo, codec, substream, + HDA_GEN_PCM_ACT_CLEANUP); + return err; } static int playback_pcm_close(struct hda_pcm_stream *hinfo, @@ -3307,6 +3332,8 @@ static int playback_pcm_close(struct hda_pcm_stream *hinfo, struct hda_gen_spec *spec = codec->spec; mutex_lock(&spec->pcm_mutex); spec->active_streams &= ~(1 << STREAM_MULTI_OUT); + call_pcm_playback_hook(hinfo, codec, substream, + HDA_GEN_PCM_ACT_CLOSE); mutex_unlock(&spec->pcm_mutex); return 0; } @@ -3323,6 +3350,8 @@ static int alt_playback_pcm_open(struct hda_pcm_stream *hinfo, err = -EBUSY; else spec->active_streams |= 1 << STREAM_INDEP_HP; + call_pcm_playback_hook(hinfo, codec, substream, + HDA_GEN_PCM_ACT_OPEN); mutex_unlock(&spec->pcm_mutex); return err; } @@ -3334,10 +3363,34 @@ static int alt_playback_pcm_close(struct hda_pcm_stream *hinfo, struct hda_gen_spec *spec = codec->spec; mutex_lock(&spec->pcm_mutex); spec->active_streams &= ~(1 << STREAM_INDEP_HP); + call_pcm_playback_hook(hinfo, codec, substream, + HDA_GEN_PCM_ACT_CLOSE); mutex_unlock(&spec->pcm_mutex); return 0; } +static int alt_playback_pcm_prepare(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + unsigned int stream_tag, + unsigned int format, + struct snd_pcm_substream *substream) +{ + snd_hda_codec_setup_stream(codec, hinfo->nid, stream_tag, 0, format); + call_pcm_playback_hook(hinfo, codec, substream, + HDA_GEN_PCM_ACT_PREPARE); + return 0; +} + +static int alt_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + snd_hda_codec_cleanup_stream(codec, hinfo->nid); + call_pcm_playback_hook(hinfo, codec, substream, + HDA_GEN_PCM_ACT_CLEANUP); + return 0; +} + /* * Digital out */ @@ -3432,7 +3485,9 @@ static const struct hda_pcm_stream pcm_analog_alt_playback = { /* NID is set in build_pcms */ .ops = { .open = alt_playback_pcm_open, - .close = alt_playback_pcm_close + .close = alt_playback_pcm_close, + .prepare = alt_playback_pcm_prepare, + .cleanup = alt_playback_pcm_cleanup }, }; diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h index 4c0d9add6145..7e84c22f98b8 100644 --- a/sound/pci/hda/hda_generic.h +++ b/sound/pci/hda/hda_generic.h @@ -69,6 +69,14 @@ struct automic_entry { /* active stream id */ enum { STREAM_MULTI_OUT, STREAM_INDEP_HP }; +/* PCM hook action */ +enum { + HDA_GEN_PCM_ACT_OPEN, + HDA_GEN_PCM_ACT_PREPARE, + HDA_GEN_PCM_ACT_CLEANUP, + HDA_GEN_PCM_ACT_CLOSE, +}; + struct hda_gen_spec { char stream_name_analog[32]; /* analog PCM stream */ const struct hda_pcm_stream *stream_analog_playback; @@ -191,6 +199,12 @@ struct hda_gen_spec { void (*automute_hook)(struct hda_codec *codec); void (*cap_sync_hook)(struct hda_codec *codec); + /* PCM playback hook */ + void (*pcm_playback_hook)(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream, + int action); + /* automute / autoswitch hooks */ void (*hp_automute_hook)(struct hda_codec *codec, struct hda_jack_tbl *tbl); -- cgit v1.2.3 From 76a19c69d9c971d652e263799536412ec7f8dcf3 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 7 Jan 2013 12:11:33 +0100 Subject: ALSA: hda - Allow jack detection when polling is enabled Let is_jack_detectable() return true when the jack polling is enabled for the codec. VT1708 uses the polling explicitly so we need to allow it. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_jack.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_jack.c b/sound/pci/hda/hda_jack.c index 6479b6501828..1d035efeff4f 100644 --- a/sound/pci/hda/hda_jack.c +++ b/sound/pci/hda/hda_jack.c @@ -29,7 +29,8 @@ bool is_jack_detectable(struct hda_codec *codec, hda_nid_t nid) if (get_defcfg_misc(snd_hda_codec_get_pincfg(codec, nid)) & AC_DEFCFG_MISC_NO_PRESENCE) return false; - if (!(get_wcaps(codec, nid) & AC_WCAP_UNSOL_CAP)) + if (!(get_wcaps(codec, nid) & AC_WCAP_UNSOL_CAP) && + !codec->jackpoll_interval) return false; return true; } -- cgit v1.2.3 From fce52a3bb15bf3db63cbde496f212edf5e6d366e Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 7 Jan 2013 12:42:48 +0100 Subject: ALSA: hda - Add snd_hda_gen_free() and snd_hda_gen_check_power_status() Just to remove duplicated codes. Also fixed EXPORT_SYMBOL() in hda_generic.c. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 33 ++++++++++++++++++--------------- sound/pci/hda/hda_generic.h | 5 +++++ sound/pci/hda/patch_realtek.c | 12 +----------- 3 files changed, 24 insertions(+), 26 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 28ad5e98b341..1fb31975ee38 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -3939,36 +3939,39 @@ int snd_hda_gen_init(struct hda_codec *codec) hda_call_check_power_status(codec, 0x01); return 0; } -EXPORT_SYMBOL(snd_hda_gen_init); +EXPORT_SYMBOL_HDA(snd_hda_gen_init); -/* - * the generic codec support - */ +void snd_hda_gen_free(struct hda_codec *codec) +{ + snd_hda_gen_spec_free(codec->spec); + kfree(codec->spec); + codec->spec = NULL; +} +EXPORT_SYMBOL_HDA(snd_hda_gen_free); #ifdef CONFIG_PM -static int generic_check_power_status(struct hda_codec *codec, hda_nid_t nid) +int snd_hda_gen_check_power_status(struct hda_codec *codec, hda_nid_t nid) { struct hda_gen_spec *spec = codec->spec; return snd_hda_check_amp_list_power(codec, &spec->loopback, nid); } +EXPORT_SYMBOL_HDA(snd_hda_gen_check_power_status); #endif -static void generic_free(struct hda_codec *codec) -{ - snd_hda_gen_spec_free(codec->spec); - kfree(codec->spec); - codec->spec = NULL; -} + +/* + * the generic codec support + */ static const struct hda_codec_ops generic_patch_ops = { .build_controls = snd_hda_gen_build_controls, .build_pcms = snd_hda_gen_build_pcms, .init = snd_hda_gen_init, - .free = generic_free, + .free = snd_hda_gen_free, .unsol_event = snd_hda_jack_unsol_event, #ifdef CONFIG_PM - .check_power_status = generic_check_power_status, + .check_power_status = snd_hda_gen_check_power_status, #endif }; @@ -3995,7 +3998,7 @@ int snd_hda_parse_generic_codec(struct hda_codec *codec) return 0; error: - generic_free(codec); + snd_hda_gen_free(codec); return err; } -EXPORT_SYMBOL(snd_hda_parse_generic_codec); +EXPORT_SYMBOL_HDA(snd_hda_parse_generic_codec); diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h index 7e84c22f98b8..00a1eab2c0b2 100644 --- a/sound/pci/hda/hda_generic.h +++ b/sound/pci/hda/hda_generic.h @@ -218,6 +218,7 @@ int snd_hda_gen_spec_init(struct hda_gen_spec *spec); void snd_hda_gen_spec_free(struct hda_gen_spec *spec); int snd_hda_gen_init(struct hda_codec *codec); +void snd_hda_gen_free(struct hda_codec *codec); struct nid_path *snd_hda_get_nid_path(struct hda_codec *codec, hda_nid_t from_nid, hda_nid_t to_nid); @@ -257,4 +258,8 @@ void snd_hda_gen_mic_autoswitch(struct hda_codec *codec, struct hda_jack_tbl *jack); void snd_hda_gen_update_outputs(struct hda_codec *codec); +#ifdef CONFIG_PM +int snd_hda_gen_check_power_status(struct hda_codec *codec, hda_nid_t nid); +#endif + #endif /* __SOUND_HDA_GENERIC_H */ diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index fee21625e509..bcb258bca99d 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -808,14 +808,6 @@ static int alc_init(struct hda_codec *codec) return 0; } -#ifdef CONFIG_PM -static int alc_check_power_status(struct hda_codec *codec, hda_nid_t nid) -{ - struct alc_spec *spec = codec->spec; - return snd_hda_check_amp_list_power(codec, &spec->gen.loopback, nid); -} -#endif - static inline void alc_shutup(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; @@ -876,10 +868,8 @@ static const struct hda_codec_ops alc_patch_ops = { .unsol_event = snd_hda_jack_unsol_event, #ifdef CONFIG_PM .resume = alc_resume, -#endif -#ifdef CONFIG_PM .suspend = alc_suspend, - .check_power_status = alc_check_power_status, + .check_power_status = snd_hda_gen_check_power_status, #endif .reboot_notify = alc_shutup, }; -- cgit v1.2.3 From dd5e72030429edfcdee7c0fcdce702ecc81a739e Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 7 Jan 2013 12:47:04 +0100 Subject: ALSA: hda - Remove dead HDA_CTL_BIND_VOL and HDA_CTL_BIND_SW codes Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 1fb31975ee38..c20df5753cda 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -576,15 +576,11 @@ enum { HDA_CTL_WIDGET_VOL, HDA_CTL_WIDGET_MUTE, HDA_CTL_BIND_MUTE, - HDA_CTL_BIND_VOL, - HDA_CTL_BIND_SW, }; static const struct snd_kcontrol_new control_templates[] = { HDA_CODEC_VOLUME(NULL, 0, 0, 0), HDA_CODEC_MUTE(NULL, 0, 0, 0), HDA_BIND_MUTE(NULL, 0, 0, 0), - HDA_BIND_VOL(NULL, 0), - HDA_BIND_SW(NULL, 0), }; /* add dynamic controls from template */ -- cgit v1.2.3 From 5187ac168d6552ca10a95869c1fd33c256e7746a Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 7 Jan 2013 12:52:16 +0100 Subject: ALSA: hda - Add brief comments to exported snd_hda_gen_*_() functions Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index c20df5753cda..11436c115953 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -3901,6 +3901,10 @@ static void clear_unsol_on_unused_pins(struct hda_codec *codec) } } +/* + * initialize the generic spec; + * this can be put as patch_ops.init function + */ int snd_hda_gen_init(struct hda_codec *codec) { struct hda_gen_spec *spec = codec->spec; @@ -3937,7 +3941,10 @@ int snd_hda_gen_init(struct hda_codec *codec) } EXPORT_SYMBOL_HDA(snd_hda_gen_init); - +/* + * free the generic spec; + * this can be put as patch_ops.free function + */ void snd_hda_gen_free(struct hda_codec *codec) { snd_hda_gen_spec_free(codec->spec); @@ -3947,6 +3954,10 @@ void snd_hda_gen_free(struct hda_codec *codec) EXPORT_SYMBOL_HDA(snd_hda_gen_free); #ifdef CONFIG_PM +/* + * check the loopback power save state; + * this can be put as patch_ops.check_power_status function + */ int snd_hda_gen_check_power_status(struct hda_codec *codec, hda_nid_t nid) { struct hda_gen_spec *spec = codec->spec; -- cgit v1.2.3 From cd5be3f9de8945f782e1bbeffd080876eb2aa9f8 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 7 Jan 2013 15:07:00 +0100 Subject: ALSA: hda - Clear path indices properly at each re-evaluation The path indices must be reset at each evaluation of DAC assignment. Otherwise the badness value will be wrongly calculated and mixers may be inconsistently assigned. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 11436c115953..1b8fd4d6cae9 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -1138,6 +1138,16 @@ static int fill_and_eval_dacs(struct hda_codec *codec, memset(spec->multiout.extra_out_nid, 0, sizeof(spec->multiout.extra_out_nid)); spec->multi_ios = 0; snd_array_free(&spec->paths); + + /* clear path indices */ + memset(spec->out_paths, 0, sizeof(spec->out_paths)); + memset(spec->hp_paths, 0, sizeof(spec->hp_paths)); + memset(spec->speaker_paths, 0, sizeof(spec->speaker_paths)); + memset(spec->aamix_out_paths, 0, sizeof(spec->aamix_out_paths)); + memset(spec->digout_paths, 0, sizeof(spec->digout_paths)); + memset(spec->loopback_paths, 0, sizeof(spec->loopback_paths)); + memset(&spec->digin_path, 0, sizeof(spec->digin_path)); + badness = 0; /* fill hard-wired DACs first */ -- cgit v1.2.3 From 0e614dd058ec8a426c16d2057fc814696c2381d9 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 7 Jan 2013 15:11:44 +0100 Subject: ALSA: hda - Use direct path reference in assign_out_path_ctls() Instead of looking through paths with the dac -> pin connection at each time, just pass the already parsed path index to assign_out_path_ctls(). This simplifies the code a bit. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 1b8fd4d6cae9..3f9439c39311 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -757,23 +757,26 @@ enum { BAD_SHARED_VOL = 0x10, }; -/* look for widgets in the path between the given NIDs appropriate for +/* look for widgets in the given path which are appropriate for * volume and mute controls, and assign the values to ctls[]. * * When no appropriate widget is found in the path, the badness value * is incremented depending on the situation. The function returns the * total badness for both volume and mute controls. */ -static int assign_out_path_ctls(struct hda_codec *codec, hda_nid_t pin, - hda_nid_t dac) +static int assign_out_path_ctls(struct hda_codec *codec, struct nid_path *path) { - struct nid_path *path = snd_hda_get_nid_path(codec, dac, pin); hda_nid_t nid; unsigned int val; int badness = 0; if (!path) return BAD_SHARED_VOL * 2; + + if (path->ctls[NID_PATH_VOL_CTL] || + path->ctls[NID_PATH_MUTE_CTL]) + return 0; /* already evaluated */ + nid = look_for_out_vol_nid(codec, path); if (nid) { val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT); @@ -866,8 +869,9 @@ static int try_assign_dacs(struct hda_codec *codec, int num_outs, struct nid_path *path; hda_nid_t pin = pins[i]; - if (dacs[i]) { - badness += assign_out_path_ctls(codec, pin, dacs[i]); + path = snd_hda_get_path_from_idx(codec, path_idx[i]); + if (path) { + badness += assign_out_path_ctls(codec, path); continue; } @@ -916,9 +920,8 @@ static int try_assign_dacs(struct hda_codec *codec, int num_outs, print_nid_path("output", path); path->active = true; path_idx[i] = snd_hda_get_path_idx(codec, path); + badness += assign_out_path_ctls(codec, path); } - if (dac) - badness += assign_out_path_ctls(codec, pin, dac); } return badness; @@ -1001,6 +1004,7 @@ static int fill_multi_ios(struct hda_codec *codec, unsigned int defcfg = snd_hda_codec_get_pincfg(codec, reference_pin); unsigned int location = get_defcfg_location(defcfg); int badness = 0; + struct nid_path *path; old_pins = spec->multi_ios; if (old_pins >= 2) @@ -1012,7 +1016,6 @@ static int fill_multi_ios(struct hda_codec *codec, for (type = AUTO_PIN_LINE_IN; type >= AUTO_PIN_MIC; type--) { for (i = 0; i < cfg->num_inputs; i++) { - struct nid_path *path; hda_nid_t nid = cfg->inputs[i].pin; hda_nid_t dac = 0; @@ -1067,9 +1070,10 @@ static int fill_multi_ios(struct hda_codec *codec, } /* assign volume and mute controls */ - for (i = old_pins; i < spec->multi_ios; i++) - badness += assign_out_path_ctls(codec, spec->multi_io[i].pin, - spec->multi_io[i].dac); + for (i = old_pins; i < spec->multi_ios; i++) { + path = snd_hda_get_path_from_idx(codec, spec->out_paths[cfg->line_outs + i]); + badness += assign_out_path_ctls(codec, path); + } return badness; } -- cgit v1.2.3 From 50b1548775da7e80a3e1f1d9f0ddab1fd5d17fa3 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 7 Jan 2013 15:14:00 +0100 Subject: ALSA: hda - Remove unused dac reference in create_multi_out_ctls() Remove useless code. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 3f9439c39311..1fbc1b32359c 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -1428,12 +1428,8 @@ static int create_multi_out_ctls(struct hda_codec *codec, for (i = 0; i < noutputs; i++) { const char *name; int index; - hda_nid_t dac; struct nid_path *path; - dac = spec->multiout.dac_nids[i]; - if (!dac) - continue; if (i >= cfg->line_outs) { index = 0; name = channel_name[i]; -- cgit v1.2.3 From affdb62b815b38261f09f9d4ec210a35c7ffb1f3 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 7 Jan 2013 15:36:45 +0100 Subject: ALSA: hda - Don't set up active streams twice We don't have to set up a stream that has been already set up previously. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_codec.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 7eab3ae5ab6d..733bce62670c 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -1493,7 +1493,7 @@ void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid, "NID=0x%x, stream=0x%x, channel=%d, format=0x%x\n", nid, stream_tag, channel_id, format); p = get_hda_cvt_setup(codec, nid); - if (!p) + if (!p || p->active) return; if (codec->pcm_format_first) @@ -1540,7 +1540,7 @@ void __snd_hda_codec_cleanup_stream(struct hda_codec *codec, hda_nid_t nid, snd_printdd("hda_codec_cleanup_stream: NID=0x%x\n", nid); p = get_hda_cvt_setup(codec, nid); - if (p) { + if (p && p->active) { /* here we just clear the active flag when do_now isn't set; * actual clean-ups will be done later in * purify_inactive_streams() called from snd_hda_codec_prpapre() -- cgit v1.2.3 From a07a949be6eb1c9aab06adaadce72dbd27b7d9cb Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 7 Jan 2013 16:44:06 +0100 Subject: ALSA: hda - Fix multi-io channel mode management The multi-io channels can vary not only from 1 to 6 but also may vary from 6 to 8 or such. At the same time, there are more speaker pins available than the primary output pins. So, we need three variables to check: the minimum channel counts for primary outputs, the current channel counts for primary outputs, and the minimum channel counts for all outputs. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 92 ++++++++++++++++++++++++++++++--------------- sound/pci/hda/hda_generic.h | 16 +++++++- 2 files changed, 76 insertions(+), 32 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 1fbc1b32359c..afa54f87b691 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -1125,6 +1125,25 @@ static int check_aamix_out_path(struct hda_codec *codec, int path_idx) return snd_hda_get_path_idx(codec, path); } +/* fill the empty entries in the dac array for speaker/hp with the + * shared dac pointed by the paths + */ +static void refill_shared_dacs(struct hda_codec *codec, int num_outs, + hda_nid_t *dacs, int *path_idx) +{ + struct nid_path *path; + int i; + + for (i = 0; i < num_outs; i++) { + if (dacs[i]) + continue; + path = snd_hda_get_path_from_idx(codec, path_idx[i]); + if (!path) + continue; + dacs[i] = path->path[0]; + } +} + /* fill in the dac_nids table from the parsed pin configuration */ static int fill_and_eval_dacs(struct hda_codec *codec, bool fill_hardwired, @@ -1183,19 +1202,6 @@ static int fill_and_eval_dacs(struct hda_codec *codec, spec->private_dac_nids, spec->out_paths, &main_out_badness); - /* re-count num_dacs and squash invalid entries */ - spec->multiout.num_dacs = 0; - for (i = 0; i < cfg->line_outs; i++) { - if (spec->private_dac_nids[i]) - spec->multiout.num_dacs++; - else { - memmove(spec->private_dac_nids + i, - spec->private_dac_nids + i + 1, - sizeof(hda_nid_t) * (cfg->line_outs - i - 1)); - spec->private_dac_nids[cfg->line_outs - 1] = 0; - } - } - if (fill_mio_first && cfg->line_outs == 1 && cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) { /* try to fill multi-io first */ @@ -1246,16 +1252,41 @@ static int fill_and_eval_dacs(struct hda_codec *codec, if (count_multiio_pins(codec, cfg->hp_pins[0]) >= 2) spec->multi_ios = 1; /* give badness */ + /* re-count num_dacs and squash invalid entries */ + spec->multiout.num_dacs = 0; + for (i = 0; i < cfg->line_outs; i++) { + if (spec->private_dac_nids[i]) + spec->multiout.num_dacs++; + else { + memmove(spec->private_dac_nids + i, + spec->private_dac_nids + i + 1, + sizeof(hda_nid_t) * (cfg->line_outs - i - 1)); + spec->private_dac_nids[cfg->line_outs - 1] = 0; + } + } + + spec->ext_channel_count = spec->min_channel_count = + spec->multiout.num_dacs; + if (spec->multi_ios == 2) { for (i = 0; i < 2; i++) spec->private_dac_nids[spec->multiout.num_dacs++] = spec->multi_io[i].dac; - spec->ext_channel_count = 2; } else if (spec->multi_ios) { spec->multi_ios = 0; badness += BAD_MULTI_IO; } + /* re-fill the shared DAC for speaker / headphone */ + if (cfg->line_out_type != AUTO_PIN_HP_OUT) + refill_shared_dacs(codec, cfg->hp_outs, + spec->multiout.hp_out_nid, + spec->hp_paths); + if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) + refill_shared_dacs(codec, cfg->speaker_outs, + spec->multiout.extra_out_nid, + spec->speaker_paths); + return badness; } @@ -1610,14 +1641,15 @@ static int ch_mode_info(struct snd_kcontrol *kcontrol, { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct hda_gen_spec *spec = codec->spec; + int chs; uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; uinfo->count = 1; uinfo->value.enumerated.items = spec->multi_ios + 1; if (uinfo->value.enumerated.item > spec->multi_ios) uinfo->value.enumerated.item = spec->multi_ios; - sprintf(uinfo->value.enumerated.name, "%dch", - (uinfo->value.enumerated.item + 1) * 2); + chs = uinfo->value.enumerated.item * 2 + spec->min_channel_count; + sprintf(uinfo->value.enumerated.name, "%dch", chs); return 0; } @@ -1626,7 +1658,8 @@ static int ch_mode_get(struct snd_kcontrol *kcontrol, { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct hda_gen_spec *spec = codec->spec; - ucontrol->value.enumerated.item[0] = (spec->ext_channel_count - 1) / 2; + ucontrol->value.enumerated.item[0] = + (spec->ext_channel_count - spec->min_channel_count) / 2; return 0; } @@ -1674,9 +1707,9 @@ static int ch_mode_put(struct snd_kcontrol *kcontrol, ch = ucontrol->value.enumerated.item[0]; if (ch < 0 || ch > spec->multi_ios) return -EINVAL; - if (ch == (spec->ext_channel_count - 1) / 2) + if (ch == (spec->ext_channel_count - spec->min_channel_count) / 2) return 0; - spec->ext_channel_count = (ch + 1) * 2; + spec->ext_channel_count = ch * 2 + spec->min_channel_count; for (i = 0; i < spec->multi_ios; i++) set_multi_io(codec, i, i < ch); spec->multiout.max_channels = max(spec->ext_channel_count, @@ -3127,17 +3160,16 @@ int snd_hda_gen_parse_auto_config(struct hda_codec *codec, if (err < 0) return err; - /* check the multiple speaker pins */ - if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) - spec->const_channel_count = cfg->line_outs * 2; - else - spec->const_channel_count = cfg->speaker_outs * 2; - - if (spec->multi_ios > 0) - spec->multiout.max_channels = max(spec->ext_channel_count, - spec->const_channel_count); - else - spec->multiout.max_channels = spec->multiout.num_dacs * 2; + spec->const_channel_count = spec->ext_channel_count; + /* check the multiple speaker and headphone pins */ + if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) + spec->const_channel_count = max(spec->const_channel_count, + cfg->speaker_outs * 2); + if (cfg->line_out_type != AUTO_PIN_HP_OUT) + spec->const_channel_count = max(spec->const_channel_count, + cfg->hp_outs * 2); + spec->multiout.max_channels = max(spec->ext_channel_count, + spec->const_channel_count); err = check_auto_mute_availability(codec); if (err < 0) diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h index 00a1eab2c0b2..b65769cbde2b 100644 --- a/sound/pci/hda/hda_generic.h +++ b/sound/pci/hda/hda_generic.h @@ -117,8 +117,20 @@ struct hda_gen_spec { unsigned int cur_mux[3]; /* channel model */ - int const_channel_count; /* min. channel count (for speakers) */ - int ext_channel_count; /* current channel count for multi-io */ + /* min_channel_count contains the minimum channel count for primary + * outputs. When multi_ios is set, the channels can be configured + * between min_channel_count and (min_channel_count + multi_ios * 2). + * + * ext_channel_count contains the current channel count of the primary + * out. This varies in the range above. + * + * Meanwhile, const_channel_count is the channel count for all outputs + * including headphone and speakers. It's a constant value, and the + * PCM is set up as max(ext_channel_count, const_channel_count). + */ + int min_channel_count; /* min. channel count for primary out */ + int ext_channel_count; /* current channel count for primary */ + int const_channel_count; /* channel count for all */ /* PCM information */ struct hda_pcm pcm_rec[3]; /* used in build_pcms() */ -- cgit v1.2.3 From c697b716859c6c8c4e9f102304638c83ff2e61aa Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 7 Jan 2013 17:09:26 +0100 Subject: ALSA: hda - Manage input paths via path indices ... like we did for output and loopback paths. It makes the code slightly easier. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 44 ++++++++++++++++++-------------------------- sound/pci/hda/hda_generic.h | 1 + 2 files changed, 19 insertions(+), 26 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index afa54f87b691..7861c3af6ae0 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -1168,6 +1168,7 @@ static int fill_and_eval_dacs(struct hda_codec *codec, memset(spec->speaker_paths, 0, sizeof(spec->speaker_paths)); memset(spec->aamix_out_paths, 0, sizeof(spec->aamix_out_paths)); memset(spec->digout_paths, 0, sizeof(spec->digout_paths)); + memset(spec->input_paths, 0, sizeof(spec->input_paths)); memset(spec->loopback_paths, 0, sizeof(spec->loopback_paths)); memset(&spec->digin_path, 0, sizeof(spec->digin_path)); @@ -2058,6 +2059,7 @@ static int create_input_ctls(struct hda_codec *codec) for (i = 0; i < cfg->num_inputs; i++) { hda_nid_t pin; const char *label; + int imux_idx; bool imux_added; pin = cfg->inputs[i].pin; @@ -2083,24 +2085,23 @@ static int create_input_ctls(struct hda_codec *codec) } imux_added = false; + imux_idx = imux->num_items; for (c = 0; c < num_adcs; c++) { struct nid_path *path; hda_nid_t adc = spec->adc_nids[c]; if (!is_reachable_path(codec, pin, adc)) continue; - path = snd_array_new(&spec->paths); - if (!path) - return -ENOMEM; - memset(path, 0, sizeof(*path)); - if (!snd_hda_parse_nid_path(codec, pin, adc, HDA_PARSE_ALL, path)) { + path = snd_hda_add_new_path(codec, pin, adc, HDA_PARSE_ALL); + if (!path) { snd_printd(KERN_ERR "invalid input path 0x%x -> 0x%x\n", pin, adc); - spec->paths.used--; continue; } print_nid_path("input", path); + spec->input_paths[imux_idx][c] = + snd_hda_get_path_idx(codec, path); if (!imux_added) { spec->imux_pins[imux->num_items] = pin; @@ -2119,13 +2120,13 @@ static int create_input_ctls(struct hda_codec *codec) * input source mux */ -/* get the ADC NID corresponding to the given index */ -static hda_nid_t get_adc_nid(struct hda_codec *codec, int adc_idx, int imux_idx) +/* get the input path specified by the given adc and imux indices */ +static struct nid_path *get_input_path(struct hda_codec *codec, int adc_idx, int imux_idx) { struct hda_gen_spec *spec = codec->spec; if (spec->dyn_adc_switch) adc_idx = spec->dyn_adc_idx[imux_idx]; - return spec->adc_nids[adc_idx]; + return snd_hda_get_path_from_idx(codec, spec->input_paths[imux_idx][adc_idx]); } static int mux_select(struct hda_codec *codec, unsigned int adc_idx, @@ -2194,9 +2195,8 @@ static int cap_put_caller(struct snd_kcontrol *kcontrol, */ codec->cached_write = 1; for (i = 0; i < imux->num_items; i++) { - path = snd_hda_get_nid_path(codec, spec->imux_pins[i], - get_adc_nid(codec, adc_idx, i)); - if (!path->ctls[type]) + path = get_input_path(codec, adc_idx, i); + if (!path || !path->ctls[type]) continue; kcontrol->private_value = path->ctls[type]; err = func(kcontrol, ucontrol); @@ -2396,21 +2396,18 @@ static int create_bind_cap_vol_ctl(struct hda_codec *codec, int idx, /* return the vol ctl when used first in the imux list */ static unsigned int get_first_cap_ctl(struct hda_codec *codec, int idx, int type) { - struct hda_gen_spec *spec = codec->spec; struct nid_path *path; unsigned int ctl; int i; - path = snd_hda_get_nid_path(codec, spec->imux_pins[idx], - get_adc_nid(codec, 0, idx)); + path = get_input_path(codec, 0, idx); if (!path) return 0; ctl = path->ctls[type]; if (!ctl) return 0; for (i = 0; i < idx - 1; i++) { - path = snd_hda_get_nid_path(codec, spec->imux_pins[i], - get_adc_nid(codec, 0, i)); + path = get_input_path(codec, 0, i); if (path && path->ctls[type] == ctl) return 0; } @@ -2476,8 +2473,7 @@ static int create_capture_mixers(struct hda_codec *codec) vol = sw = 0; for (i = 0; i < imux->num_items; i++) { struct nid_path *path; - path = snd_hda_get_nid_path(codec, spec->imux_pins[i], - get_adc_nid(codec, n, i)); + path = get_input_path(codec, n, i); if (!path) continue; parse_capvol_in_path(codec, path); @@ -2635,9 +2631,7 @@ static int mux_select(struct hda_codec *codec, unsigned int adc_idx, if (spec->cur_mux[adc_idx] == idx) return 0; - path = snd_hda_get_nid_path(codec, - spec->imux_pins[spec->cur_mux[adc_idx]], - spec->adc_nids[adc_idx]); + path = get_input_path(codec, adc_idx, spec->cur_mux[adc_idx]); if (!path) return 0; if (path->active) @@ -2651,8 +2645,7 @@ static int mux_select(struct hda_codec *codec, unsigned int adc_idx, if (spec->dyn_adc_switch) dyn_adc_pcm_resetup(codec, idx); - path = snd_hda_get_nid_path(codec, spec->imux_pins[idx], - get_adc_nid(codec, adc_idx, idx)); + path = get_input_path(codec, adc_idx, idx); if (!path) return 0; if (path->active) @@ -3889,8 +3882,7 @@ static void init_input_src(struct hda_codec *codec) for (c = 0; c < nums; c++) { for (i = 0; i < imux->num_items; i++) { - path = snd_hda_get_nid_path(codec, spec->imux_pins[i], - get_adc_nid(codec, c, i)); + path = get_input_path(codec, c, i); if (path) { bool active = path->active; if (i == spec->cur_mux[c]) diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h index b65769cbde2b..1ad9127e9f95 100644 --- a/sound/pci/hda/hda_generic.h +++ b/sound/pci/hda/hda_generic.h @@ -156,6 +156,7 @@ struct hda_gen_spec { int speaker_paths[AUTO_CFG_MAX_OUTS]; int aamix_out_paths[3]; int digout_paths[AUTO_CFG_MAX_OUTS]; + int input_paths[HDA_MAX_NUM_INPUTS][AUTO_CFG_MAX_OUTS]; int loopback_paths[HDA_MAX_NUM_INPUTS]; int digin_path; -- cgit v1.2.3 From 3ca529d339f1904b68c6251172522302fca77b28 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 7 Jan 2013 17:25:08 +0100 Subject: ALSA: hda - Re-define snd_hda_parse_nid_path() This commit modifies the definition of snd_hda_parse_nid_path() slightly, now with_aa_mix argument is changed to anchor_nid, so that it can handle any NID generically as an anchor point to include or exclude. The with_aa_mix field in struct nid_path is removed again by this change. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 102 ++++++++++++++++++++++++-------------------- sound/pci/hda/hda_generic.h | 12 +----- 2 files changed, 57 insertions(+), 57 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 7861c3af6ae0..8e7ce7d2c138 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -87,9 +87,25 @@ EXPORT_SYMBOL_HDA(snd_hda_gen_spec_free); * parsing paths */ +/* return the position of NID in the list, or -1 if not found */ +static int find_idx_in_nid_list(hda_nid_t nid, const hda_nid_t *list, int nums) +{ + int i; + for (i = 0; i < nums; i++) + if (list[i] == nid) + return i; + return -1; +} + +/* return true if the given NID is contained in the path */ +static bool is_nid_contained(struct nid_path *path, hda_nid_t nid) +{ + return find_idx_in_nid_list(nid, path->path, path->depth) >= 0; +} + static struct nid_path *get_nid_path(struct hda_codec *codec, hda_nid_t from_nid, hda_nid_t to_nid, - int with_aa_mix) + int anchor_nid) { struct hda_gen_spec *spec = codec->spec; int i; @@ -100,8 +116,9 @@ static struct nid_path *get_nid_path(struct hda_codec *codec, continue; if ((!from_nid || path->path[0] == from_nid) && (!to_nid || path->path[path->depth - 1] == to_nid)) { - if (with_aa_mix == HDA_PARSE_ALL || - path->with_aa_mix == with_aa_mix) + if (!anchor_nid || + (anchor_nid > 0 && is_nid_contained(path, anchor_nid)) || + (anchor_nid < 0 && !is_nid_contained(path, anchor_nid))) return path; } } @@ -114,7 +131,7 @@ static struct nid_path *get_nid_path(struct hda_codec *codec, struct nid_path *snd_hda_get_nid_path(struct hda_codec *codec, hda_nid_t from_nid, hda_nid_t to_nid) { - return get_nid_path(codec, from_nid, to_nid, HDA_PARSE_ALL); + return get_nid_path(codec, from_nid, to_nid, 0); } EXPORT_SYMBOL_HDA(snd_hda_get_nid_path); @@ -213,17 +230,16 @@ static void print_nid_path(const char *pfx, struct nid_path *path) /* called recursively */ static bool __parse_nid_path(struct hda_codec *codec, hda_nid_t from_nid, hda_nid_t to_nid, - int with_aa_mix, struct nid_path *path, int depth) + int anchor_nid, struct nid_path *path, + int depth) { - struct hda_gen_spec *spec = codec->spec; const hda_nid_t *conn; int i, nums; - if (to_nid == spec->mixer_nid) { - if (with_aa_mix == HDA_PARSE_NO_AAMIX) - return false; - with_aa_mix = HDA_PARSE_ALL; /* mark aa-mix is included */ - } + if (to_nid == anchor_nid) + anchor_nid = 0; /* anchor passed */ + else if (to_nid == (hda_nid_t)(-anchor_nid)) + return false; /* hit the exclusive nid */ nums = snd_hda_get_conn_list(codec, to_nid, &conn); for (i = 0; i < nums; i++) { @@ -236,8 +252,8 @@ static bool __parse_nid_path(struct hda_codec *codec, is_dac_already_used(codec, conn[i])) continue; } - /* aa-mix is requested but not included? */ - if (!(spec->mixer_nid && with_aa_mix == HDA_PARSE_ONLY_AAMIX)) + /* anchor is not requested or already passed? */ + if (anchor_nid <= 0) goto found; } if (depth >= MAX_NID_PATH_DEPTH) @@ -249,15 +265,13 @@ static bool __parse_nid_path(struct hda_codec *codec, type == AC_WID_PIN) continue; if (__parse_nid_path(codec, from_nid, conn[i], - with_aa_mix, path, depth + 1)) + anchor_nid, path, depth + 1)) goto found; } return false; found: path->path[path->depth] = conn[i]; - if (conn[i] == spec->mixer_nid) - path->with_aa_mix = true; path->idx[path->depth + 1] = i; if (nums > 1 && get_wcaps_type(get_wcaps(codec, to_nid)) != AC_WID_AUD_MIX) path->multi[path->depth + 1] = 1; @@ -267,17 +281,17 @@ static bool __parse_nid_path(struct hda_codec *codec, /* parse the widget path from the given nid to the target nid; * when @from_nid is 0, try to find an empty DAC; - * when @with_aa_mix is HDA_PARSE_NO_AAMIX, paths with spec->mixer_nid are - * excluded, only the paths that don't go through the mixer will be chosen. - * when @with_aa_mix is HDA_PARSE_ONLY_AAMIX, only the paths going through - * spec->mixer_nid will be chosen. - * when @with_aa_mix is HDA_PARSE_ALL, no special handling about mixer widget. + * when @anchor_nid is set to a positive value, only paths through the widget + * with the given value are evaluated. + * when @anchor_nid is set to a negative value, paths through the widget + * with the negative of given value are excluded, only other paths are chosen. + * when @anchor_nid is zero, no special handling about path selection. */ bool snd_hda_parse_nid_path(struct hda_codec *codec, hda_nid_t from_nid, - hda_nid_t to_nid, int with_aa_mix, + hda_nid_t to_nid, int anchor_nid, struct nid_path *path) { - if (__parse_nid_path(codec, from_nid, to_nid, with_aa_mix, path, 1)) { + if (__parse_nid_path(codec, from_nid, to_nid, anchor_nid, path, 1)) { path->path[path->depth] = to_nid; path->depth++; return true; @@ -292,7 +306,7 @@ EXPORT_SYMBOL_HDA(snd_hda_parse_nid_path); */ struct nid_path * snd_hda_add_new_path(struct hda_codec *codec, hda_nid_t from_nid, - hda_nid_t to_nid, int with_aa_mix) + hda_nid_t to_nid, int anchor_nid) { struct hda_gen_spec *spec = codec->spec; struct nid_path *path; @@ -301,7 +315,7 @@ snd_hda_add_new_path(struct hda_codec *codec, hda_nid_t from_nid, return NULL; /* check whether the path has been already added */ - path = get_nid_path(codec, from_nid, to_nid, with_aa_mix); + path = get_nid_path(codec, from_nid, to_nid, anchor_nid); if (path) return path; @@ -309,7 +323,7 @@ snd_hda_add_new_path(struct hda_codec *codec, hda_nid_t from_nid, if (!path) return NULL; memset(path, 0, sizeof(*path)); - if (snd_hda_parse_nid_path(codec, from_nid, to_nid, with_aa_mix, path)) + if (snd_hda_parse_nid_path(codec, from_nid, to_nid, anchor_nid, path)) return path; /* push back */ spec->paths.used--; @@ -909,10 +923,10 @@ static int try_assign_dacs(struct hda_codec *codec, int num_outs, else badness += bad->no_dac; } - path = snd_hda_add_new_path(codec, dac, pin, HDA_PARSE_NO_AAMIX); + path = snd_hda_add_new_path(codec, dac, pin, -spec->mixer_nid); if (!path && !i && spec->mixer_nid) { /* try with aamix */ - path = snd_hda_add_new_path(codec, dac, pin, HDA_PARSE_ALL); + path = snd_hda_add_new_path(codec, dac, pin, 0); } if (!path) dac = dacs[i] = 0; @@ -1038,7 +1052,8 @@ static int fill_multi_ios(struct hda_codec *codec, badness++; continue; } - path = snd_hda_add_new_path(codec, dac, nid, HDA_PARSE_NO_AAMIX); + path = snd_hda_add_new_path(codec, dac, nid, + -spec->mixer_nid); if (!path) { badness++; continue; @@ -1093,9 +1108,10 @@ static bool map_singles(struct hda_codec *codec, int outs, dac = get_dac_if_single(codec, pins[i]); if (!dac) continue; - path = snd_hda_add_new_path(codec, dac, pins[i], HDA_PARSE_NO_AAMIX); + path = snd_hda_add_new_path(codec, dac, pins[i], + -spec->mixer_nid); if (!path && !i && spec->mixer_nid) - path = snd_hda_add_new_path(codec, dac, pins[i], HDA_PARSE_ALL); + path = snd_hda_add_new_path(codec, dac, pins[i], 0); if (path) { dacs[i] = dac; found = true; @@ -1110,14 +1126,16 @@ static bool map_singles(struct hda_codec *codec, int outs, /* create a new path including aamix if available, and return its index */ static int check_aamix_out_path(struct hda_codec *codec, int path_idx) { + struct hda_gen_spec *spec = codec->spec; struct nid_path *path; path = snd_hda_get_path_from_idx(codec, path_idx); - if (!path || !path->depth || path->with_aa_mix) + if (!path || !path->depth || + is_nid_contained(path, spec->mixer_nid)) return 0; path = snd_hda_add_new_path(codec, path->path[0], path->path[path->depth - 1], - HDA_PARSE_ONLY_AAMIX); + spec->mixer_nid); if (!path) return 0; print_nid_path("output-aamix", path); @@ -1919,7 +1937,7 @@ static int new_analog_input(struct hda_codec *codec, int input_idx, !nid_has_mute(codec, mix_nid, HDA_INPUT)) return 0; /* no need for analog loopback */ - path = snd_hda_add_new_path(codec, pin, mix_nid, HDA_PARSE_ALL); + path = snd_hda_add_new_path(codec, pin, mix_nid, 0); if (!path) return -EINVAL; print_nid_path("loopback", path); @@ -2092,7 +2110,7 @@ static int create_input_ctls(struct hda_codec *codec) if (!is_reachable_path(codec, pin, adc)) continue; - path = snd_hda_add_new_path(codec, pin, adc, HDA_PARSE_ALL); + path = snd_hda_add_new_path(codec, pin, adc, 0); if (!path) { snd_printd(KERN_ERR "invalid input path 0x%x -> 0x%x\n", @@ -2567,7 +2585,7 @@ static void parse_digital(struct hda_codec *codec) dig_nid = look_for_dac(codec, pin, true); if (!dig_nid) continue; - path = snd_hda_add_new_path(codec, dig_nid, pin, HDA_PARSE_ALL); + path = snd_hda_add_new_path(codec, dig_nid, pin, 0); if (!path) continue; print_nid_path("digout", path); @@ -2595,7 +2613,7 @@ static void parse_digital(struct hda_codec *codec) continue; path = snd_hda_add_new_path(codec, spec->autocfg.dig_in_pin, - dig_nid, HDA_PARSE_ALL); + dig_nid, 0); if (path) { print_nid_path("digin", path); path->active = true; @@ -2971,16 +2989,6 @@ static int check_auto_mute_availability(struct hda_codec *codec) return 0; } -/* return the position of NID in the list, or -1 if not found */ -static int find_idx_in_nid_list(hda_nid_t nid, const hda_nid_t *list, int nums) -{ - int i; - for (i = 0; i < nums; i++) - if (list[i] == nid) - return i; - return -1; -} - /* check whether all auto-mic pins are valid; setup indices if OK */ static bool auto_mic_check_imux(struct hda_codec *codec) { diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h index 1ad9127e9f95..343195c827e4 100644 --- a/sound/pci/hda/hda_generic.h +++ b/sound/pci/hda/hda_generic.h @@ -53,7 +53,6 @@ struct nid_path { unsigned char multi[MAX_NID_PATH_DEPTH]; unsigned int ctls[NID_PATH_NUM_CTLS]; /* NID_PATH_XXX_CTL */ bool active; - bool with_aa_mix; }; /* mic/line-in auto switching entry */ @@ -237,19 +236,12 @@ struct nid_path *snd_hda_get_nid_path(struct hda_codec *codec, hda_nid_t from_nid, hda_nid_t to_nid); int snd_hda_get_path_idx(struct hda_codec *codec, struct nid_path *path); struct nid_path *snd_hda_get_path_from_idx(struct hda_codec *codec, int idx); - -enum { - HDA_PARSE_NO_AAMIX, - HDA_PARSE_ONLY_AAMIX, - HDA_PARSE_ALL, -}; - bool snd_hda_parse_nid_path(struct hda_codec *codec, hda_nid_t from_nid, - hda_nid_t to_nid, int with_aa_mix, + hda_nid_t to_nid, int anchor_nid, struct nid_path *path); struct nid_path * snd_hda_add_new_path(struct hda_codec *codec, hda_nid_t from_nid, - hda_nid_t to_nid, int with_aa_mix); + hda_nid_t to_nid, int anchor_nid); void snd_hda_activate_path(struct hda_codec *codec, struct nid_path *path, bool enable, bool add_aamix); -- cgit v1.2.3 From fb690cf58278163b464e3ea8b76ad31e07fb140c Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 7 Jan 2013 18:21:47 +0100 Subject: ALSA: hda - Handle BOTH jack port as a fixed output When the default config value shows the connection AC_JACK_PORT_BOTH, it's better to handle it as a speaker pin. This makes the behavior consistent in snd_hda_get_pin_label() and snd_hda_parse_pin_defcfg(). There are only few old machines showing this attribute, and all of them are actually fixed speaker pins, as far as I know. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_auto_parser.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_auto_parser.c b/sound/pci/hda/hda_auto_parser.c index 44c81d34544d..6a01c012a0ae 100644 --- a/sound/pci/hda/hda_auto_parser.c +++ b/sound/pci/hda/hda_auto_parser.c @@ -156,7 +156,8 @@ int snd_hda_parse_pin_defcfg(struct hda_codec *codec, /* workaround for buggy BIOS setups */ if (dev == AC_JACK_LINE_OUT) { - if (conn == AC_JACK_PORT_FIXED) + if (conn == AC_JACK_PORT_FIXED || + conn == AC_JACK_PORT_BOTH) dev = AC_JACK_SPEAKER; } -- cgit v1.2.3 From d12daf6f41693b6b34351b37b6d05d1a6f9b3472 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 7 Jan 2013 16:32:11 +0100 Subject: ALSA: hda - Add a flag to suppress mic auto-switch Add a new flag spec->suppress_mic_auto_switch for codecs that don't support unsol events properly like VT1708. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 3 +++ sound/pci/hda/hda_generic.h | 1 + 2 files changed, 4 insertions(+) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 8e7ce7d2c138..b488c62ed97d 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -3034,6 +3034,9 @@ static int check_auto_mic_availability(struct hda_codec *codec) unsigned int types; int i, num_pins; + if (spec->suppress_auto_mic) + return 0; + types = 0; num_pins = 0; for (i = 0; i < cfg->num_inputs; i++) { diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h index 343195c827e4..1763e33b90ef 100644 --- a/sound/pci/hda/hda_generic.h +++ b/sound/pci/hda/hda_generic.h @@ -175,6 +175,7 @@ struct hda_gen_spec { unsigned int automute_speaker_possible:1; /* there are speakers and either LO or HP */ unsigned int automute_lo_possible:1; /* there are line outs and HP */ unsigned int keep_vref_in_automute:1; /* Don't clear VREF in automute */ + unsigned int suppress_auto_mic:1; /* suppress input jack auto switch */ unsigned int line_in_auto_switch:1; /* allow line-in auto switch */ /* other flags */ -- cgit v1.2.3 From ca29683bd63a463d48934dc5b50ec4aecbfaa7c2 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 9 Jan 2013 08:41:41 +0100 Subject: ALSA: hda - Exclude aamix from capture paths The capture paths shouldn't contain the analog loopback mixer. Pass a proper argument to exclude the aamix NID. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index b488c62ed97d..f07b32668f42 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -2110,13 +2110,9 @@ static int create_input_ctls(struct hda_codec *codec) if (!is_reachable_path(codec, pin, adc)) continue; - path = snd_hda_add_new_path(codec, pin, adc, 0); - if (!path) { - snd_printd(KERN_ERR - "invalid input path 0x%x -> 0x%x\n", - pin, adc); + path = snd_hda_add_new_path(codec, pin, adc, -mixer); + if (!path) continue; - } print_nid_path("input", path); spec->input_paths[imux_idx][c] = snd_hda_get_path_idx(codec, path); -- cgit v1.2.3 From 54d778b31c98b305bf47fbbabd4107a3898ebe66 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 9 Jan 2013 08:46:34 +0100 Subject: ALSA: hda - Return "Headphone Mic" from hda_get_autocfg_input_label() Instead of handling special cases in the caller side, give a proper name string "Headphone Mic" from hda_get_autocfg_input_label() when the headhpone jack pin is specified as an input. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_auto_parser.c | 2 ++ sound/pci/hda/hda_generic.c | 4 ---- 2 files changed, 2 insertions(+), 4 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_auto_parser.c b/sound/pci/hda/hda_auto_parser.c index 6a01c012a0ae..e5b20219d850 100644 --- a/sound/pci/hda/hda_auto_parser.c +++ b/sound/pci/hda/hda_auto_parser.c @@ -395,6 +395,8 @@ static const char *hda_get_input_pin_label(struct hda_codec *codec, return "SPDIF In"; case AC_JACK_DIG_OTHER_IN: return "Digital In"; + case AC_JACK_HP_OUT: + return "Headphone Mic"; default: return "Misc"; } diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index f07b32668f42..aa4e6398e3b4 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -2085,8 +2085,6 @@ static int create_input_ctls(struct hda_codec *codec) continue; label = hda_get_autocfg_input_label(codec, cfg, i); - if (spec->shared_mic_hp && !strcmp(label, "Misc")) - label = "Headphone Mic"; if (prev_label && !strcmp(label, prev_label)) type_idx++; else @@ -2540,8 +2538,6 @@ static int parse_mic_boost(struct hda_codec *codec) unsigned int val; label = hda_get_autocfg_input_label(codec, cfg, i); - if (spec->shared_mic_hp && !strcmp(label, "Misc")) - label = "Headphone Mic"; if (prev_label && !strcmp(label, prev_label)) type_idx++; else -- cgit v1.2.3 From 3a65bcdc577a338712c2eaefc194909de79d4982 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 9 Jan 2013 09:06:18 +0100 Subject: ALSA: hda - Fix inconsistent input_paths after ADC reduction In the current parser code, the input_paths[] may become inconsistent when some of detected ADCs are dropped due to incomplete inputs, since the driver rearranges only adc_nids[] but doesn't touch input_paths[]. This patch fixes the issue, and also it optimizes the reachability checks by simply referring to the parsed input_paths[] instead of calling is_reachable() again for each connection. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 34 ++++++++++++++++++++++------------ 1 file changed, 22 insertions(+), 12 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index aa4e6398e3b4..d16ef1d490fb 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -2002,24 +2002,24 @@ static int check_dyn_adc_switch(struct hda_codec *codec) { struct hda_gen_spec *spec = codec->spec; struct hda_input_mux *imux = &spec->input_mux; - hda_nid_t adc_nids[ARRAY_SIZE(spec->adc_nids)]; + unsigned int ok_bits; int i, n, nums; - hda_nid_t pin, adc; again: nums = 0; + ok_bits = 0; for (n = 0; n < spec->num_adc_nids; n++) { - adc = spec->adc_nids[n]; for (i = 0; i < imux->num_items; i++) { - pin = spec->imux_pins[i]; - if (!is_reachable_path(codec, pin, adc)) + if (!spec->input_paths[i][n]) break; } - if (i >= imux->num_items) - adc_nids[nums++] = adc; + if (i >= imux->num_items) { + ok_bits |= (1 << n); + nums++; + } } - if (!nums) { + if (!ok_bits) { if (spec->shared_mic_hp) { spec->shared_mic_hp = 0; imux->num_items = 1; @@ -2028,10 +2028,8 @@ static int check_dyn_adc_switch(struct hda_codec *codec) /* check whether ADC-switch is possible */ for (i = 0; i < imux->num_items; i++) { - pin = spec->imux_pins[i]; for (n = 0; n < spec->num_adc_nids; n++) { - adc = spec->adc_nids[n]; - if (is_reachable_path(codec, pin, adc)) { + if (spec->input_paths[i][n]) { spec->dyn_adc_idx[i] = n; break; } @@ -2041,7 +2039,19 @@ static int check_dyn_adc_switch(struct hda_codec *codec) snd_printdd("hda-codec: enabling ADC switching\n"); spec->dyn_adc_switch = 1; } else if (nums != spec->num_adc_nids) { - memcpy(spec->adc_nids, adc_nids, nums * sizeof(hda_nid_t)); + /* shrink the invalid adcs and input paths */ + nums = 0; + for (n = 0; n < spec->num_adc_nids; n++) { + if (!(ok_bits & (1 << n))) + continue; + if (n != nums) { + spec->adc_nids[nums] = spec->adc_nids[n]; + for (i = 0; i < imux->num_items; i++) + spec->input_paths[i][nums] = + spec->input_paths[i][n]; + } + nums++; + } spec->num_adc_nids = nums; } -- cgit v1.2.3 From f3fc0b0b1fe31e0ec9a72ab85b421e74c696f00d Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 9 Jan 2013 09:14:23 +0100 Subject: ALSA: hda - Allow aamix as a capture source Since some codecs can choose the aamix as a capture source, we should support it as well. When spec->add_stereo_mix_input flag is set, the parser checks the availability of aamix as the input source, and adds the paths automatically when possible. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 69 +++++++++++++++++++++++++++++---------------- sound/pci/hda/hda_generic.h | 1 + 2 files changed, 45 insertions(+), 25 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index d16ef1d490fb..26e8d83b5672 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -2067,6 +2067,40 @@ static int check_dyn_adc_switch(struct hda_codec *codec) return 0; } +/* parse capture source paths from the given pin and create imux items */ +static int parse_capture_source(struct hda_codec *codec, hda_nid_t pin, + int num_adcs, const char *label, int anchor) +{ + struct hda_gen_spec *spec = codec->spec; + struct hda_input_mux *imux = &spec->input_mux; + int imux_idx = imux->num_items; + bool imux_added = false; + int c; + + for (c = 0; c < num_adcs; c++) { + struct nid_path *path; + hda_nid_t adc = spec->adc_nids[c]; + + if (!is_reachable_path(codec, pin, adc)) + continue; + path = snd_hda_add_new_path(codec, pin, adc, anchor); + if (!path) + continue; + print_nid_path("input", path); + spec->input_paths[imux_idx][c] = + snd_hda_get_path_idx(codec, path); + + if (!imux_added) { + spec->imux_pins[imux->num_items] = pin; + snd_hda_add_imux_item(imux, label, + imux->num_items, NULL); + imux_added = true; + } + } + + return 0; +} + /* * create playback/capture controls for input pins */ @@ -2075,9 +2109,8 @@ static int create_input_ctls(struct hda_codec *codec) struct hda_gen_spec *spec = codec->spec; const struct auto_pin_cfg *cfg = &spec->autocfg; hda_nid_t mixer = spec->mixer_nid; - struct hda_input_mux *imux = &spec->input_mux; int num_adcs; - int i, c, err, type_idx = 0; + int i, err, type_idx = 0; const char *prev_label = NULL; num_adcs = fill_adc_nids(codec); @@ -2087,8 +2120,6 @@ static int create_input_ctls(struct hda_codec *codec) for (i = 0; i < cfg->num_inputs; i++) { hda_nid_t pin; const char *label; - int imux_idx; - bool imux_added; pin = cfg->inputs[i].pin; if (!is_input_pin(codec, pin)) @@ -2110,28 +2141,16 @@ static int create_input_ctls(struct hda_codec *codec) } } - imux_added = false; - imux_idx = imux->num_items; - for (c = 0; c < num_adcs; c++) { - struct nid_path *path; - hda_nid_t adc = spec->adc_nids[c]; - - if (!is_reachable_path(codec, pin, adc)) - continue; - path = snd_hda_add_new_path(codec, pin, adc, -mixer); - if (!path) - continue; - print_nid_path("input", path); - spec->input_paths[imux_idx][c] = - snd_hda_get_path_idx(codec, path); + err = parse_capture_source(codec, pin, num_adcs, label, -mixer); + if (err < 0) + return err; + } - if (!imux_added) { - spec->imux_pins[imux->num_items] = pin; - snd_hda_add_imux_item(imux, label, - imux->num_items, NULL); - imux_added = true; - } - } + if (mixer && spec->add_stereo_mix_input) { + err = parse_capture_source(codec, mixer, num_adcs, + "Stereo Mix", 0); + if (err < 0) + return err; } return 0; diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h index 1763e33b90ef..89683c7fe263 100644 --- a/sound/pci/hda/hda_generic.h +++ b/sound/pci/hda/hda_generic.h @@ -190,6 +190,7 @@ struct hda_gen_spec { unsigned int vmaster_mute_enum:1; /* add vmaster mute mode enum */ unsigned int indep_hp:1; /* independent HP supported */ unsigned int indep_hp_enabled:1; /* independent HP enabled */ + unsigned int add_stereo_mix_input:1; /* add aamix as a capture src */ /* loopback mixing mode */ bool aamix_mode; -- cgit v1.2.3 From 980428cecc4ca767bd9dd61fc286cd4124fd34af Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 9 Jan 2013 09:28:20 +0100 Subject: ALSA: hda - Clear the dropped paths properly When a DAC is reassigned from surrounds to front or ADCs are reduced due to incomplete imux, we clear the path indices but the path instances remain as is. Since the paths might be still referred through the whole path list parsing (e.g. is_active_nid()), we should clear these path instances as well. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 26e8d83b5672..a9bf188fe849 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -331,6 +331,15 @@ snd_hda_add_new_path(struct hda_codec *codec, hda_nid_t from_nid, } EXPORT_SYMBOL_HDA(snd_hda_add_new_path); +/* clear the given path as invalid so that it won't be picked up later */ +static void invalidate_nid_path(struct hda_codec *codec, int idx) +{ + struct nid_path *path = snd_hda_get_path_from_idx(codec, idx); + if (!path) + return; + memset(path, 0, sizeof(*path)); +} + /* look for an empty DAC slot */ static hda_nid_t look_for_dac(struct hda_codec *codec, hda_nid_t pin, bool is_digital) @@ -891,10 +900,12 @@ static int try_assign_dacs(struct hda_codec *codec, int num_outs, dacs[i] = look_for_dac(codec, pin, false); if (!dacs[i] && !i) { + /* try to steal the DAC of surrounds for the front */ for (j = 1; j < num_outs; j++) { if (is_reachable_path(codec, dacs[j], pin)) { dacs[0] = dacs[j]; dacs[j] = 0; + invalidate_nid_path(codec, path_idx[j]); path_idx[j] = 0; break; } @@ -2046,9 +2057,12 @@ static int check_dyn_adc_switch(struct hda_codec *codec) continue; if (n != nums) { spec->adc_nids[nums] = spec->adc_nids[n]; - for (i = 0; i < imux->num_items; i++) + for (i = 0; i < imux->num_items; i++) { + invalidate_nid_path(codec, + spec->input_paths[i][nums]); spec->input_paths[i][nums] = spec->input_paths[i][n]; + } } nums++; } -- cgit v1.2.3 From d7fdc00ae50b3dc02364301b334a6352c58e9e85 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jan 2013 08:38:04 +0100 Subject: ALSA: hda - Add helper functions to cache the current pinctl target We already have the list of whole pin widgets and there is an unused space in the list; let's use it for caching the current pinctl value. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_codec.c | 27 +++++++++++++++++++++++++++ sound/pci/hda/hda_codec.h | 4 ++-- sound/pci/hda/hda_local.h | 4 ++++ 3 files changed, 33 insertions(+), 2 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 733bce62670c..505cb72e4ee7 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -1100,6 +1100,32 @@ unsigned int snd_hda_codec_get_pincfg(struct hda_codec *codec, hda_nid_t nid) } EXPORT_SYMBOL_HDA(snd_hda_codec_get_pincfg); +/* remember the current pinctl target value */ +int snd_hda_codec_set_pin_target(struct hda_codec *codec, hda_nid_t nid, + unsigned int val) +{ + struct hda_pincfg *pin; + + pin = look_up_pincfg(codec, &codec->init_pins, nid); + if (!pin) + return -EINVAL; + pin->target = val; + return 0; +} +EXPORT_SYMBOL_HDA(snd_hda_codec_set_pin_target); + +/* return the current pinctl target value */ +int snd_hda_codec_get_pin_target(struct hda_codec *codec, hda_nid_t nid) +{ + struct hda_pincfg *pin; + + pin = look_up_pincfg(codec, &codec->init_pins, nid); + if (!pin) + return 0; + return pin->target; +} +EXPORT_SYMBOL_HDA(snd_hda_codec_get_pin_target); + /** * snd_hda_shutup_pins - Shut up all pins * @codec: the HDA codec @@ -5266,6 +5292,7 @@ int _snd_hda_set_pin_ctl(struct hda_codec *codec, hda_nid_t pin, val &= ~(AC_PINCTL_IN_EN | AC_PINCTL_VREFEN); } } + snd_hda_codec_set_pin_target(codec, pin, val); if (cached) return snd_hda_codec_update_cache(codec, pin, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, val); diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index 93ec747ce904..4c4f1660e654 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -981,8 +981,8 @@ void snd_hda_codec_resume_cache(struct hda_codec *codec); /* the struct for codec->pin_configs */ struct hda_pincfg { hda_nid_t nid; - unsigned char ctrl; /* current pin control value */ - unsigned char pad; /* reserved */ + unsigned char ctrl; /* original pin control value */ + unsigned char target; /* target pin control value */ unsigned int cfg; /* default configuration */ }; diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index fec0e2d72894..655a40f93020 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -529,6 +529,10 @@ snd_hda_set_pin_ctl_cache(struct hda_codec *codec, hda_nid_t pin, return _snd_hda_set_pin_ctl(codec, pin, val, true); } +int snd_hda_codec_get_pin_target(struct hda_codec *codec, hda_nid_t nid); +int snd_hda_codec_set_pin_target(struct hda_codec *codec, hda_nid_t nid, + unsigned int val); + /* * get widget capabilities */ -- cgit v1.2.3 From 62f3a2f718131e6f42746ccd26dbf4eb5eab677a Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jan 2013 08:56:46 +0100 Subject: ALSA: hda - More strict correction of invalid pinctl bits Check more strictly about the validity of pinctl values in snd_hda_set_pin_ctl() and correct the wrong bits automatically. Also provide the helper function to correct pinctl bits to codec drivers. This automatically fixes the invalid pinctl writes that are found in a few Realtek fixups for NID 0x0f amp like ASUS A6Rp. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_codec.c | 66 +++++++++++++++++++++++++++++++++++++---------- sound/pci/hda/hda_local.h | 2 ++ 2 files changed, 54 insertions(+), 14 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 505cb72e4ee7..0a531f2f9255 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -5275,23 +5275,61 @@ unsigned int snd_hda_get_default_vref(struct hda_codec *codec, hda_nid_t pin) } EXPORT_SYMBOL_HDA(snd_hda_get_default_vref); +/* correct the pin ctl value for matching with the pin cap */ +unsigned int snd_hda_correct_pin_ctl(struct hda_codec *codec, + hda_nid_t pin, unsigned int val) +{ + static unsigned int cap_lists[][2] = { + { AC_PINCTL_VREF_100, AC_PINCAP_VREF_100 }, + { AC_PINCTL_VREF_80, AC_PINCAP_VREF_80 }, + { AC_PINCTL_VREF_50, AC_PINCAP_VREF_50 }, + { AC_PINCTL_VREF_GRD, AC_PINCAP_VREF_GRD }, + }; + unsigned int cap; + + if (!val) + return 0; + cap = snd_hda_query_pin_caps(codec, pin); + if (!cap) + return val; /* don't know what to do... */ + + if (val & AC_PINCTL_OUT_EN) { + if (!(cap & AC_PINCAP_OUT)) + val &= ~(AC_PINCTL_OUT_EN | AC_PINCTL_HP_EN); + else if ((val & AC_PINCTL_HP_EN) && !(cap & AC_PINCAP_HP_DRV)) + val &= ~AC_PINCTL_HP_EN; + } + + if (val & AC_PINCTL_IN_EN) { + if (!(cap & AC_PINCAP_IN)) + val &= ~(AC_PINCTL_IN_EN | AC_PINCTL_VREFEN); + else { + unsigned int vcap, vref; + int i; + vcap = (cap & AC_PINCAP_VREF) >> AC_PINCAP_VREF_SHIFT; + vref = val & AC_PINCTL_VREFEN; + for (i = 0; i < ARRAY_SIZE(cap_lists); i++) { + if (vref == cap_lists[i][0] && + !(vcap & cap_lists[i][1])) { + if (i == ARRAY_SIZE(cap_lists) - 1) + vref = AC_PINCTL_VREF_HIZ; + else + vref = cap_lists[i + 1][0]; + } + } + val &= ~AC_PINCTL_VREFEN; + val |= vref; + } + } + + return val; +} +EXPORT_SYMBOL_HDA(snd_hda_correct_pin_ctl); + int _snd_hda_set_pin_ctl(struct hda_codec *codec, hda_nid_t pin, unsigned int val, bool cached) { - if (val) { - unsigned int cap = snd_hda_query_pin_caps(codec, pin); - if (cap && (val & AC_PINCTL_OUT_EN)) { - if (!(cap & AC_PINCAP_OUT)) - val &= ~(AC_PINCTL_OUT_EN | AC_PINCTL_HP_EN); - else if ((val & AC_PINCTL_HP_EN) && - !(cap & AC_PINCAP_HP_DRV)) - val &= ~AC_PINCTL_HP_EN; - } - if (cap && (val & AC_PINCTL_IN_EN)) { - if (!(cap & AC_PINCAP_IN)) - val &= ~(AC_PINCTL_IN_EN | AC_PINCTL_VREFEN); - } - } + val = snd_hda_correct_pin_ctl(codec, pin, val); snd_hda_codec_set_pin_target(codec, pin, val); if (cached) return snd_hda_codec_update_cache(codec, pin, 0, diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index 655a40f93020..aa721aa1921a 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -490,6 +490,8 @@ struct hda_bus_unsolicited { #define PIN_HP_AMP (AC_PINCTL_HP_EN) unsigned int snd_hda_get_default_vref(struct hda_codec *codec, hda_nid_t pin); +unsigned int snd_hda_correct_pin_ctl(struct hda_codec *codec, + hda_nid_t pin, unsigned int val); int _snd_hda_set_pin_ctl(struct hda_codec *codec, hda_nid_t pin, unsigned int val, bool cached); -- cgit v1.2.3 From 2c12c30d3fe5589d32ceddade09f13f1d3d6391d Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jan 2013 09:33:29 +0100 Subject: ALSA: hda - Manage current pinctl values in generic parser Use the new pin target accessors for managing the current pinctl values in the generic parser. The pinctl values of all active pins are once determined at the initialization phase, and stored via snd_hda_codec_set_pin_target(). This will be referred again in the codec init or resume phase to set the actual pinctl. This value is kept while the auto-mute. When a line-out or a speaker pin is muted by auto-mute, the driver simply disables the pin, but it doesn't touch the cached pinctl target value. Upon unmute, this value is used to restore the original pinctl in return. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 137 +++++++++++++++++++++++++++----------------- 1 file changed, 85 insertions(+), 52 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index a9bf188fe849..e786f103cb2b 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -83,6 +83,41 @@ void snd_hda_gen_spec_free(struct hda_gen_spec *spec) } EXPORT_SYMBOL_HDA(snd_hda_gen_spec_free); +/* + * pin control value accesses + */ + +#define update_pin_ctl(codec, pin, val) \ + snd_hda_codec_update_cache(codec, pin, 0, \ + AC_VERB_SET_PIN_WIDGET_CONTROL, val) + +/* restore the pinctl based on the cached value */ +static inline void restore_pin_ctl(struct hda_codec *codec, hda_nid_t pin) +{ + update_pin_ctl(codec, pin, snd_hda_codec_get_pin_target(codec, pin)); +} + +/* set the pinctl target value and write it if requested */ +static void set_pin_target(struct hda_codec *codec, hda_nid_t pin, + unsigned int val, bool do_write) +{ + if (!pin) + return; + val = snd_hda_correct_pin_ctl(codec, pin, val); + snd_hda_codec_set_pin_target(codec, pin, val); + if (do_write) + update_pin_ctl(codec, pin, val); +} + +/* set pinctl target values for all given pins */ +static void set_pin_targets(struct hda_codec *codec, int num_pins, + hda_nid_t *pins, unsigned int val) +{ + int i; + for (i = 0; i < num_pins; i++) + set_pin_target(codec, pins[i], val, false); +} + /* * parsing paths */ @@ -1317,6 +1352,15 @@ static int fill_and_eval_dacs(struct hda_codec *codec, spec->multiout.extra_out_nid, spec->speaker_paths); + /* set initial pinctl targets */ + set_pin_targets(codec, cfg->line_outs, cfg->line_out_pins, + cfg->line_out_type == AUTO_PIN_HP_OUT ? PIN_HP : PIN_OUT); + if (cfg->line_out_type != AUTO_PIN_HP_OUT) + set_pin_targets(codec, cfg->hp_outs, cfg->hp_pins, PIN_HP); + if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) + set_pin_targets(codec, cfg->speaker_outs, + cfg->speaker_pins, PIN_OUT); + return badness; } @@ -1715,14 +1759,13 @@ static int set_multi_io(struct hda_codec *codec, int idx, bool output) return 0; if (output) { - snd_hda_set_pin_ctl_cache(codec, nid, PIN_OUT); + set_pin_target(codec, nid, PIN_OUT, true); snd_hda_activate_path(codec, path, true, true); set_pin_eapd(codec, nid, true); } else { set_pin_eapd(codec, nid, false); snd_hda_activate_path(codec, path, false, true); - snd_hda_set_pin_ctl_cache(codec, nid, - spec->multi_io[idx].ctl_in); + set_pin_target(codec, nid, spec->multi_io[idx].ctl_in, true); } return 0; } @@ -1871,7 +1914,7 @@ static void update_shared_mic_hp(struct hda_codec *codec, bool set_as_mic) } val = set_as_mic ? val | PIN_IN : PIN_HP; - snd_hda_set_pin_ctl_cache(codec, pin, val); + set_pin_target(codec, pin, val, true); spec->automute_speaker = !set_as_mic; call_update_outputs(codec); @@ -2126,6 +2169,7 @@ static int create_input_ctls(struct hda_codec *codec) int num_adcs; int i, err, type_idx = 0; const char *prev_label = NULL; + unsigned int val; num_adcs = fill_adc_nids(codec); if (num_adcs < 0) @@ -2146,6 +2190,11 @@ static int create_input_ctls(struct hda_codec *codec) type_idx = 0; prev_label = label; + val = PIN_IN; + if (cfg->inputs[i].type == AUTO_PIN_MIC) + val |= snd_hda_get_default_vref(codec, pin); + set_pin_target(codec, pin, val, false); + if (mixer) { if (is_reachable_path(codec, pin, mixer)) { err = new_analog_input(codec, i, pin, @@ -2611,12 +2660,12 @@ static void parse_digital(struct hda_codec *codec) struct hda_gen_spec *spec = codec->spec; struct nid_path *path; int i, nums; - hda_nid_t dig_nid; + hda_nid_t dig_nid, pin; /* support multiple SPDIFs; the secondary is set up as a slave */ nums = 0; for (i = 0; i < spec->autocfg.dig_outs; i++) { - hda_nid_t pin = spec->autocfg.dig_out_pins[i]; + pin = spec->autocfg.dig_out_pins[i]; dig_nid = look_for_dac(codec, pin, true); if (!dig_nid) continue; @@ -2626,6 +2675,7 @@ static void parse_digital(struct hda_codec *codec) print_nid_path("digout", path); path->active = true; spec->digout_paths[i] = snd_hda_get_path_idx(codec, path); + set_pin_target(codec, pin, PIN_OUT, false); if (!nums) { spec->multiout.dig_out_nid = dig_nid; spec->dig_out_type = spec->autocfg.dig_out_type[0]; @@ -2639,6 +2689,7 @@ static void parse_digital(struct hda_codec *codec) } if (spec->autocfg.dig_in_pin) { + pin = spec->autocfg.dig_in_pin; dig_nid = codec->start_nid; for (i = 0; i < codec->num_nodes; i++, dig_nid++) { unsigned int wcaps = get_wcaps(codec, dig_nid); @@ -2646,14 +2697,13 @@ static void parse_digital(struct hda_codec *codec) continue; if (!(wcaps & AC_WCAP_DIGITAL)) continue; - path = snd_hda_add_new_path(codec, - spec->autocfg.dig_in_pin, - dig_nid, 0); + path = snd_hda_add_new_path(codec, pin, dig_nid, 0); if (path) { print_nid_path("digin", path); path->active = true; spec->dig_in_nid = dig_nid; spec->digin_path = snd_hda_get_path_idx(codec, path); + set_pin_target(codec, pin, PIN_IN, false); break; } } @@ -2730,10 +2780,9 @@ static bool detect_jacks(struct hda_codec *codec, int num_pins, hda_nid_t *pins) /* standard HP/line-out auto-mute helper */ static void do_automute(struct hda_codec *codec, int num_pins, hda_nid_t *pins, - bool mute, bool hp_out) + bool mute) { struct hda_gen_spec *spec = codec->spec; - unsigned int pin_bits = mute ? 0 : (hp_out ? PIN_HP : PIN_OUT); int i; for (i = 0; i < num_pins; i++) { @@ -2744,14 +2793,18 @@ static void do_automute(struct hda_codec *codec, int num_pins, hda_nid_t *pins, /* don't reset VREF value in case it's controlling * the amp (see alc861_fixup_asus_amp_vref_0f()) */ - if (spec->keep_vref_in_automute) { - val = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_PIN_WIDGET_CONTROL, 0); - val &= ~PIN_HP; - } else + if (spec->keep_vref_in_automute) + val = snd_hda_codec_get_pin_target(codec, nid) & ~PIN_HP; + else val = 0; - val |= pin_bits; - snd_hda_set_pin_ctl_cache(codec, nid, val); + if (!mute) + val |= snd_hda_codec_get_pin_target(codec, nid); + /* here we call update_pin_ctl() so that the pinctl is changed + * without changing the pinctl target value; + * the original target value will be still referred at the + * init / resume again + */ + update_pin_ctl(codec, nid, val); set_pin_eapd(codec, nid, !mute); } } @@ -2768,7 +2821,7 @@ void snd_hda_gen_update_outputs(struct hda_codec *codec) */ if (!spec->shared_mic_hp) /* don't change HP-pin when shared with mic */ do_automute(codec, ARRAY_SIZE(spec->autocfg.hp_pins), - spec->autocfg.hp_pins, spec->master_mute, true); + spec->autocfg.hp_pins, spec->master_mute); if (!spec->automute_speaker) on = 0; @@ -2776,7 +2829,7 @@ void snd_hda_gen_update_outputs(struct hda_codec *codec) on = spec->hp_jack_present | spec->line_jack_present; on |= spec->master_mute; do_automute(codec, ARRAY_SIZE(spec->autocfg.speaker_pins), - spec->autocfg.speaker_pins, on, false); + spec->autocfg.speaker_pins, on); /* toggle line-out mutes if needed, too */ /* if LO is a copy of either HP or Speaker, don't need to handle it */ @@ -2789,7 +2842,7 @@ void snd_hda_gen_update_outputs(struct hda_codec *codec) on = spec->hp_jack_present; on |= spec->master_mute; do_automute(codec, ARRAY_SIZE(spec->autocfg.line_out_pins), - spec->autocfg.line_out_pins, on, false); + spec->autocfg.line_out_pins, on); } EXPORT_SYMBOL_HDA(snd_hda_gen_update_outputs); @@ -3806,8 +3859,7 @@ EXPORT_SYMBOL_HDA(snd_hda_gen_build_pcms); */ /* configure the given path as a proper output */ -static void set_output_and_unmute(struct hda_codec *codec, - int pin_type, int path_idx) +static void set_output_and_unmute(struct hda_codec *codec, int path_idx) { struct nid_path *path; hda_nid_t pin; @@ -3816,7 +3868,7 @@ static void set_output_and_unmute(struct hda_codec *codec, if (!path || !path->depth) return; pin = path->path[path->depth - 1]; - snd_hda_set_pin_ctl_cache(codec, pin, pin_type); + restore_pin_ctl(codec, pin); snd_hda_activate_path(codec, path, path->active, true); set_pin_eapd(codec, pin, path->active); } @@ -3825,26 +3877,19 @@ static void set_output_and_unmute(struct hda_codec *codec, static void init_multi_out(struct hda_codec *codec) { struct hda_gen_spec *spec = codec->spec; - int pin_type; int i; - if (spec->autocfg.line_out_type == AUTO_PIN_HP_OUT) - pin_type = PIN_HP; - else - pin_type = PIN_OUT; - for (i = 0; i < spec->autocfg.line_outs; i++) - set_output_and_unmute(codec, pin_type, spec->out_paths[i]); + set_output_and_unmute(codec, spec->out_paths[i]); } -static void __init_extra_out(struct hda_codec *codec, int num_outs, - int *paths, int type) +static void __init_extra_out(struct hda_codec *codec, int num_outs, int *paths) { int i; for (i = 0; i < num_outs; i++) - set_output_and_unmute(codec, type, paths[i]); + set_output_and_unmute(codec, paths[i]); } /* initialize hp and speaker paths */ @@ -3853,11 +3898,10 @@ static void init_extra_out(struct hda_codec *codec) struct hda_gen_spec *spec = codec->spec; if (spec->autocfg.line_out_type != AUTO_PIN_HP_OUT) - __init_extra_out(codec, spec->autocfg.hp_outs, - spec->hp_paths, PIN_HP); + __init_extra_out(codec, spec->autocfg.hp_outs, spec->hp_paths); if (spec->autocfg.line_out_type != AUTO_PIN_SPEAKER_OUT) __init_extra_out(codec, spec->autocfg.speaker_outs, - spec->speaker_paths, PIN_OUT); + spec->speaker_paths); } /* initialize multi-io paths */ @@ -3874,22 +3918,11 @@ static void init_multi_io(struct hda_codec *codec) continue; if (!spec->multi_io[i].ctl_in) spec->multi_io[i].ctl_in = - snd_hda_codec_update_cache(codec, pin, 0, - AC_VERB_GET_PIN_WIDGET_CONTROL, 0); + snd_hda_codec_get_pin_target(codec, pin); snd_hda_activate_path(codec, path, path->active, true); } } -/* set up the input pin config, depending on the given auto-pin type */ -static void set_input_pin(struct hda_codec *codec, hda_nid_t nid, - int auto_pin_type) -{ - unsigned int val = PIN_IN; - if (auto_pin_type == AUTO_PIN_MIC) - val |= snd_hda_get_default_vref(codec, nid); - snd_hda_set_pin_ctl_cache(codec, nid, val); -} - /* set up input pins and loopback paths */ static void init_analog_input(struct hda_codec *codec) { @@ -3900,7 +3933,7 @@ static void init_analog_input(struct hda_codec *codec) for (i = 0; i < cfg->num_inputs; i++) { hda_nid_t nid = cfg->inputs[i].pin; if (is_input_pin(codec, nid)) - set_input_pin(codec, nid, cfg->inputs[i].type); + restore_pin_ctl(codec, nid); /* init loopback inputs */ if (spec->mixer_nid) { @@ -3953,11 +3986,11 @@ static void init_digital(struct hda_codec *codec) hda_nid_t pin; for (i = 0; i < spec->autocfg.dig_outs; i++) - set_output_and_unmute(codec, PIN_OUT, spec->digout_paths[i]); + set_output_and_unmute(codec, spec->digout_paths[i]); pin = spec->autocfg.dig_in_pin; if (pin) { struct nid_path *path; - snd_hda_set_pin_ctl_cache(codec, pin, PIN_IN); + restore_pin_ctl(codec, pin); path = snd_hda_get_path_from_idx(codec, spec->digin_path); if (path) snd_hda_activate_path(codec, path, path->active, false); -- cgit v1.2.3 From 0b4df931ce3502311928bf66088cd76a2b5e604f Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jan 2013 09:45:13 +0100 Subject: ALSA: hda - Avoid auto-mute or auto-mic of retasked jacks When a jack is retasked as a different direction (e.g. a mic jack is used as a CLFE output), such a jack shouldn't be counted as the target for the automatic jack switching. Skip the automute or the autoswitch when the current pinctl direction is different from what we suppose. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index e786f103cb2b..2020faf9e412 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -2773,6 +2773,9 @@ static bool detect_jacks(struct hda_codec *codec, int num_pins, hda_nid_t *pins) hda_nid_t nid = pins[i]; if (!nid) break; + /* don't detect pins retasked as inputs */ + if (snd_hda_codec_get_pin_target(codec, nid) & AC_PINCTL_IN_EN) + continue; present |= snd_hda_jack_detect(codec, nid); } return present; @@ -2899,7 +2902,11 @@ void snd_hda_gen_mic_autoswitch(struct hda_codec *codec, struct hda_jack_tbl *ja return; for (i = spec->am_num_entries - 1; i > 0; i--) { - if (snd_hda_jack_detect(codec, spec->am_entry[i].pin)) { + hda_nid_t pin = spec->am_entry[i].pin; + /* don't detect pins retasked as outputs */ + if (snd_hda_codec_get_pin_target(codec, pin) & AC_PINCTL_OUT_EN) + continue; + if (snd_hda_jack_detect(codec, pin)) { mux_select(codec, 0, spec->am_entry[i].idx); return; } -- cgit v1.2.3 From 1727a771b4ff0fb62cbf32cad3c51493e8a4c167 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jan 2013 09:52:52 +0100 Subject: ALSA: hda/realtek - Drop aliases for old fixups Now the whole codebase has been changed from the earlier kernels, it makes little sense to keep these aliases. Simply replace with the official names. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 521 ++++++++++++++++++++---------------------- 1 file changed, 252 insertions(+), 269 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index bcb258bca99d..183b951fa63b 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -66,23 +66,6 @@ struct alc_customize_define { unsigned int fixup:1; /* Means that this sku is set by driver, not read from hw */ }; -/* make compatible with old code */ -#define alc_apply_pincfgs snd_hda_apply_pincfgs -#define alc_apply_fixup snd_hda_apply_fixup -#define alc_pick_fixup snd_hda_pick_fixup -#define alc_fixup hda_fixup -#define alc_pincfg hda_pintbl -#define alc_model_fixup hda_model_fixup - -#define ALC_FIXUP_PINS HDA_FIXUP_PINS -#define ALC_FIXUP_VERBS HDA_FIXUP_VERBS -#define ALC_FIXUP_FUNC HDA_FIXUP_FUNC - -#define ALC_FIXUP_ACT_PRE_PROBE HDA_FIXUP_ACT_PRE_PROBE -#define ALC_FIXUP_ACT_PROBE HDA_FIXUP_ACT_PROBE -#define ALC_FIXUP_ACT_INIT HDA_FIXUP_ACT_INIT -#define ALC_FIXUP_ACT_BUILD HDA_FIXUP_ACT_BUILD - struct alc_spec { struct hda_gen_spec gen; /* must be at head */ @@ -734,9 +717,9 @@ static int alc_add_inv_dmic_mixer(struct hda_codec *codec, hda_nid_t nid) /* typically the digital mic is put at node 0x12 */ static void alc_fixup_inv_dmic_0x12(struct hda_codec *codec, - const struct alc_fixup *fix, int action) + const struct hda_fixup *fix, int action) { - if (action == ALC_FIXUP_ACT_PROBE) + if (action == HDA_FIXUP_ACT_PROBE) alc_add_inv_dmic_mixer(codec, 0x12); } @@ -782,7 +765,7 @@ static int alc_build_controls(struct hda_codec *codec) } #endif - alc_apply_fixup(codec, ALC_FIXUP_ACT_BUILD); + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_BUILD); return 0; } @@ -803,7 +786,7 @@ static int alc_init(struct hda_codec *codec) snd_hda_gen_init(codec); - alc_apply_fixup(codec, ALC_FIXUP_ACT_INIT); + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_INIT); return 0; } @@ -1050,23 +1033,23 @@ enum { /* enable the volume-knob widget support on NID 0x21 */ static void alc880_fixup_vol_knob(struct hda_codec *codec, - const struct alc_fixup *fix, int action) + const struct hda_fixup *fix, int action) { - if (action == ALC_FIXUP_ACT_PROBE) + if (action == HDA_FIXUP_ACT_PROBE) snd_hda_jack_detect_enable_callback(codec, 0x21, ALC_DCVOL_EVENT, alc_update_knob_master); } -static const struct alc_fixup alc880_fixups[] = { +static const struct hda_fixup alc880_fixups[] = { [ALC880_FIXUP_GPIO1] = { - .type = ALC_FIXUP_VERBS, + .type = HDA_FIXUP_VERBS, .v.verbs = alc_gpio1_init_verbs, }, [ALC880_FIXUP_GPIO2] = { - .type = ALC_FIXUP_VERBS, + .type = HDA_FIXUP_VERBS, .v.verbs = alc_gpio2_init_verbs, }, [ALC880_FIXUP_MEDION_RIM] = { - .type = ALC_FIXUP_VERBS, + .type = HDA_FIXUP_VERBS, .v.verbs = (const struct hda_verb[]) { { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 }, { 0x20, AC_VERB_SET_PROC_COEF, 0x3060 }, @@ -1076,8 +1059,8 @@ static const struct alc_fixup alc880_fixups[] = { .chain_id = ALC880_FIXUP_GPIO2, }, [ALC880_FIXUP_LG] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { /* disable bogus unused pins */ { 0x16, 0x411111f0 }, { 0x18, 0x411111f0 }, @@ -1086,8 +1069,8 @@ static const struct alc_fixup alc880_fixups[] = { } }, [ALC880_FIXUP_W810] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { /* disable bogus unused pins */ { 0x17, 0x411111f0 }, { } @@ -1096,7 +1079,7 @@ static const struct alc_fixup alc880_fixups[] = { .chain_id = ALC880_FIXUP_GPIO2, }, [ALC880_FIXUP_EAPD_COEF] = { - .type = ALC_FIXUP_VERBS, + .type = HDA_FIXUP_VERBS, .v.verbs = (const struct hda_verb[]) { /* change to EAPD mode */ { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 }, @@ -1105,7 +1088,7 @@ static const struct alc_fixup alc880_fixups[] = { }, }, [ALC880_FIXUP_TCL_S700] = { - .type = ALC_FIXUP_VERBS, + .type = HDA_FIXUP_VERBS, .v.verbs = (const struct hda_verb[]) { /* change to EAPD mode */ { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 }, @@ -1116,13 +1099,13 @@ static const struct alc_fixup alc880_fixups[] = { .chain_id = ALC880_FIXUP_GPIO2, }, [ALC880_FIXUP_VOL_KNOB] = { - .type = ALC_FIXUP_FUNC, + .type = HDA_FIXUP_FUNC, .v.func = alc880_fixup_vol_knob, }, [ALC880_FIXUP_FUJITSU] = { /* override all pins as BIOS on old Amilo is broken */ - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { { 0x14, 0x0121411f }, /* HP */ { 0x15, 0x99030120 }, /* speaker */ { 0x16, 0x99030130 }, /* bass speaker */ @@ -1141,8 +1124,8 @@ static const struct alc_fixup alc880_fixups[] = { }, [ALC880_FIXUP_F1734] = { /* almost compatible with FUJITSU, but no bass and SPDIF */ - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { { 0x14, 0x0121411f }, /* HP */ { 0x15, 0x99030120 }, /* speaker */ { 0x16, 0x411111f0 }, /* N/A */ @@ -1161,8 +1144,8 @@ static const struct alc_fixup alc880_fixups[] = { }, [ALC880_FIXUP_UNIWILL] = { /* need to fix HP and speaker pins to be parsed correctly */ - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { { 0x14, 0x0121411f }, /* HP */ { 0x15, 0x99030120 }, /* speaker */ { 0x16, 0x99030130 }, /* bass speaker */ @@ -1170,8 +1153,8 @@ static const struct alc_fixup alc880_fixups[] = { }, }, [ALC880_FIXUP_UNIWILL_DIG] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { /* disable bogus unused pins */ { 0x17, 0x411111f0 }, { 0x19, 0x411111f0 }, @@ -1181,8 +1164,8 @@ static const struct alc_fixup alc880_fixups[] = { } }, [ALC880_FIXUP_Z71V] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { /* set up the whole pins as BIOS is utterly broken */ { 0x14, 0x99030120 }, /* speaker */ { 0x15, 0x0121411f }, /* HP */ @@ -1199,8 +1182,8 @@ static const struct alc_fixup alc880_fixups[] = { } }, [ALC880_FIXUP_3ST_BASE] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { { 0x14, 0x01014010 }, /* line-out */ { 0x15, 0x411111f0 }, /* N/A */ { 0x16, 0x411111f0 }, /* N/A */ @@ -1217,8 +1200,8 @@ static const struct alc_fixup alc880_fixups[] = { } }, [ALC880_FIXUP_3ST] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { { 0x1e, 0x411111f0 }, /* N/A */ { } }, @@ -1226,8 +1209,8 @@ static const struct alc_fixup alc880_fixups[] = { .chain_id = ALC880_FIXUP_3ST_BASE, }, [ALC880_FIXUP_3ST_DIG] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { { 0x1e, 0x0144111e }, /* SPDIF */ { } }, @@ -1235,8 +1218,8 @@ static const struct alc_fixup alc880_fixups[] = { .chain_id = ALC880_FIXUP_3ST_BASE, }, [ALC880_FIXUP_5ST_BASE] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { { 0x14, 0x01014010 }, /* front */ { 0x15, 0x411111f0 }, /* N/A */ { 0x16, 0x01011411 }, /* CLFE */ @@ -1253,8 +1236,8 @@ static const struct alc_fixup alc880_fixups[] = { } }, [ALC880_FIXUP_5ST] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { { 0x1e, 0x411111f0 }, /* N/A */ { } }, @@ -1262,8 +1245,8 @@ static const struct alc_fixup alc880_fixups[] = { .chain_id = ALC880_FIXUP_5ST_BASE, }, [ALC880_FIXUP_5ST_DIG] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { { 0x1e, 0x0144111e }, /* SPDIF */ { } }, @@ -1271,8 +1254,8 @@ static const struct alc_fixup alc880_fixups[] = { .chain_id = ALC880_FIXUP_5ST_BASE, }, [ALC880_FIXUP_6ST_BASE] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { { 0x14, 0x01014010 }, /* front */ { 0x15, 0x01016412 }, /* surr */ { 0x16, 0x01011411 }, /* CLFE */ @@ -1289,8 +1272,8 @@ static const struct alc_fixup alc880_fixups[] = { } }, [ALC880_FIXUP_6ST] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { { 0x1e, 0x411111f0 }, /* N/A */ { } }, @@ -1298,8 +1281,8 @@ static const struct alc_fixup alc880_fixups[] = { .chain_id = ALC880_FIXUP_6ST_BASE, }, [ALC880_FIXUP_6ST_DIG] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { { 0x1e, 0x0144111e }, /* SPDIF */ { } }, @@ -1375,7 +1358,7 @@ static const struct snd_pci_quirk alc880_fixup_tbl[] = { {} }; -static const struct alc_model_fixup alc880_fixup_models[] = { +static const struct hda_model_fixup alc880_fixup_models[] = { {.id = ALC880_FIXUP_3ST, .name = "3stack"}, {.id = ALC880_FIXUP_3ST_DIG, .name = "3stack-digout"}, {.id = ALC880_FIXUP_5ST, .name = "5stack"}, @@ -1401,9 +1384,9 @@ static int patch_alc880(struct hda_codec *codec) spec = codec->spec; spec->gen.need_dac_fix = 1; - alc_pick_fixup(codec, alc880_fixup_models, alc880_fixup_tbl, + snd_hda_pick_fixup(codec, alc880_fixup_models, alc880_fixup_tbl, alc880_fixups); - alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE); + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); /* automatic parse from the BIOS config */ err = alc880_parse_auto_config(codec); @@ -1421,7 +1404,7 @@ static int patch_alc880(struct hda_codec *codec) codec->patch_ops.unsol_event = alc880_unsol_event; - alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE); + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); return 0; @@ -1463,10 +1446,10 @@ static void alc260_gpio1_automute(struct hda_codec *codec) } static void alc260_fixup_gpio1_toggle(struct hda_codec *codec, - const struct alc_fixup *fix, int action) + const struct hda_fixup *fix, int action) { struct alc_spec *spec = codec->spec; - if (action == ALC_FIXUP_ACT_PROBE) { + if (action == HDA_FIXUP_ACT_PROBE) { /* although the machine has only one output pin, we need to * toggle GPIO1 according to the jack state */ @@ -1481,10 +1464,10 @@ static void alc260_fixup_gpio1_toggle(struct hda_codec *codec, } static void alc260_fixup_kn1(struct hda_codec *codec, - const struct alc_fixup *fix, int action) + const struct hda_fixup *fix, int action) { struct alc_spec *spec = codec->spec; - static const struct alc_pincfg pincfgs[] = { + static const struct hda_pintbl pincfgs[] = { { 0x0f, 0x02214000 }, /* HP/speaker */ { 0x12, 0x90a60160 }, /* int mic */ { 0x13, 0x02a19000 }, /* ext mic */ @@ -1501,32 +1484,32 @@ static void alc260_fixup_kn1(struct hda_codec *codec, }; switch (action) { - case ALC_FIXUP_ACT_PRE_PROBE: - alc_apply_pincfgs(codec, pincfgs); + case HDA_FIXUP_ACT_PRE_PROBE: + snd_hda_apply_pincfgs(codec, pincfgs); break; - case ALC_FIXUP_ACT_PROBE: + case HDA_FIXUP_ACT_PROBE: spec->init_amp = ALC_INIT_NONE; break; } } -static const struct alc_fixup alc260_fixups[] = { +static const struct hda_fixup alc260_fixups[] = { [ALC260_FIXUP_HP_DC5750] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { { 0x11, 0x90130110 }, /* speaker */ { } } }, [ALC260_FIXUP_HP_PIN_0F] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { { 0x0f, 0x01214000 }, /* HP */ { } } }, [ALC260_FIXUP_COEF] = { - .type = ALC_FIXUP_VERBS, + .type = HDA_FIXUP_VERBS, .v.verbs = (const struct hda_verb[]) { { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 }, { 0x20, AC_VERB_SET_PROC_COEF, 0x3040 }, @@ -1536,17 +1519,17 @@ static const struct alc_fixup alc260_fixups[] = { .chain_id = ALC260_FIXUP_HP_PIN_0F, }, [ALC260_FIXUP_GPIO1] = { - .type = ALC_FIXUP_VERBS, + .type = HDA_FIXUP_VERBS, .v.verbs = alc_gpio1_init_verbs, }, [ALC260_FIXUP_GPIO1_TOGGLE] = { - .type = ALC_FIXUP_FUNC, + .type = HDA_FIXUP_FUNC, .v.func = alc260_fixup_gpio1_toggle, .chained = true, .chain_id = ALC260_FIXUP_HP_PIN_0F, }, [ALC260_FIXUP_REPLACER] = { - .type = ALC_FIXUP_VERBS, + .type = HDA_FIXUP_VERBS, .v.verbs = (const struct hda_verb[]) { { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 }, { 0x20, AC_VERB_SET_PROC_COEF, 0x3050 }, @@ -1556,13 +1539,13 @@ static const struct alc_fixup alc260_fixups[] = { .chain_id = ALC260_FIXUP_GPIO1_TOGGLE, }, [ALC260_FIXUP_HP_B1900] = { - .type = ALC_FIXUP_FUNC, + .type = HDA_FIXUP_FUNC, .v.func = alc260_fixup_gpio1_toggle, .chained = true, .chain_id = ALC260_FIXUP_COEF, }, [ALC260_FIXUP_KN1] = { - .type = ALC_FIXUP_FUNC, + .type = HDA_FIXUP_FUNC, .v.func = alc260_fixup_kn1, }, }; @@ -1593,8 +1576,8 @@ static int patch_alc260(struct hda_codec *codec) spec = codec->spec; - alc_pick_fixup(codec, NULL, alc260_fixup_tbl, alc260_fixups); - alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE); + snd_hda_pick_fixup(codec, NULL, alc260_fixup_tbl, alc260_fixups); + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); /* automatic parse from the BIOS config */ err = alc260_parse_auto_config(codec); @@ -1611,7 +1594,7 @@ static int patch_alc260(struct hda_codec *codec) codec->patch_ops = alc_patch_ops; spec->shutup = alc_eapd_shutup; - alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE); + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); return 0; @@ -1665,9 +1648,9 @@ enum { }; static void alc889_fixup_coef(struct hda_codec *codec, - const struct alc_fixup *fix, int action) + const struct hda_fixup *fix, int action) { - if (action != ALC_FIXUP_ACT_INIT) + if (action != HDA_FIXUP_ACT_INIT) return; alc889_coef_init(codec); } @@ -1707,9 +1690,9 @@ static void alc882_gpio_mute(struct hda_codec *codec, int pin, int muted) /* set up GPIO at initialization */ static void alc885_fixup_macpro_gpio(struct hda_codec *codec, - const struct alc_fixup *fix, int action) + const struct hda_fixup *fix, int action) { - if (action != ALC_FIXUP_ACT_INIT) + if (action != HDA_FIXUP_ACT_INIT) return; alc882_gpio_mute(codec, 0, 0); alc882_gpio_mute(codec, 1, 0); @@ -1720,9 +1703,9 @@ static void alc885_fixup_macpro_gpio(struct hda_codec *codec, * work correctly (bko#42740) */ static void alc889_fixup_dac_route(struct hda_codec *codec, - const struct alc_fixup *fix, int action) + const struct hda_fixup *fix, int action) { - if (action == ALC_FIXUP_ACT_PRE_PROBE) { + if (action == HDA_FIXUP_ACT_PRE_PROBE) { /* fake the connections during parsing the tree */ hda_nid_t conn1[2] = { 0x0c, 0x0d }; hda_nid_t conn2[2] = { 0x0e, 0x0f }; @@ -1730,7 +1713,7 @@ static void alc889_fixup_dac_route(struct hda_codec *codec, snd_hda_override_conn_list(codec, 0x15, 2, conn1); snd_hda_override_conn_list(codec, 0x18, 2, conn2); snd_hda_override_conn_list(codec, 0x1a, 2, conn2); - } else if (action == ALC_FIXUP_ACT_PROBE) { + } else if (action == HDA_FIXUP_ACT_PROBE) { /* restore the connections */ hda_nid_t conn[5] = { 0x0c, 0x0d, 0x0e, 0x0f, 0x26 }; snd_hda_override_conn_list(codec, 0x14, 5, conn); @@ -1742,13 +1725,13 @@ static void alc889_fixup_dac_route(struct hda_codec *codec, /* Set VREF on HP pin */ static void alc889_fixup_mbp_vref(struct hda_codec *codec, - const struct alc_fixup *fix, int action) + const struct hda_fixup *fix, int action) { struct alc_spec *spec = codec->spec; static hda_nid_t nids[2] = { 0x14, 0x15 }; int i; - if (action != ALC_FIXUP_ACT_INIT) + if (action != HDA_FIXUP_ACT_INIT) return; for (i = 0; i < ARRAY_SIZE(nids); i++) { unsigned int val = snd_hda_codec_get_pincfg(codec, nids[i]); @@ -1765,13 +1748,13 @@ static void alc889_fixup_mbp_vref(struct hda_codec *codec, /* Set VREF on speaker pins on imac91 */ static void alc889_fixup_imac91_vref(struct hda_codec *codec, - const struct alc_fixup *fix, int action) + const struct hda_fixup *fix, int action) { struct alc_spec *spec = codec->spec; static hda_nid_t nids[2] = { 0x18, 0x1a }; int i; - if (action != ALC_FIXUP_ACT_INIT) + if (action != HDA_FIXUP_ACT_INIT) return; for (i = 0; i < ARRAY_SIZE(nids); i++) { unsigned int val; @@ -1787,17 +1770,17 @@ static void alc889_fixup_imac91_vref(struct hda_codec *codec, * strangely, the speaker output doesn't work on VAIO Z through DAC 0x05 */ static void alc882_fixup_no_primary_hp(struct hda_codec *codec, - const struct alc_fixup *fix, int action) + const struct hda_fixup *fix, int action) { struct alc_spec *spec = codec->spec; - if (action == ALC_FIXUP_ACT_PRE_PROBE) + if (action == HDA_FIXUP_ACT_PRE_PROBE) spec->gen.no_primary_hp = 1; } -static const struct alc_fixup alc882_fixups[] = { +static const struct hda_fixup alc882_fixups[] = { [ALC882_FIXUP_ABIT_AW9D_MAX] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { { 0x15, 0x01080104 }, /* side */ { 0x16, 0x01011012 }, /* rear */ { 0x17, 0x01016011 }, /* clfe */ @@ -1805,47 +1788,47 @@ static const struct alc_fixup alc882_fixups[] = { } }, [ALC882_FIXUP_LENOVO_Y530] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { { 0x15, 0x99130112 }, /* rear int speakers */ { 0x16, 0x99130111 }, /* subwoofer */ { } } }, [ALC882_FIXUP_PB_M5210] = { - .type = ALC_FIXUP_VERBS, + .type = HDA_FIXUP_VERBS, .v.verbs = (const struct hda_verb[]) { { 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF50 }, {} } }, [ALC882_FIXUP_ACER_ASPIRE_7736] = { - .type = ALC_FIXUP_FUNC, + .type = HDA_FIXUP_FUNC, .v.func = alc_fixup_sku_ignore, }, [ALC882_FIXUP_ASUS_W90V] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { { 0x16, 0x99130110 }, /* fix sequence for CLFE */ { } } }, [ALC889_FIXUP_CD] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { { 0x1c, 0x993301f0 }, /* CD */ { } } }, [ALC889_FIXUP_VAIO_TT] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { { 0x17, 0x90170111 }, /* hidden surround speaker */ { } } }, [ALC888_FIXUP_EEE1601] = { - .type = ALC_FIXUP_VERBS, + .type = HDA_FIXUP_VERBS, .v.verbs = (const struct hda_verb[]) { { 0x20, AC_VERB_SET_COEF_INDEX, 0x0b }, { 0x20, AC_VERB_SET_PROC_COEF, 0x0838 }, @@ -1853,7 +1836,7 @@ static const struct alc_fixup alc882_fixups[] = { } }, [ALC882_FIXUP_EAPD] = { - .type = ALC_FIXUP_VERBS, + .type = HDA_FIXUP_VERBS, .v.verbs = (const struct hda_verb[]) { /* change to EAPD mode */ { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 }, @@ -1862,7 +1845,7 @@ static const struct alc_fixup alc882_fixups[] = { } }, [ALC883_FIXUP_EAPD] = { - .type = ALC_FIXUP_VERBS, + .type = HDA_FIXUP_VERBS, .v.verbs = (const struct hda_verb[]) { /* change to EAPD mode */ { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 }, @@ -1871,7 +1854,7 @@ static const struct alc_fixup alc882_fixups[] = { } }, [ALC883_FIXUP_ACER_EAPD] = { - .type = ALC_FIXUP_VERBS, + .type = HDA_FIXUP_VERBS, .v.verbs = (const struct hda_verb[]) { /* eanable EAPD on Acer laptops */ { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 }, @@ -1880,30 +1863,30 @@ static const struct alc_fixup alc882_fixups[] = { } }, [ALC882_FIXUP_GPIO1] = { - .type = ALC_FIXUP_VERBS, + .type = HDA_FIXUP_VERBS, .v.verbs = alc_gpio1_init_verbs, }, [ALC882_FIXUP_GPIO2] = { - .type = ALC_FIXUP_VERBS, + .type = HDA_FIXUP_VERBS, .v.verbs = alc_gpio2_init_verbs, }, [ALC882_FIXUP_GPIO3] = { - .type = ALC_FIXUP_VERBS, + .type = HDA_FIXUP_VERBS, .v.verbs = alc_gpio3_init_verbs, }, [ALC882_FIXUP_ASUS_W2JC] = { - .type = ALC_FIXUP_VERBS, + .type = HDA_FIXUP_VERBS, .v.verbs = alc_gpio1_init_verbs, .chained = true, .chain_id = ALC882_FIXUP_EAPD, }, [ALC889_FIXUP_COEF] = { - .type = ALC_FIXUP_FUNC, + .type = HDA_FIXUP_FUNC, .v.func = alc889_fixup_coef, }, [ALC882_FIXUP_ACER_ASPIRE_4930G] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { { 0x16, 0x99130111 }, /* CLFE speaker */ { 0x17, 0x99130112 }, /* surround speaker */ { } @@ -1912,8 +1895,8 @@ static const struct alc_fixup alc882_fixups[] = { .chain_id = ALC882_FIXUP_GPIO1, }, [ALC882_FIXUP_ACER_ASPIRE_8930G] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { { 0x16, 0x99130111 }, /* CLFE speaker */ { 0x1b, 0x99130112 }, /* surround speaker */ { } @@ -1923,7 +1906,7 @@ static const struct alc_fixup alc882_fixups[] = { }, [ALC882_FIXUP_ASPIRE_8930G_VERBS] = { /* additional init verbs for Acer Aspire 8930G */ - .type = ALC_FIXUP_VERBS, + .type = HDA_FIXUP_VERBS, .v.verbs = (const struct hda_verb[]) { /* Enable all DACs */ /* DAC DISABLE/MUTE 1? */ @@ -1957,31 +1940,31 @@ static const struct alc_fixup alc882_fixups[] = { .chain_id = ALC882_FIXUP_GPIO1, }, [ALC885_FIXUP_MACPRO_GPIO] = { - .type = ALC_FIXUP_FUNC, + .type = HDA_FIXUP_FUNC, .v.func = alc885_fixup_macpro_gpio, }, [ALC889_FIXUP_DAC_ROUTE] = { - .type = ALC_FIXUP_FUNC, + .type = HDA_FIXUP_FUNC, .v.func = alc889_fixup_dac_route, }, [ALC889_FIXUP_MBP_VREF] = { - .type = ALC_FIXUP_FUNC, + .type = HDA_FIXUP_FUNC, .v.func = alc889_fixup_mbp_vref, .chained = true, .chain_id = ALC882_FIXUP_GPIO1, }, [ALC889_FIXUP_IMAC91_VREF] = { - .type = ALC_FIXUP_FUNC, + .type = HDA_FIXUP_FUNC, .v.func = alc889_fixup_imac91_vref, .chained = true, .chain_id = ALC882_FIXUP_GPIO1, }, [ALC882_FIXUP_INV_DMIC] = { - .type = ALC_FIXUP_FUNC, + .type = HDA_FIXUP_FUNC, .v.func = alc_fixup_inv_dmic_0x12, }, [ALC882_FIXUP_NO_PRIMARY_HP] = { - .type = ALC_FIXUP_FUNC, + .type = HDA_FIXUP_FUNC, .v.func = alc882_fixup_no_primary_hp, }, }; @@ -2056,7 +2039,7 @@ static const struct snd_pci_quirk alc882_fixup_tbl[] = { {} }; -static const struct alc_model_fixup alc882_fixup_models[] = { +static const struct hda_model_fixup alc882_fixup_models[] = { {.id = ALC882_FIXUP_ACER_ASPIRE_4930G, .name = "acer-aspire-4930g"}, {.id = ALC882_FIXUP_ACER_ASPIRE_8930G, .name = "acer-aspire-8930g"}, {.id = ALC883_FIXUP_ACER_EAPD, .name = "acer-aspire"}, @@ -2099,9 +2082,9 @@ static int patch_alc882(struct hda_codec *codec) break; } - alc_pick_fixup(codec, alc882_fixup_models, alc882_fixup_tbl, + snd_hda_pick_fixup(codec, alc882_fixup_models, alc882_fixup_tbl, alc882_fixups); - alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE); + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); alc_auto_parse_customize_define(codec); @@ -2119,7 +2102,7 @@ static int patch_alc882(struct hda_codec *codec) codec->patch_ops = alc_patch_ops; - alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE); + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); return 0; @@ -2152,10 +2135,10 @@ enum { ALC262_FIXUP_INV_DMIC, }; -static const struct alc_fixup alc262_fixups[] = { +static const struct hda_fixup alc262_fixups[] = { [ALC262_FIXUP_FSC_H270] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { { 0x14, 0x99130110 }, /* speaker */ { 0x15, 0x0221142f }, /* front HP */ { 0x1b, 0x0121141f }, /* rear HP */ @@ -2163,21 +2146,21 @@ static const struct alc_fixup alc262_fixups[] = { } }, [ALC262_FIXUP_HP_Z200] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { { 0x16, 0x99130120 }, /* internal speaker */ { } } }, [ALC262_FIXUP_TYAN] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { { 0x14, 0x1993e1f0 }, /* int AUX */ { } } }, [ALC262_FIXUP_LENOVO_3000] = { - .type = ALC_FIXUP_VERBS, + .type = HDA_FIXUP_VERBS, .v.verbs = (const struct hda_verb[]) { { 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF50 }, {} @@ -2186,7 +2169,7 @@ static const struct alc_fixup alc262_fixups[] = { .chain_id = ALC262_FIXUP_BENQ, }, [ALC262_FIXUP_BENQ] = { - .type = ALC_FIXUP_VERBS, + .type = HDA_FIXUP_VERBS, .v.verbs = (const struct hda_verb[]) { { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 }, { 0x20, AC_VERB_SET_PROC_COEF, 0x3070 }, @@ -2194,7 +2177,7 @@ static const struct alc_fixup alc262_fixups[] = { } }, [ALC262_FIXUP_BENQ_T31] = { - .type = ALC_FIXUP_VERBS, + .type = HDA_FIXUP_VERBS, .v.verbs = (const struct hda_verb[]) { { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 }, { 0x20, AC_VERB_SET_PROC_COEF, 0x3050 }, @@ -2202,7 +2185,7 @@ static const struct alc_fixup alc262_fixups[] = { } }, [ALC262_FIXUP_INV_DMIC] = { - .type = ALC_FIXUP_FUNC, + .type = HDA_FIXUP_FUNC, .v.func = alc_fixup_inv_dmic_0x12, }, }; @@ -2219,7 +2202,7 @@ static const struct snd_pci_quirk alc262_fixup_tbl[] = { {} }; -static const struct alc_model_fixup alc262_fixup_models[] = { +static const struct hda_model_fixup alc262_fixup_models[] = { {.id = ALC262_FIXUP_INV_DMIC, .name = "inv-dmic"}, {} }; @@ -2252,9 +2235,9 @@ static int patch_alc262(struct hda_codec *codec) #endif alc_fix_pll_init(codec, 0x20, 0x0a, 10); - alc_pick_fixup(codec, alc262_fixup_models, alc262_fixup_tbl, + snd_hda_pick_fixup(codec, alc262_fixup_models, alc262_fixup_tbl, alc262_fixups); - alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE); + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); alc_auto_parse_customize_define(codec); @@ -2273,7 +2256,7 @@ static int patch_alc262(struct hda_codec *codec) codec->patch_ops = alc_patch_ops; spec->shutup = alc_eapd_shutup; - alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE); + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); return 0; @@ -2314,13 +2297,13 @@ enum { ALC268_FIXUP_HP_EAPD, }; -static const struct alc_fixup alc268_fixups[] = { +static const struct hda_fixup alc268_fixups[] = { [ALC268_FIXUP_INV_DMIC] = { - .type = ALC_FIXUP_FUNC, + .type = HDA_FIXUP_FUNC, .v.func = alc_fixup_inv_dmic_0x12, }, [ALC268_FIXUP_HP_EAPD] = { - .type = ALC_FIXUP_VERBS, + .type = HDA_FIXUP_VERBS, .v.verbs = (const struct hda_verb[]) { {0x15, AC_VERB_SET_EAPD_BTLENABLE, 0}, {} @@ -2328,7 +2311,7 @@ static const struct alc_fixup alc268_fixups[] = { }, }; -static const struct alc_model_fixup alc268_fixup_models[] = { +static const struct hda_model_fixup alc268_fixup_models[] = { {.id = ALC268_FIXUP_INV_DMIC, .name = "inv-dmic"}, {.id = ALC268_FIXUP_HP_EAPD, .name = "hp-eapd"}, {} @@ -2374,8 +2357,8 @@ static int patch_alc268(struct hda_codec *codec) spec = codec->spec; - alc_pick_fixup(codec, alc268_fixup_models, alc268_fixup_tbl, alc268_fixups); - alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE); + snd_hda_pick_fixup(codec, alc268_fixup_models, alc268_fixup_tbl, alc268_fixups); + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); /* automatic parse from the BIOS config */ err = alc268_parse_auto_config(codec); @@ -2406,7 +2389,7 @@ static int patch_alc268(struct hda_codec *codec) codec->patch_ops = alc_patch_ops; spec->shutup = alc_eapd_shutup; - alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE); + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); return 0; @@ -2564,27 +2547,27 @@ static int alc269_resume(struct hda_codec *codec) #endif /* CONFIG_PM */ static void alc269_fixup_pincfg_no_hp_to_lineout(struct hda_codec *codec, - const struct alc_fixup *fix, int action) + const struct hda_fixup *fix, int action) { struct alc_spec *spec = codec->spec; - if (action == ALC_FIXUP_ACT_PRE_PROBE) + if (action == HDA_FIXUP_ACT_PRE_PROBE) spec->parse_flags = HDA_PINCFG_NO_HP_FIXUP; } static void alc269_fixup_hweq(struct hda_codec *codec, - const struct alc_fixup *fix, int action) + const struct hda_fixup *fix, int action) { int coef; - if (action != ALC_FIXUP_ACT_INIT) + if (action != HDA_FIXUP_ACT_INIT) return; coef = alc_read_coef_idx(codec, 0x1e); alc_write_coef_idx(codec, 0x1e, coef | 0x80); } static void alc271_fixup_dmic(struct hda_codec *codec, - const struct alc_fixup *fix, int action) + const struct hda_fixup *fix, int action) { static const struct hda_verb verbs[] = { {0x20, AC_VERB_SET_COEF_INDEX, 0x0d}, @@ -2601,11 +2584,11 @@ static void alc271_fixup_dmic(struct hda_codec *codec, } static void alc269_fixup_pcm_44k(struct hda_codec *codec, - const struct alc_fixup *fix, int action) + const struct hda_fixup *fix, int action) { struct alc_spec *spec = codec->spec; - if (action != ALC_FIXUP_ACT_PROBE) + if (action != HDA_FIXUP_ACT_PROBE) return; /* Due to a hardware problem on Lenovo Ideadpad, we need to @@ -2616,11 +2599,11 @@ static void alc269_fixup_pcm_44k(struct hda_codec *codec, } static void alc269_fixup_stereo_dmic(struct hda_codec *codec, - const struct alc_fixup *fix, int action) + const struct hda_fixup *fix, int action) { int coef; - if (action != ALC_FIXUP_ACT_INIT) + if (action != HDA_FIXUP_ACT_INIT) return; /* The digital-mic unit sends PDM (differential signal) instead of * the standard PCM, thus you can't record a valid mono stream as is. @@ -2647,10 +2630,10 @@ static void alc269_quanta_automute(struct hda_codec *codec) } static void alc269_fixup_quanta_mute(struct hda_codec *codec, - const struct alc_fixup *fix, int action) + const struct hda_fixup *fix, int action) { struct alc_spec *spec = codec->spec; - if (action != ALC_FIXUP_ACT_PROBE) + if (action != HDA_FIXUP_ACT_PROBE) return; spec->gen.automute_hook = alc269_quanta_automute; } @@ -2665,10 +2648,10 @@ static void alc269_fixup_mic1_mute_hook(void *private_data, int enabled) } static void alc269_fixup_mic1_mute(struct hda_codec *codec, - const struct alc_fixup *fix, int action) + const struct hda_fixup *fix, int action) { struct alc_spec *spec = codec->spec; - if (action == ALC_FIXUP_ACT_PROBE) { + if (action == HDA_FIXUP_ACT_PROBE) { spec->gen.vmaster_mute.hook = alc269_fixup_mic1_mute_hook; spec->gen.vmaster_mute_enum = 1; } @@ -2683,17 +2666,17 @@ static void alc269_fixup_mic2_mute_hook(void *private_data, int enabled) } static void alc269_fixup_mic2_mute(struct hda_codec *codec, - const struct alc_fixup *fix, int action) + const struct hda_fixup *fix, int action) { struct alc_spec *spec = codec->spec; - if (action == ALC_FIXUP_ACT_PROBE) { + if (action == HDA_FIXUP_ACT_PROBE) { spec->gen.vmaster_mute.hook = alc269_fixup_mic2_mute_hook; spec->gen.vmaster_mute_enum = 1; } } static void alc271_hp_gate_mic_jack(struct hda_codec *codec, - const struct alc_fixup *fix, + const struct hda_fixup *fix, int action) { struct alc_spec *spec = codec->spec; @@ -2701,7 +2684,7 @@ static void alc271_hp_gate_mic_jack(struct hda_codec *codec, if (snd_BUG_ON(!spec->gen.am_entry[1].pin || !spec->gen.autocfg.hp_pins[0])) return; - if (action == ALC_FIXUP_ACT_PROBE) + if (action == HDA_FIXUP_ACT_PROBE) snd_hda_jack_set_gating_jack(codec, spec->gen.am_entry[1].pin, spec->gen.autocfg.hp_pins[0]); } @@ -2732,16 +2715,16 @@ enum { ALC271_FIXUP_HP_GATE_MIC_JACK, }; -static const struct alc_fixup alc269_fixups[] = { +static const struct hda_fixup alc269_fixups[] = { [ALC269_FIXUP_SONY_VAIO] = { - .type = ALC_FIXUP_VERBS, + .type = HDA_FIXUP_VERBS, .v.verbs = (const struct hda_verb[]) { {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREFGRD}, {} } }, [ALC275_FIXUP_SONY_VAIO_GPIO2] = { - .type = ALC_FIXUP_VERBS, + .type = HDA_FIXUP_VERBS, .v.verbs = (const struct hda_verb[]) { {0x01, AC_VERB_SET_GPIO_MASK, 0x04}, {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x04}, @@ -2752,7 +2735,7 @@ static const struct alc_fixup alc269_fixups[] = { .chain_id = ALC269_FIXUP_SONY_VAIO }, [ALC269_FIXUP_DELL_M101Z] = { - .type = ALC_FIXUP_VERBS, + .type = HDA_FIXUP_VERBS, .v.verbs = (const struct hda_verb[]) { /* Enables internal speaker */ {0x20, AC_VERB_SET_COEF_INDEX, 13}, @@ -2761,50 +2744,50 @@ static const struct alc_fixup alc269_fixups[] = { } }, [ALC269_FIXUP_SKU_IGNORE] = { - .type = ALC_FIXUP_FUNC, + .type = HDA_FIXUP_FUNC, .v.func = alc_fixup_sku_ignore, }, [ALC269_FIXUP_ASUS_G73JW] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { { 0x17, 0x99130111 }, /* subwoofer */ { } } }, [ALC269_FIXUP_LENOVO_EAPD] = { - .type = ALC_FIXUP_VERBS, + .type = HDA_FIXUP_VERBS, .v.verbs = (const struct hda_verb[]) { {0x14, AC_VERB_SET_EAPD_BTLENABLE, 0}, {} } }, [ALC275_FIXUP_SONY_HWEQ] = { - .type = ALC_FIXUP_FUNC, + .type = HDA_FIXUP_FUNC, .v.func = alc269_fixup_hweq, .chained = true, .chain_id = ALC275_FIXUP_SONY_VAIO_GPIO2 }, [ALC271_FIXUP_DMIC] = { - .type = ALC_FIXUP_FUNC, + .type = HDA_FIXUP_FUNC, .v.func = alc271_fixup_dmic, }, [ALC269_FIXUP_PCM_44K] = { - .type = ALC_FIXUP_FUNC, + .type = HDA_FIXUP_FUNC, .v.func = alc269_fixup_pcm_44k, .chained = true, .chain_id = ALC269_FIXUP_QUANTA_MUTE }, [ALC269_FIXUP_STEREO_DMIC] = { - .type = ALC_FIXUP_FUNC, + .type = HDA_FIXUP_FUNC, .v.func = alc269_fixup_stereo_dmic, }, [ALC269_FIXUP_QUANTA_MUTE] = { - .type = ALC_FIXUP_FUNC, + .type = HDA_FIXUP_FUNC, .v.func = alc269_fixup_quanta_mute, }, [ALC269_FIXUP_LIFEBOOK] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { { 0x1a, 0x2101103f }, /* dock line-out */ { 0x1b, 0x23a11040 }, /* dock mic-in */ { } @@ -2813,8 +2796,8 @@ static const struct alc_fixup alc269_fixups[] = { .chain_id = ALC269_FIXUP_QUANTA_MUTE }, [ALC269_FIXUP_AMIC] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { { 0x14, 0x99130110 }, /* speaker */ { 0x15, 0x0121401f }, /* HP out */ { 0x18, 0x01a19c20 }, /* mic */ @@ -2823,8 +2806,8 @@ static const struct alc_fixup alc269_fixups[] = { }, }, [ALC269_FIXUP_DMIC] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { { 0x12, 0x99a3092f }, /* int-mic */ { 0x14, 0x99130110 }, /* speaker */ { 0x15, 0x0121401f }, /* HP out */ @@ -2833,8 +2816,8 @@ static const struct alc_fixup alc269_fixups[] = { }, }, [ALC269VB_FIXUP_AMIC] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { { 0x14, 0x99130110 }, /* speaker */ { 0x18, 0x01a19c20 }, /* mic */ { 0x19, 0x99a3092f }, /* int-mic */ @@ -2843,8 +2826,8 @@ static const struct alc_fixup alc269_fixups[] = { }, }, [ALC269VB_FIXUP_DMIC] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { { 0x12, 0x99a3092f }, /* int-mic */ { 0x14, 0x99130110 }, /* speaker */ { 0x18, 0x01a19c20 }, /* mic */ @@ -2853,20 +2836,20 @@ static const struct alc_fixup alc269_fixups[] = { }, }, [ALC269_FIXUP_MIC1_MUTE_LED] = { - .type = ALC_FIXUP_FUNC, + .type = HDA_FIXUP_FUNC, .v.func = alc269_fixup_mic1_mute, }, [ALC269_FIXUP_MIC2_MUTE_LED] = { - .type = ALC_FIXUP_FUNC, + .type = HDA_FIXUP_FUNC, .v.func = alc269_fixup_mic2_mute, }, [ALC269_FIXUP_INV_DMIC] = { - .type = ALC_FIXUP_FUNC, + .type = HDA_FIXUP_FUNC, .v.func = alc_fixup_inv_dmic_0x12, }, [ALC269_FIXUP_LENOVO_DOCK] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { { 0x19, 0x23a11040 }, /* dock mic */ { 0x1b, 0x2121103f }, /* dock headphone */ { } @@ -2875,12 +2858,12 @@ static const struct alc_fixup alc269_fixups[] = { .chain_id = ALC269_FIXUP_PINCFG_NO_HP_TO_LINEOUT }, [ALC269_FIXUP_PINCFG_NO_HP_TO_LINEOUT] = { - .type = ALC_FIXUP_FUNC, + .type = HDA_FIXUP_FUNC, .v.func = alc269_fixup_pincfg_no_hp_to_lineout, }, [ALC271_FIXUP_AMIC_MIC2] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { { 0x14, 0x99130110 }, /* speaker */ { 0x19, 0x01a19c20 }, /* mic */ { 0x1b, 0x99a7012f }, /* int-mic */ @@ -2889,7 +2872,7 @@ static const struct alc_fixup alc269_fixups[] = { }, }, [ALC271_FIXUP_HP_GATE_MIC_JACK] = { - .type = ALC_FIXUP_FUNC, + .type = HDA_FIXUP_FUNC, .v.func = alc271_hp_gate_mic_jack, .chained = true, .chain_id = ALC271_FIXUP_AMIC_MIC2, @@ -2982,7 +2965,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { {} }; -static const struct alc_model_fixup alc269_fixup_models[] = { +static const struct hda_model_fixup alc269_fixup_models[] = { {.id = ALC269_FIXUP_AMIC, .name = "laptop-amic"}, {.id = ALC269_FIXUP_DMIC, .name = "laptop-dmic"}, {.id = ALC269_FIXUP_STEREO_DMIC, .name = "alc269-dmic"}, @@ -3051,9 +3034,9 @@ static int patch_alc269(struct hda_codec *codec) spec = codec->spec; spec->gen.shared_mic_vref_pin = 0x18; - alc_pick_fixup(codec, alc269_fixup_models, + snd_hda_pick_fixup(codec, alc269_fixup_models, alc269_fixup_tbl, alc269_fixups); - alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE); + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); alc_auto_parse_customize_define(codec); @@ -3117,7 +3100,7 @@ static int patch_alc269(struct hda_codec *codec) #endif spec->shutup = alc269_shutup; - alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE); + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); return 0; @@ -3147,12 +3130,12 @@ enum { /* On some laptops, VREF of pin 0x0f is abused for controlling the main amp */ static void alc861_fixup_asus_amp_vref_0f(struct hda_codec *codec, - const struct alc_fixup *fix, int action) + const struct hda_fixup *fix, int action) { struct alc_spec *spec = codec->spec; unsigned int val; - if (action != ALC_FIXUP_ACT_INIT) + if (action != HDA_FIXUP_ACT_INIT) return; val = snd_hda_codec_read(codec, 0x0f, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0); @@ -3165,31 +3148,31 @@ static void alc861_fixup_asus_amp_vref_0f(struct hda_codec *codec, /* suppress the jack-detection */ static void alc_fixup_no_jack_detect(struct hda_codec *codec, - const struct alc_fixup *fix, int action) + const struct hda_fixup *fix, int action) { - if (action == ALC_FIXUP_ACT_PRE_PROBE) + if (action == HDA_FIXUP_ACT_PRE_PROBE) codec->no_jack_detect = 1; } -static const struct alc_fixup alc861_fixups[] = { +static const struct hda_fixup alc861_fixups[] = { [ALC861_FIXUP_FSC_AMILO_PI1505] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { { 0x0b, 0x0221101f }, /* HP */ { 0x0f, 0x90170310 }, /* speaker */ { } } }, [ALC861_FIXUP_AMP_VREF_0F] = { - .type = ALC_FIXUP_FUNC, + .type = HDA_FIXUP_FUNC, .v.func = alc861_fixup_asus_amp_vref_0f, }, [ALC861_FIXUP_NO_JACK_DETECT] = { - .type = ALC_FIXUP_FUNC, + .type = HDA_FIXUP_FUNC, .v.func = alc_fixup_no_jack_detect, }, [ALC861_FIXUP_ASUS_A6RP] = { - .type = ALC_FIXUP_FUNC, + .type = HDA_FIXUP_FUNC, .v.func = alc861_fixup_asus_amp_vref_0f, .chained = true, .chain_id = ALC861_FIXUP_NO_JACK_DETECT, @@ -3219,8 +3202,8 @@ static int patch_alc861(struct hda_codec *codec) spec = codec->spec; - alc_pick_fixup(codec, NULL, alc861_fixup_tbl, alc861_fixups); - alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE); + snd_hda_pick_fixup(codec, NULL, alc861_fixup_tbl, alc861_fixups); + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); /* automatic parse from the BIOS config */ err = alc861_parse_auto_config(codec); @@ -3239,7 +3222,7 @@ static int patch_alc861(struct hda_codec *codec) spec->power_hook = alc_power_eapd; #endif - alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE); + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); return 0; @@ -3269,17 +3252,17 @@ enum { /* exclude VREF80 */ static void alc861vd_fixup_dallas(struct hda_codec *codec, - const struct alc_fixup *fix, int action) + const struct hda_fixup *fix, int action) { - if (action == ALC_FIXUP_ACT_PRE_PROBE) { + if (action == HDA_FIXUP_ACT_PRE_PROBE) { snd_hda_override_pin_caps(codec, 0x18, 0x00000734); snd_hda_override_pin_caps(codec, 0x19, 0x0000073c); } } -static const struct alc_fixup alc861vd_fixups[] = { +static const struct hda_fixup alc861vd_fixups[] = { [ALC660VD_FIX_ASUS_GPIO1] = { - .type = ALC_FIXUP_VERBS, + .type = HDA_FIXUP_VERBS, .v.verbs = (const struct hda_verb[]) { /* reset GPIO1 */ {0x01, AC_VERB_SET_GPIO_MASK, 0x03}, @@ -3289,7 +3272,7 @@ static const struct alc_fixup alc861vd_fixups[] = { } }, [ALC861VD_FIX_DALLAS] = { - .type = ALC_FIXUP_FUNC, + .type = HDA_FIXUP_FUNC, .v.func = alc861vd_fixup_dallas, }, }; @@ -3314,8 +3297,8 @@ static int patch_alc861vd(struct hda_codec *codec) spec = codec->spec; - alc_pick_fixup(codec, NULL, alc861vd_fixup_tbl, alc861vd_fixups); - alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE); + snd_hda_pick_fixup(codec, NULL, alc861vd_fixup_tbl, alc861vd_fixups); + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); /* automatic parse from the BIOS config */ err = alc861vd_parse_auto_config(codec); @@ -3333,7 +3316,7 @@ static int patch_alc861vd(struct hda_codec *codec) spec->shutup = alc_eapd_shutup; - alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE); + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); return 0; @@ -3374,9 +3357,9 @@ static int alc662_parse_auto_config(struct hda_codec *codec) } static void alc272_fixup_mario(struct hda_codec *codec, - const struct alc_fixup *fix, int action) + const struct hda_fixup *fix, int action) { - if (action != ALC_FIXUP_ACT_PROBE) + if (action != HDA_FIXUP_ACT_PROBE) return; if (snd_hda_override_amp_caps(codec, 0x2, HDA_OUTPUT, (0x3b << AC_AMPCAP_OFFSET_SHIFT) | @@ -3407,39 +3390,39 @@ enum { ALC662_FIXUP_INV_DMIC, }; -static const struct alc_fixup alc662_fixups[] = { +static const struct hda_fixup alc662_fixups[] = { [ALC662_FIXUP_ASPIRE] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { { 0x15, 0x99130112 }, /* subwoofer */ { } } }, [ALC662_FIXUP_IDEAPAD] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { { 0x17, 0x99130112 }, /* subwoofer */ { } } }, [ALC272_FIXUP_MARIO] = { - .type = ALC_FIXUP_FUNC, + .type = HDA_FIXUP_FUNC, .v.func = alc272_fixup_mario, }, [ALC662_FIXUP_CZC_P10T] = { - .type = ALC_FIXUP_VERBS, + .type = HDA_FIXUP_VERBS, .v.verbs = (const struct hda_verb[]) { {0x14, AC_VERB_SET_EAPD_BTLENABLE, 0}, {} } }, [ALC662_FIXUP_SKU_IGNORE] = { - .type = ALC_FIXUP_FUNC, + .type = HDA_FIXUP_FUNC, .v.func = alc_fixup_sku_ignore, }, [ALC662_FIXUP_HP_RP5800] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { { 0x14, 0x0221201f }, /* HP out */ { } }, @@ -3447,8 +3430,8 @@ static const struct alc_fixup alc662_fixups[] = { .chain_id = ALC662_FIXUP_SKU_IGNORE }, [ALC662_FIXUP_ASUS_MODE1] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { { 0x14, 0x99130110 }, /* speaker */ { 0x18, 0x01a19c20 }, /* mic */ { 0x19, 0x99a3092f }, /* int-mic */ @@ -3459,8 +3442,8 @@ static const struct alc_fixup alc662_fixups[] = { .chain_id = ALC662_FIXUP_SKU_IGNORE }, [ALC662_FIXUP_ASUS_MODE2] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { { 0x14, 0x99130110 }, /* speaker */ { 0x18, 0x01a19820 }, /* mic */ { 0x19, 0x99a3092f }, /* int-mic */ @@ -3471,8 +3454,8 @@ static const struct alc_fixup alc662_fixups[] = { .chain_id = ALC662_FIXUP_SKU_IGNORE }, [ALC662_FIXUP_ASUS_MODE3] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { { 0x14, 0x99130110 }, /* speaker */ { 0x15, 0x0121441f }, /* HP */ { 0x18, 0x01a19840 }, /* mic */ @@ -3484,8 +3467,8 @@ static const struct alc_fixup alc662_fixups[] = { .chain_id = ALC662_FIXUP_SKU_IGNORE }, [ALC662_FIXUP_ASUS_MODE4] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { { 0x14, 0x99130110 }, /* speaker */ { 0x16, 0x99130111 }, /* speaker */ { 0x18, 0x01a19840 }, /* mic */ @@ -3497,8 +3480,8 @@ static const struct alc_fixup alc662_fixups[] = { .chain_id = ALC662_FIXUP_SKU_IGNORE }, [ALC662_FIXUP_ASUS_MODE5] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { { 0x14, 0x99130110 }, /* speaker */ { 0x15, 0x0121441f }, /* HP */ { 0x16, 0x99130111 }, /* speaker */ @@ -3510,8 +3493,8 @@ static const struct alc_fixup alc662_fixups[] = { .chain_id = ALC662_FIXUP_SKU_IGNORE }, [ALC662_FIXUP_ASUS_MODE6] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { { 0x14, 0x99130110 }, /* speaker */ { 0x15, 0x01211420 }, /* HP2 */ { 0x18, 0x01a19840 }, /* mic */ @@ -3523,8 +3506,8 @@ static const struct alc_fixup alc662_fixups[] = { .chain_id = ALC662_FIXUP_SKU_IGNORE }, [ALC662_FIXUP_ASUS_MODE7] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { { 0x14, 0x99130110 }, /* speaker */ { 0x17, 0x99130111 }, /* speaker */ { 0x18, 0x01a19840 }, /* mic */ @@ -3537,8 +3520,8 @@ static const struct alc_fixup alc662_fixups[] = { .chain_id = ALC662_FIXUP_SKU_IGNORE }, [ALC662_FIXUP_ASUS_MODE8] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { { 0x14, 0x99130110 }, /* speaker */ { 0x12, 0x99a30970 }, /* int-mic */ { 0x15, 0x01214020 }, /* HP */ @@ -3551,18 +3534,18 @@ static const struct alc_fixup alc662_fixups[] = { .chain_id = ALC662_FIXUP_SKU_IGNORE }, [ALC662_FIXUP_NO_JACK_DETECT] = { - .type = ALC_FIXUP_FUNC, + .type = HDA_FIXUP_FUNC, .v.func = alc_fixup_no_jack_detect, }, [ALC662_FIXUP_ZOTAC_Z68] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { { 0x1b, 0x02214020 }, /* Front HP */ { } } }, [ALC662_FIXUP_INV_DMIC] = { - .type = ALC_FIXUP_FUNC, + .type = HDA_FIXUP_FUNC, .v.func = alc_fixup_inv_dmic_0x12, }, }; @@ -3642,7 +3625,7 @@ static const struct snd_pci_quirk alc662_fixup_tbl[] = { {} }; -static const struct alc_model_fixup alc662_fixup_models[] = { +static const struct hda_model_fixup alc662_fixup_models[] = { {.id = ALC272_FIXUP_MARIO, .name = "mario"}, {.id = ALC662_FIXUP_ASUS_MODE1, .name = "asus-mode1"}, {.id = ALC662_FIXUP_ASUS_MODE2, .name = "asus-mode2"}, @@ -3703,9 +3686,9 @@ static int patch_alc662(struct hda_codec *codec) spec->init_hook = alc662_fill_coef; alc662_fill_coef(codec); - alc_pick_fixup(codec, alc662_fixup_models, + snd_hda_pick_fixup(codec, alc662_fixup_models, alc662_fixup_tbl, alc662_fixups); - alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE); + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); alc_auto_parse_customize_define(codec); @@ -3743,7 +3726,7 @@ static int patch_alc662(struct hda_codec *codec) codec->patch_ops = alc_patch_ops; spec->shutup = alc_eapd_shutup; - alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE); + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); return 0; -- cgit v1.2.3 From d3f02d60eecfc43088a3cb95d35e0cf75b4b8266 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jan 2013 10:12:22 +0100 Subject: ALSA: hda/realtek - Read the cached pinctl value in fixups ... instead of reading the value from the codec at each time. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 183b951fa63b..7a4b78376303 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -1737,8 +1737,7 @@ static void alc889_fixup_mbp_vref(struct hda_codec *codec, unsigned int val = snd_hda_codec_get_pincfg(codec, nids[i]); if (get_defcfg_device(val) != AC_JACK_HP_OUT) continue; - val = snd_hda_codec_read(codec, nids[i], 0, - AC_VERB_GET_PIN_WIDGET_CONTROL, 0); + val = snd_hda_codec_get_pin_target(codec, nids[i]); val |= AC_PINCTL_VREF_80; snd_hda_set_pin_ctl(codec, nids[i], val); spec->gen.keep_vref_in_automute = 1; @@ -1758,8 +1757,7 @@ static void alc889_fixup_imac91_vref(struct hda_codec *codec, return; for (i = 0; i < ARRAY_SIZE(nids); i++) { unsigned int val; - val = snd_hda_codec_read(codec, nids[i], 0, - AC_VERB_GET_PIN_WIDGET_CONTROL, 0); + val = snd_hda_codec_get_pin_target(codec, nids[i]); val |= AC_PINCTL_VREF_50; snd_hda_set_pin_ctl(codec, nids[i], val); } @@ -3137,8 +3135,7 @@ static void alc861_fixup_asus_amp_vref_0f(struct hda_codec *codec, if (action != HDA_FIXUP_ACT_INIT) return; - val = snd_hda_codec_read(codec, 0x0f, 0, - AC_VERB_GET_PIN_WIDGET_CONTROL, 0); + val = snd_hda_codec_get_pin_target(codec, 0x0f); if (!(val & (AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN))) val |= AC_PINCTL_IN_EN; val |= AC_PINCTL_VREF_50; -- cgit v1.2.3 From fd1082159d1445b0306a4696a2aade251ce2fcb2 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jan 2013 10:18:14 +0100 Subject: ALSA: hda - Add a new fixup type to override pinctl values Add a new fixup type, HDA_FIXUP_PINCTLS, for overriding the pinctl values of the given pins. It takes the same array of struct pintbl like HDA_FIXUP_PINS, but each entry contains the pinctl value instead of the pin default config value. This patch also replaces the corresponding codes in patch_realtek.c. Without this change, the direct call of verbs may be overridden again by the later call of pinctl restoration by the driver. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_auto_parser.c | 15 +++++++++++++++ sound/pci/hda/hda_local.h | 1 + sound/pci/hda/patch_realtek.c | 18 +++++++++--------- 3 files changed, 25 insertions(+), 9 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_auto_parser.c b/sound/pci/hda/hda_auto_parser.c index e5b20219d850..55ed857a7922 100644 --- a/sound/pci/hda/hda_auto_parser.c +++ b/sound/pci/hda/hda_auto_parser.c @@ -655,6 +655,13 @@ void snd_hda_apply_pincfgs(struct hda_codec *codec, } EXPORT_SYMBOL_HDA(snd_hda_apply_pincfgs); +static void set_pin_targets(struct hda_codec *codec, + const struct hda_pintbl *cfg) +{ + for (; cfg->nid; cfg++) + snd_hda_set_pin_ctl_cache(codec, cfg->nid, cfg->val); +} + void snd_hda_apply_fixup(struct hda_codec *codec, int action) { int id = codec->fixup_id; @@ -694,6 +701,14 @@ void snd_hda_apply_fixup(struct hda_codec *codec, int action) codec->chip_name, modelname); fix->v.func(codec, fix, action); break; + case HDA_FIXUP_PINCTLS: + if (action != HDA_FIXUP_ACT_PROBE || !fix->v.pins) + break; + snd_printdd(KERN_INFO SFX + "%s: Apply pinctl for %s\n", + codec->chip_name, modelname); + set_pin_targets(codec, fix->v.pins); + break; default: snd_printk(KERN_ERR SFX "%s: Invalid fixup type %d\n", diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index aa721aa1921a..c09440dd5bfa 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -422,6 +422,7 @@ enum { HDA_FIXUP_PINS, HDA_FIXUP_VERBS, HDA_FIXUP_FUNC, + HDA_FIXUP_PINCTLS, }; /* fixup action definitions */ diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 7a4b78376303..c8fcfa830778 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -1794,9 +1794,9 @@ static const struct hda_fixup alc882_fixups[] = { } }, [ALC882_FIXUP_PB_M5210] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - { 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF50 }, + .type = HDA_FIXUP_PINCTLS, + .v.pins = (const struct hda_pintbl[]) { + { 0x19, PIN_VREF50 }, {} } }, @@ -2158,9 +2158,9 @@ static const struct hda_fixup alc262_fixups[] = { } }, [ALC262_FIXUP_LENOVO_3000] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - { 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF50 }, + .type = HDA_FIXUP_PINCTLS, + .v.pins = (const struct hda_pintbl[]) { + { 0x19, PIN_VREF50 }, {} }, .chained = true, @@ -2715,9 +2715,9 @@ enum { static const struct hda_fixup alc269_fixups[] = { [ALC269_FIXUP_SONY_VAIO] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREFGRD}, + .type = HDA_FIXUP_PINCTLS, + .v.pins = (const struct hda_pintbl[]) { + {0x19, PIN_VREFGRD}, {} } }, -- cgit v1.2.3 From a365fed9806e182cb4e1b7bb1855759489d95858 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jan 2013 16:10:06 +0100 Subject: ALSA: hda - Update automute / automic upon jack retasking When a multi-io jack is switched to another direction, call the automute and autoswitch update functions, as this jack won't be used as the headphone or the mic jack that may turn off others. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 2020faf9e412..fb4d84394b7b 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -1767,6 +1767,12 @@ static int set_multi_io(struct hda_codec *codec, int idx, bool output) snd_hda_activate_path(codec, path, false, true); set_pin_target(codec, nid, spec->multi_io[idx].ctl_in, true); } + + /* update jack retasking in case it modifies any of them */ + snd_hda_gen_hp_automute(codec, NULL); + snd_hda_gen_line_automute(codec, NULL); + snd_hda_gen_mic_autoswitch(codec, NULL); + return 0; } -- cgit v1.2.3 From 978e77e78cff7a85a31ad552ffd8afee319e8721 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jan 2013 16:57:58 +0100 Subject: ALSA: hda - Add output jack mode enum controls Add the enum controls for changing the headphone amp bits of output jacks, such as "Headphone Jack Mode". This feature isn't enabled as default, so far, unless spec->add_out_jack_modes flag is set. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 110 ++++++++++++++++++++++++++++++++++++++++++++ sound/pci/hda/hda_generic.h | 1 + 2 files changed, 111 insertions(+) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index fb4d84394b7b..55b7897444a0 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -1959,6 +1959,101 @@ static int create_shared_input(struct hda_codec *codec) return 0; } +/* + * output jack mode + */ +static int out_jack_mode_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + static const char * const texts[] = { + "Line Out", "Headphone Out", + }; + return snd_hda_enum_helper_info(kcontrol, uinfo, 2, texts); +} + +static int out_jack_mode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + hda_nid_t nid = kcontrol->private_value; + if (snd_hda_codec_get_pin_target(codec, nid) == PIN_HP) + ucontrol->value.enumerated.item[0] = 1; + else + ucontrol->value.enumerated.item[0] = 0; + return 0; +} + +static int out_jack_mode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + hda_nid_t nid = kcontrol->private_value; + unsigned int val; + + val = ucontrol->value.enumerated.item[0] ? PIN_HP : PIN_OUT; + if (snd_hda_codec_get_pin_target(codec, nid) == val) + return 0; + snd_hda_set_pin_ctl_cache(codec, nid, val); + return 1; +} + +static const struct snd_kcontrol_new out_jack_mode_enum = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .info = out_jack_mode_info, + .get = out_jack_mode_get, + .put = out_jack_mode_put, +}; + +static bool find_kctl_name(struct hda_codec *codec, const char *name, int idx) +{ + struct hda_gen_spec *spec = codec->spec; + int i; + + for (i = 0; i < spec->kctls.used; i++) { + struct snd_kcontrol_new *kctl = snd_array_elem(&spec->kctls, i); + if (!strcmp(kctl->name, name) && kctl->index == idx) + return true; + } + return false; +} + +static void get_jack_mode_name(struct hda_codec *codec, hda_nid_t pin, + char *name, size_t name_len) +{ + struct hda_gen_spec *spec = codec->spec; + int idx = 0; + + snd_hda_get_pin_label(codec, pin, &spec->autocfg, name, name_len, &idx); + strlcat(name, " Jack Mode", name_len); + + for (; find_kctl_name(codec, name, idx); idx++) + ; +} + +static int create_out_jack_modes(struct hda_codec *codec, int num_pins, + hda_nid_t *pins) +{ + struct hda_gen_spec *spec = codec->spec; + int i; + + for (i = 0; i < num_pins; i++) { + hda_nid_t pin = pins[i]; + unsigned int pincap = snd_hda_query_pin_caps(codec, pin); + if ((pincap & AC_PINCAP_OUT) && (pincap & AC_PINCAP_HP_DRV)) { + struct snd_kcontrol_new *knew; + char name[44]; + get_jack_mode_name(codec, pin, name, sizeof(name)); + knew = snd_hda_gen_add_kctl(spec, name, + &out_jack_mode_enum); + if (!knew) + return -ENOMEM; + knew->private_value = pin; + } + } + + return 0; +} + /* * Parse input paths @@ -3298,6 +3393,21 @@ int snd_hda_gen_parse_auto_config(struct hda_codec *codec, if (err < 0) return err; + if (spec->add_out_jack_modes) { + if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) { + err = create_out_jack_modes(codec, cfg->line_outs, + cfg->line_out_pins); + if (err < 0) + return err; + } + if (cfg->line_out_type != AUTO_PIN_HP_OUT) { + err = create_out_jack_modes(codec, cfg->hp_outs, + cfg->hp_pins); + if (err < 0) + return err; + } + } + dig_only: parse_digital(codec); diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h index 89683c7fe263..bfa2d973268b 100644 --- a/sound/pci/hda/hda_generic.h +++ b/sound/pci/hda/hda_generic.h @@ -191,6 +191,7 @@ struct hda_gen_spec { unsigned int indep_hp:1; /* independent HP supported */ unsigned int indep_hp_enabled:1; /* independent HP enabled */ unsigned int add_stereo_mix_input:1; /* add aamix as a capture src */ + unsigned int add_out_jack_modes:1; /* add output jack mode enum ctls */ /* loopback mixing mode */ bool aamix_mode; -- cgit v1.2.3 From 39aedee7a1cc3c72d68674ff6ff142299fa0897b Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jan 2013 17:10:40 +0100 Subject: ALSA: hda/realtek - Add a fixup for FSC S7020 laptop Try to recover from the regression: set the HP amp for the speaker and add the hp jack mode enum as default. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index c8fcfa830778..42fc05c3fdf6 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -1436,6 +1436,7 @@ enum { ALC260_FIXUP_REPLACER, ALC260_FIXUP_HP_B1900, ALC260_FIXUP_KN1, + ALC260_FIXUP_FSC_S7020, }; static void alc260_gpio1_automute(struct hda_codec *codec) @@ -1493,6 +1494,17 @@ static void alc260_fixup_kn1(struct hda_codec *codec, } } +static void alc260_fixup_fsc_s7020(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct alc_spec *spec = codec->spec; + + if (action == HDA_FIXUP_ACT_PRE_PROBE) + spec->gen.add_out_jack_modes = 1; + else if (action == HDA_FIXUP_ACT_PROBE) + snd_hda_set_pin_ctl_cache(codec, 0x10, PIN_HP); +} + static const struct hda_fixup alc260_fixups[] = { [ALC260_FIXUP_HP_DC5750] = { .type = HDA_FIXUP_PINS, @@ -1548,6 +1560,10 @@ static const struct hda_fixup alc260_fixups[] = { .type = HDA_FIXUP_FUNC, .v.func = alc260_fixup_kn1, }, + [ALC260_FIXUP_FSC_S7020] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc260_fixup_fsc_s7020, + }, }; static const struct snd_pci_quirk alc260_fixup_tbl[] = { @@ -1556,6 +1572,7 @@ static const struct snd_pci_quirk alc260_fixup_tbl[] = { SND_PCI_QUIRK(0x1025, 0x008f, "Acer", ALC260_FIXUP_GPIO1), SND_PCI_QUIRK(0x103c, 0x280a, "HP dc5750", ALC260_FIXUP_HP_DC5750), SND_PCI_QUIRK(0x103c, 0x30ba, "HP Presario B1900", ALC260_FIXUP_HP_B1900), + SND_PCI_QUIRK(0x10cf, 0x1326, "FSC LifeBook S7020", ALC260_FIXUP_FSC_S7020), SND_PCI_QUIRK(0x1509, 0x4540, "Favorit 100XS", ALC260_FIXUP_GPIO1), SND_PCI_QUIRK(0x152d, 0x0729, "Quanta KN1", ALC260_FIXUP_KN1), SND_PCI_QUIRK(0x161f, 0x2057, "Replacer 672V", ALC260_FIXUP_REPLACER), -- cgit v1.2.3 From 9bb1f06fe0844b742ab97326b34229bc8290c9c6 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jan 2013 17:14:29 +0100 Subject: ALSA: hda/realtek - Fix the timing for some fixups Some fixups such as setting the flags influencing on the parser behavior should be applied before actually parsing the tree. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 42fc05c3fdf6..eb889d2e94ee 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -2666,7 +2666,7 @@ static void alc269_fixup_mic1_mute(struct hda_codec *codec, const struct hda_fixup *fix, int action) { struct alc_spec *spec = codec->spec; - if (action == HDA_FIXUP_ACT_PROBE) { + if (action == HDA_FIXUP_ACT_PRE_PROBE) { spec->gen.vmaster_mute.hook = alc269_fixup_mic1_mute_hook; spec->gen.vmaster_mute_enum = 1; } @@ -2684,7 +2684,7 @@ static void alc269_fixup_mic2_mute(struct hda_codec *codec, const struct hda_fixup *fix, int action) { struct alc_spec *spec = codec->spec; - if (action == HDA_FIXUP_ACT_PROBE) { + if (action == HDA_FIXUP_ACT_PRE_PROBE) { spec->gen.vmaster_mute.hook = alc269_fixup_mic2_mute_hook; spec->gen.vmaster_mute_enum = 1; } @@ -3373,7 +3373,7 @@ static int alc662_parse_auto_config(struct hda_codec *codec) static void alc272_fixup_mario(struct hda_codec *codec, const struct hda_fixup *fix, int action) { - if (action != HDA_FIXUP_ACT_PROBE) + if (action != HDA_FIXUP_ACT_PRE_PROBE) return; if (snd_hda_override_amp_caps(codec, 0x2, HDA_OUTPUT, (0x3b << AC_AMPCAP_OFFSET_SHIFT) | -- cgit v1.2.3 From 08fb0d0ee1b9c7aef79f54a9ae24470621eb6447 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jan 2013 17:33:58 +0100 Subject: ALSA: hda/realtek - Generic mute LED implementation for HP laptops As David Henningsson recently suggested, some HP laptops use an unused mic pin for controlling a mute LED, and this information is provided via DMI string "HP_Mute_LED_X_Y" string. This patch adds the generic support for such cases, as we've already done in patch_sigmatel.c. This is applied generically to all devices with ID 0x103c. But as we don't know whether the device 103c:1586 really contains HP_Mute_LED_X_Y DMI string, still keep the static setup for this device using the mic2 pin 0x19. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 76 +++++++++++++++++++++++++++---------------- 1 file changed, 48 insertions(+), 28 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index eb889d2e94ee..fab31d29fa23 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -82,6 +83,10 @@ struct alc_spec { unsigned int inv_dmic_muted:1; /* R-ch of inv d-mic is muted? */ hda_nid_t inv_dmic_pin; + /* mute LED for HP laptops, see alc269_fixup_mic_mute_hook() */ + int mute_led_polarity; + hda_nid_t mute_led_nid; + /* hooks */ void (*init_hook)(struct hda_codec *codec); #ifdef CONFIG_PM @@ -2653,39 +2658,54 @@ static void alc269_fixup_quanta_mute(struct hda_codec *codec, spec->gen.automute_hook = alc269_quanta_automute; } -/* update mute-LED according to the speaker mute state via mic1 VREF pin */ -static void alc269_fixup_mic1_mute_hook(void *private_data, int enabled) +/* update mute-LED according to the speaker mute state via mic VREF pin */ +static void alc269_fixup_mic_mute_hook(void *private_data, int enabled) { struct hda_codec *codec = private_data; - unsigned int pinval = AC_PINCTL_IN_EN + (enabled ? - AC_PINCTL_VREF_HIZ : AC_PINCTL_VREF_80); - snd_hda_set_pin_ctl_cache(codec, 0x18, pinval); + struct alc_spec *spec = codec->spec; + unsigned int pinval; + + if (spec->mute_led_polarity) + enabled = !enabled; + pinval = AC_PINCTL_IN_EN | + (enabled ? AC_PINCTL_VREF_HIZ : AC_PINCTL_VREF_80); + if (spec->mute_led_nid) + snd_hda_set_pin_ctl_cache(codec, spec->mute_led_nid, pinval); } -static void alc269_fixup_mic1_mute(struct hda_codec *codec, - const struct hda_fixup *fix, int action) +static void alc269_fixup_hp_mute_led(struct hda_codec *codec, + const struct hda_fixup *fix, int action) { struct alc_spec *spec = codec->spec; - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - spec->gen.vmaster_mute.hook = alc269_fixup_mic1_mute_hook; + const struct dmi_device *dev = NULL; + + if (action != HDA_FIXUP_ACT_PRE_PROBE) + return; + + while ((dev = dmi_find_device(DMI_DEV_TYPE_OEM_STRING, NULL, dev))) { + int pol, pin; + if (sscanf(dev->name, "HP_Mute_LED_%d_%x", &pol, &pin) != 2) + continue; + if (pin < 0x0a || pin >= 0x10) + break; + spec->mute_led_polarity = pol; + spec->mute_led_nid = pin - 0x0a + 0x18; + spec->gen.vmaster_mute.hook = alc269_fixup_mic_mute_hook; spec->gen.vmaster_mute_enum = 1; + snd_printd("Detected mute LED for %x:%d\n", spec->mute_led_nid, + spec->mute_led_polarity); + break; } } -/* update mute-LED according to the speaker mute state via mic2 VREF pin */ -static void alc269_fixup_mic2_mute_hook(void *private_data, int enabled) -{ - struct hda_codec *codec = private_data; - unsigned int pinval = enabled ? 0x20 : 0x24; - snd_hda_set_pin_ctl_cache(codec, 0x19, pinval); -} - -static void alc269_fixup_mic2_mute(struct hda_codec *codec, - const struct hda_fixup *fix, int action) +static void alc269_fixup_hp_mute_led_mic2(struct hda_codec *codec, + const struct hda_fixup *fix, int action) { struct alc_spec *spec = codec->spec; if (action == HDA_FIXUP_ACT_PRE_PROBE) { - spec->gen.vmaster_mute.hook = alc269_fixup_mic2_mute_hook; + spec->mute_led_polarity = 0; + spec->mute_led_nid = 0x19; + spec->gen.vmaster_mute.hook = alc269_fixup_mic_mute_hook; spec->gen.vmaster_mute_enum = 1; } } @@ -2721,8 +2741,8 @@ enum { ALC269_FIXUP_DMIC, ALC269VB_FIXUP_AMIC, ALC269VB_FIXUP_DMIC, - ALC269_FIXUP_MIC1_MUTE_LED, - ALC269_FIXUP_MIC2_MUTE_LED, + ALC269_FIXUP_HP_MUTE_LED, + ALC269_FIXUP_HP_MUTE_LED_MIC2, ALC269_FIXUP_INV_DMIC, ALC269_FIXUP_LENOVO_DOCK, ALC269_FIXUP_PINCFG_NO_HP_TO_LINEOUT, @@ -2850,13 +2870,13 @@ static const struct hda_fixup alc269_fixups[] = { { } }, }, - [ALC269_FIXUP_MIC1_MUTE_LED] = { + [ALC269_FIXUP_HP_MUTE_LED] = { .type = HDA_FIXUP_FUNC, - .v.func = alc269_fixup_mic1_mute, + .v.func = alc269_fixup_hp_mute_led, }, - [ALC269_FIXUP_MIC2_MUTE_LED] = { + [ALC269_FIXUP_HP_MUTE_LED_MIC2] = { .type = HDA_FIXUP_FUNC, - .v.func = alc269_fixup_mic2_mute, + .v.func = alc269_fixup_hp_mute_led_mic2, }, [ALC269_FIXUP_INV_DMIC] = { .type = HDA_FIXUP_FUNC, @@ -2897,8 +2917,8 @@ static const struct hda_fixup alc269_fixups[] = { static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1025, 0x029b, "Acer 1810TZ", ALC269_FIXUP_INV_DMIC), SND_PCI_QUIRK(0x1025, 0x0349, "Acer AOD260", ALC269_FIXUP_INV_DMIC), - SND_PCI_QUIRK(0x103c, 0x1586, "HP", ALC269_FIXUP_MIC2_MUTE_LED), - SND_PCI_QUIRK(0x103c, 0x1972, "HP Pavilion 17", ALC269_FIXUP_MIC1_MUTE_LED), + SND_PCI_QUIRK(0x103c, 0x1586, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC2), + SND_PCI_QUIRK_VENDOR(0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED), SND_PCI_QUIRK(0x1043, 0x1427, "Asus Zenbook UX31E", ALC269VB_FIXUP_DMIC), SND_PCI_QUIRK(0x1043, 0x1517, "Asus Zenbook UX31A", ALC269VB_FIXUP_DMIC), SND_PCI_QUIRK(0x1043, 0x1a13, "Asus G73Jw", ALC269_FIXUP_ASUS_G73JW), -- cgit v1.2.3 From 09b70e8509862debff5a033052f8adbd7402fa4e Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jan 2013 18:21:56 +0100 Subject: ALSA: hda - Protect user-defined arrays via mutex The pincfgs, init_verbs and hints set by sysfs or patch might be changed dynamically on the fly, thus we need to protect it. Add a simple protection via a mutex. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_codec.c | 13 ++++++-- sound/pci/hda/hda_codec.h | 1 + sound/pci/hda/hda_hwdep.c | 67 ++++++++++++++++++++++++++++++------------ sound/pci/hda/patch_sigmatel.c | 5 ++++ 4 files changed, 65 insertions(+), 21 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 0a531f2f9255..b28e4031b8a1 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -1086,9 +1086,16 @@ unsigned int snd_hda_codec_get_pincfg(struct hda_codec *codec, hda_nid_t nid) struct hda_pincfg *pin; #ifdef CONFIG_SND_HDA_HWDEP - pin = look_up_pincfg(codec, &codec->user_pins, nid); - if (pin) - return pin->cfg; + { + unsigned int cfg = 0; + mutex_lock(&codec->user_mutex); + pin = look_up_pincfg(codec, &codec->user_pins, nid); + if (pin) + cfg = pin->cfg; + mutex_unlock(&codec->user_mutex); + if (cfg) + return cfg; + } #endif pin = look_up_pincfg(codec, &codec->driver_pins, nid); if (pin) diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index 4c4f1660e654..61085b311059 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -845,6 +845,7 @@ struct hda_codec { struct snd_array cvt_setups; /* audio convert setups */ #ifdef CONFIG_SND_HDA_HWDEP + struct mutex user_mutex; struct snd_hwdep *hwdep; /* assigned hwdep device */ struct snd_array init_verbs; /* additional init verbs */ struct snd_array hints; /* additional hints */ diff --git a/sound/pci/hda/hda_hwdep.c b/sound/pci/hda/hda_hwdep.c index a5c9411bb367..2dddf7fbebcc 100644 --- a/sound/pci/hda/hda_hwdep.c +++ b/sound/pci/hda/hda_hwdep.c @@ -148,6 +148,7 @@ int snd_hda_create_hwdep(struct hda_codec *codec) hwdep->ops.ioctl_compat = hda_hwdep_ioctl_compat; #endif + mutex_init(&codec->user_mutex); snd_array_init(&codec->init_verbs, sizeof(struct hda_verb), 32); snd_array_init(&codec->hints, sizeof(struct hda_hint), 32); snd_array_init(&codec->user_pins, sizeof(struct hda_pincfg), 16); @@ -346,12 +347,14 @@ static ssize_t init_verbs_show(struct device *dev, struct snd_hwdep *hwdep = dev_get_drvdata(dev); struct hda_codec *codec = hwdep->private_data; int i, len = 0; + mutex_lock(&codec->user_mutex); for (i = 0; i < codec->init_verbs.used; i++) { struct hda_verb *v = snd_array_elem(&codec->init_verbs, i); len += snprintf(buf + len, PAGE_SIZE - len, "0x%02x 0x%03x 0x%04x\n", v->nid, v->verb, v->param); } + mutex_unlock(&codec->user_mutex); return len; } @@ -364,12 +367,16 @@ static int parse_init_verbs(struct hda_codec *codec, const char *buf) return -EINVAL; if (!nid || !verb) return -EINVAL; + mutex_lock(&codec->user_mutex); v = snd_array_new(&codec->init_verbs); - if (!v) + if (!v) { + mutex_unlock(&codec->user_mutex); return -ENOMEM; + } v->nid = nid; v->verb = verb; v->param = param; + mutex_unlock(&codec->user_mutex); return 0; } @@ -392,11 +399,13 @@ static ssize_t hints_show(struct device *dev, struct snd_hwdep *hwdep = dev_get_drvdata(dev); struct hda_codec *codec = hwdep->private_data; int i, len = 0; + mutex_lock(&codec->user_mutex); for (i = 0; i < codec->hints.used; i++) { struct hda_hint *hint = snd_array_elem(&codec->hints, i); len += snprintf(buf + len, PAGE_SIZE - len, "%s = %s\n", hint->key, hint->val); } + mutex_unlock(&codec->user_mutex); return len; } @@ -431,6 +440,7 @@ static int parse_hints(struct hda_codec *codec, const char *buf) { char *key, *val; struct hda_hint *hint; + int err = 0; buf = skip_spaces(buf); if (!*buf || *buf == '#' || *buf == '\n') @@ -450,26 +460,31 @@ static int parse_hints(struct hda_codec *codec, const char *buf) val = skip_spaces(val); remove_trail_spaces(key); remove_trail_spaces(val); + mutex_lock(&codec->user_mutex); hint = get_hint(codec, key); if (hint) { /* replace */ kfree(hint->key); hint->key = key; hint->val = val; - return 0; + goto unlock; } /* allocate a new hint entry */ if (codec->hints.used >= MAX_HINTS) hint = NULL; else hint = snd_array_new(&codec->hints); - if (!hint) { - kfree(key); - return -ENOMEM; + if (hint) { + hint->key = key; + hint->val = val; + } else { + err = -ENOMEM; } - hint->key = key; - hint->val = val; - return 0; + unlock: + mutex_unlock(&codec->user_mutex); + if (err) + kfree(key); + return err; } static ssize_t hints_store(struct device *dev, @@ -489,11 +504,13 @@ static ssize_t pin_configs_show(struct hda_codec *codec, char *buf) { int i, len = 0; + mutex_lock(&codec->user_mutex); for (i = 0; i < list->used; i++) { struct hda_pincfg *pin = snd_array_elem(list, i); len += sprintf(buf + len, "0x%02x 0x%08x\n", pin->nid, pin->cfg); } + mutex_unlock(&codec->user_mutex); return len; } @@ -528,13 +545,16 @@ static ssize_t driver_pin_configs_show(struct device *dev, static int parse_user_pin_configs(struct hda_codec *codec, const char *buf) { - int nid, cfg; + int nid, cfg, err; if (sscanf(buf, "%i %i", &nid, &cfg) != 2) return -EINVAL; if (!nid) return -EINVAL; - return snd_hda_add_pincfg(codec, &codec->user_pins, nid, cfg); + mutex_lock(&codec->user_mutex); + err = snd_hda_add_pincfg(codec, &codec->user_pins, nid, cfg); + mutex_unlock(&codec->user_mutex); + return err; } static ssize_t user_pin_configs_store(struct device *dev, @@ -600,16 +620,27 @@ EXPORT_SYMBOL_HDA(snd_hda_get_hint); int snd_hda_get_bool_hint(struct hda_codec *codec, const char *key) { - const char *p = snd_hda_get_hint(codec, key); + const char *p; + int ret; + + mutex_lock(&codec->user_mutex); + p = snd_hda_get_hint(codec, key); if (!p || !*p) - return -ENOENT; - switch (toupper(*p)) { - case 'T': /* true */ - case 'Y': /* yes */ - case '1': - return 1; + ret = -ENOENT; + else { + switch (toupper(*p)) { + case 'T': /* true */ + case 'Y': /* yes */ + case '1': + ret = 1; + break; + default: + ret = 0; + break; + } } - return 0; + mutex_unlock(&codec->user_mutex); + return ret; } EXPORT_SYMBOL_HDA(snd_hda_get_bool_hint); diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index a86547ca17c8..d3a81f10fe5c 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -4298,15 +4298,20 @@ static void stac_toggle_power_map(struct hda_codec *codec, hda_nid_t nid, static inline int get_int_hint(struct hda_codec *codec, const char *key, int *valp) { +#ifdef CONFIG_SND_HDA_RECONFIG const char *p; + mutex_lock(&codec->user_mutex); p = snd_hda_get_hint(codec, key); if (p) { unsigned long val; if (!strict_strtoul(p, 0, &val)) { *valp = val; + mutex_unlock(&codec->user_mutex); return 1; } } + mutex_unlock(&codec->user_mutex); +#endif return 0; } -- cgit v1.2.3 From bc759721fb44bc07e4f82445cc378a9d2724651f Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 11 Jan 2013 17:40:31 +0100 Subject: ALSA: hda - Add snd_hda_get_int_hint() helper function It'll be used in hda_generic.c, too. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_hwdep.c | 20 ++++++++++++++++++++ sound/pci/hda/hda_local.h | 7 +++++++ sound/pci/hda/patch_sigmatel.c | 22 ++++------------------ 3 files changed, 31 insertions(+), 18 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_hwdep.c b/sound/pci/hda/hda_hwdep.c index 2dddf7fbebcc..ce67608734b5 100644 --- a/sound/pci/hda/hda_hwdep.c +++ b/sound/pci/hda/hda_hwdep.c @@ -644,6 +644,26 @@ int snd_hda_get_bool_hint(struct hda_codec *codec, const char *key) } EXPORT_SYMBOL_HDA(snd_hda_get_bool_hint); +int snd_hda_get_int_hint(struct hda_codec *codec, const char *key, int *valp) +{ + const char *p; + unsigned long val; + int ret; + + mutex_lock(&codec->user_mutex); + p = snd_hda_get_hint(codec, key); + if (!p) + ret = -ENOENT; + else if (strict_strtoul(p, 0, &val)) + ret = -EINVAL; + else { + *valp = val; + ret = 0; + } + mutex_unlock(&codec->user_mutex); + return ret; +} +EXPORT_SYMBOL_HDA(snd_hda_get_int_hint); #endif /* CONFIG_SND_HDA_RECONFIG */ #ifdef CONFIG_SND_HDA_PATCH_LOADER diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index c09440dd5bfa..9e6353aafb1a 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -618,6 +618,7 @@ static inline int snd_hda_hwdep_add_sysfs(struct hda_codec *codec) #ifdef CONFIG_SND_HDA_RECONFIG const char *snd_hda_get_hint(struct hda_codec *codec, const char *key); int snd_hda_get_bool_hint(struct hda_codec *codec, const char *key); +int snd_hda_get_int_hint(struct hda_codec *codec, const char *key, int *valp); #else static inline const char *snd_hda_get_hint(struct hda_codec *codec, const char *key) @@ -630,6 +631,12 @@ int snd_hda_get_bool_hint(struct hda_codec *codec, const char *key) { return -ENOENT; } + +static inline +int snd_hda_get_int_hint(struct hda_codec *codec, const char *key, int *valp) +{ + return -ENOENT; +} #endif /* diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index d3a81f10fe5c..9cc4cb9b4bd2 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -4295,24 +4295,10 @@ static void stac92xx_power_down(struct hda_codec *codec) static void stac_toggle_power_map(struct hda_codec *codec, hda_nid_t nid, int enable); -static inline int get_int_hint(struct hda_codec *codec, const char *key, - int *valp) -{ -#ifdef CONFIG_SND_HDA_RECONFIG - const char *p; - mutex_lock(&codec->user_mutex); - p = snd_hda_get_hint(codec, key); - if (p) { - unsigned long val; - if (!strict_strtoul(p, 0, &val)) { - *valp = val; - mutex_unlock(&codec->user_mutex); - return 1; - } - } - mutex_unlock(&codec->user_mutex); -#endif - return 0; +static inline bool get_int_hint(struct hda_codec *codec, const char *key, + int *valp) +{ + return !snd_hda_get_int_hint(codec, key, valp); } /* override some hints from the hwdep entry */ -- cgit v1.2.3 From 1c70a583417e8db1e1d5069d7651ba294e9499de Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 11 Jan 2013 17:48:22 +0100 Subject: ALSA: hda - Allow user to give hints for codec parser behavior Through the hints via sysfs or patch, user can set specific behavior flags for the generic parser now. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_auto_parser.c | 3 ++ sound/pci/hda/hda_generic.c | 70 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_auto_parser.c b/sound/pci/hda/hda_auto_parser.c index 55ed857a7922..33b3ece224c6 100644 --- a/sound/pci/hda/hda_auto_parser.c +++ b/sound/pci/hda/hda_auto_parser.c @@ -126,6 +126,9 @@ int snd_hda_parse_pin_defcfg(struct hda_codec *codec, struct auto_out_pin hp_out[ARRAY_SIZE(cfg->hp_pins)]; int i; + if (!snd_hda_get_int_hint(codec, "parser_flags", &i)) + cond_flags = i; + memset(cfg, 0, sizeof(*cfg)); memset(line_out, 0, sizeof(line_out)); diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 55b7897444a0..4bc4cd933866 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -83,6 +83,74 @@ void snd_hda_gen_spec_free(struct hda_gen_spec *spec) } EXPORT_SYMBOL_HDA(snd_hda_gen_spec_free); +/* + * store user hints + */ +static void parse_user_hints(struct hda_codec *codec) +{ + struct hda_gen_spec *spec = codec->spec; + int val; + + val = snd_hda_get_bool_hint(codec, "jack_detect"); + if (val >= 0) + codec->no_jack_detect = !val; + val = snd_hda_get_bool_hint(codec, "inv_jack_detect"); + if (val >= 0) + codec->inv_jack_detect = !!val; + val = snd_hda_get_bool_hint(codec, "trigger_sense"); + if (val >= 0) + codec->no_trigger_sense = !val; + val = snd_hda_get_bool_hint(codec, "inv_eapd"); + if (val >= 0) + codec->inv_eapd = !!val; + val = snd_hda_get_bool_hint(codec, "pcm_format_first"); + if (val >= 0) + codec->pcm_format_first = !!val; + val = snd_hda_get_bool_hint(codec, "sticky_stream"); + if (val >= 0) + codec->no_sticky_stream = !val; + val = snd_hda_get_bool_hint(codec, "spdif_status_reset"); + if (val >= 0) + codec->spdif_status_reset = !!val; + val = snd_hda_get_bool_hint(codec, "pin_amp_workaround"); + if (val >= 0) + codec->pin_amp_workaround = !!val; + val = snd_hda_get_bool_hint(codec, "single_adc_amp"); + if (val >= 0) + codec->single_adc_amp = !!val; + + val = snd_hda_get_bool_hint(codec, "auto_mic"); + if (val >= 0) + spec->suppress_auto_mic = !val; + val = snd_hda_get_bool_hint(codec, "line_in_auto_switch"); + if (val >= 0) + spec->line_in_auto_switch = !!val; + val = snd_hda_get_bool_hint(codec, "need_dac_fix"); + if (val >= 0) + spec->need_dac_fix = !!val; + val = snd_hda_get_bool_hint(codec, "primary_hp"); + if (val >= 0) + spec->no_primary_hp = !val; + val = snd_hda_get_bool_hint(codec, "multi_cap_vol"); + if (val >= 0) + spec->multi_cap_vol = !!val; + val = snd_hda_get_bool_hint(codec, "inv_dmic_split"); + if (val >= 0) + spec->inv_dmic_split = !!val; + val = snd_hda_get_bool_hint(codec, "indep_hp"); + if (val >= 0) + spec->indep_hp = !!val; + val = snd_hda_get_bool_hint(codec, "add_stereo_mix_input"); + if (val >= 0) + spec->add_stereo_mix_input = !!val; + val = snd_hda_get_bool_hint(codec, "add_out_jack_modes"); + if (val >= 0) + spec->add_out_jack_modes = !!val; + + if (!snd_hda_get_int_hint(codec, "mixer_nid", &val)) + spec->mixer_nid = val; +} + /* * pin control value accesses */ @@ -3304,6 +3372,8 @@ int snd_hda_gen_parse_auto_config(struct hda_codec *codec, struct hda_gen_spec *spec = codec->spec; int err; + parse_user_hints(codec); + if (cfg != &spec->autocfg) { spec->autocfg = *cfg; cfg = &spec->autocfg; -- cgit v1.2.3 From 84721e81fa3513a21ecebb4c5e892ed82648a6d5 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 19 Dec 2012 17:37:12 +0100 Subject: ALSA: hda - Remove superfluous kconfig depends Signed-off-by: Takashi Iwai --- sound/pci/hda/Kconfig | 3 --- 1 file changed, 3 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig index ebec1b707fb6..30eb4c3790c6 100644 --- a/sound/pci/hda/Kconfig +++ b/sound/pci/hda/Kconfig @@ -148,7 +148,6 @@ config SND_HDA_CODEC_HDMI config SND_HDA_CODEC_CIRRUS bool "Build Cirrus Logic codec support" - depends on SND_HDA_INTEL default y help Say Y here to include Cirrus Logic codec support in @@ -173,7 +172,6 @@ config SND_HDA_CODEC_CONEXANT config SND_HDA_CODEC_CA0110 bool "Build Creative CA0110-IBG codec support" - depends on SND_HDA_INTEL default y help Say Y here to include Creative CA0110-IBG codec support in @@ -186,7 +184,6 @@ config SND_HDA_CODEC_CA0110 config SND_HDA_CODEC_CA0132 bool "Build Creative CA0132 codec support" - depends on SND_HDA_INTEL default y help Say Y here to include Creative CA0132 codec support in -- cgit v1.2.3 From b060fb0eef743b1e23d00754fdea8bedf40c2a09 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 19 Dec 2012 17:35:47 +0100 Subject: ALSA: hda - Use generic codec parser for C-Media codecs Replace the old parser code for C-Media auto-parser with the latest generic parser. For compatibility reason, the static bindings are still left, but they could be cleaned up in future. Signed-off-by: Takashi Iwai --- sound/pci/hda/Kconfig | 1 + sound/pci/hda/patch_cmedia.c | 164 ++++++++++++------------------------------- 2 files changed, 47 insertions(+), 118 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig index 30eb4c3790c6..8136c4c5b0ce 100644 --- a/sound/pci/hda/Kconfig +++ b/sound/pci/hda/Kconfig @@ -197,6 +197,7 @@ config SND_HDA_CODEC_CA0132 config SND_HDA_CODEC_CMEDIA bool "Build C-Media HD-audio codec support" default y + select SND_HDA_GENERIC help Say Y here to include C-Media HD-audio codec support in snd-hda-intel driver, such as CMI9880. diff --git a/sound/pci/hda/patch_cmedia.c b/sound/pci/hda/patch_cmedia.c index c8fdaaefe702..04dd3b6b7db5 100644 --- a/sound/pci/hda/patch_cmedia.c +++ b/sound/pci/hda/patch_cmedia.c @@ -30,6 +30,9 @@ #include "hda_codec.h" #include "hda_local.h" #include "hda_auto_parser.h" +#include "hda_jack.h" +#include "hda_generic.h" + #define NUM_PINS 11 @@ -45,6 +48,10 @@ enum { }; struct cmi_spec { + struct hda_gen_spec gen; + + /* below are only for static models */ + int board_config; unsigned int no_line_in: 1; /* no line-in (5-jack) */ unsigned int front_panel: 1; /* has front-panel 2-jack */ @@ -356,77 +363,6 @@ static int cmi9880_build_controls(struct hda_codec *codec) return 0; } -/* fill in the multi_dac_nids table, which will decide - which audio widget to use for each channel */ -static int cmi9880_fill_multi_dac_nids(struct hda_codec *codec, const struct auto_pin_cfg *cfg) -{ - struct cmi_spec *spec = codec->spec; - hda_nid_t nid; - int assigned[4]; - int i, j; - - /* clear the table, only one c-media dac assumed here */ - memset(spec->dac_nids, 0, sizeof(spec->dac_nids)); - memset(assigned, 0, sizeof(assigned)); - /* check the pins we found */ - for (i = 0; i < cfg->line_outs; i++) { - nid = cfg->line_out_pins[i]; - /* nid 0x0b~0x0e is hardwired to audio widget 0x3~0x6 */ - if (nid >= 0x0b && nid <= 0x0e) { - spec->dac_nids[i] = (nid - 0x0b) + 0x03; - assigned[nid - 0x0b] = 1; - } - } - /* left pin can be connect to any audio widget */ - for (i = 0; i < cfg->line_outs; i++) { - nid = cfg->line_out_pins[i]; - if (nid <= 0x0e) - continue; - /* search for an empty channel */ - for (j = 0; j < cfg->line_outs; j++) { - if (! assigned[j]) { - spec->dac_nids[i] = j + 0x03; - assigned[j] = 1; - break; - } - } - } - spec->num_dacs = cfg->line_outs; - return 0; -} - -/* create multi_init table, which is used for multichannel initialization */ -static int cmi9880_fill_multi_init(struct hda_codec *codec, const struct auto_pin_cfg *cfg) -{ - struct cmi_spec *spec = codec->spec; - hda_nid_t nid; - int i, j, k; - - /* clear the table, only one c-media dac assumed here */ - memset(spec->multi_init, 0, sizeof(spec->multi_init)); - for (j = 0, i = 0; i < cfg->line_outs; i++) { - nid = cfg->line_out_pins[i]; - /* set as output */ - spec->multi_init[j].nid = nid; - spec->multi_init[j].verb = AC_VERB_SET_PIN_WIDGET_CONTROL; - spec->multi_init[j].param = PIN_OUT; - j++; - if (nid > 0x0e) { - /* set connection */ - spec->multi_init[j].nid = nid; - spec->multi_init[j].verb = AC_VERB_SET_CONNECT_SEL; - spec->multi_init[j].param = 0; - /* find the index in connect list */ - k = snd_hda_get_conn_index(codec, nid, - spec->dac_nids[i], 0); - if (k >= 0) - spec->multi_init[j].param = k; - j++; - } - } - return 0; -} - static int cmi9880_init(struct hda_codec *codec) { struct cmi_spec *spec = codec->spec; @@ -632,6 +568,35 @@ static const struct hda_codec_ops cmi9880_patch_ops = { .free = cmi9880_free, }; +/* + * stuff for auto-parser + */ +static const struct hda_codec_ops cmi_auto_patch_ops = { + .build_controls = snd_hda_gen_build_controls, + .build_pcms = snd_hda_gen_build_pcms, + .init = snd_hda_gen_init, + .free = snd_hda_gen_free, +}; + +static int cmi_parse_auto_config(struct hda_codec *codec) +{ + struct cmi_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->gen.autocfg; + int err; + + snd_hda_gen_spec_init(&spec->gen); + + err = snd_hda_parse_pin_defcfg(codec, cfg, NULL, 0); + if (err < 0) + return err; + err = snd_hda_gen_parse_auto_config(codec, cfg); + if (err < 0) + return err; + + codec->patch_ops = cmi_auto_patch_ops; + return 0; +} + static int patch_cmi9880(struct hda_codec *codec) { struct cmi_spec *spec; @@ -650,6 +615,15 @@ static int patch_cmi9880(struct hda_codec *codec) spec->board_config = CMI_AUTO; /* try everything */ } + if (spec->board_config == CMI_AUTO) { + int err = cmi_parse_auto_config(codec); + if (err < 0) { + snd_hda_gen_free(codec); + return err; + } + return 0; + } + /* copy default DAC NIDs */ memcpy(spec->dac_nids, cmi9880_dac_nids, sizeof(spec->dac_nids)); spec->num_dacs = 4; @@ -678,59 +652,13 @@ static int patch_cmi9880(struct hda_codec *codec) } break; case CMI_ALLOUT: + default: spec->front_panel = 1; spec->multiout.max_channels = 8; spec->no_line_in = 1; spec->input_mux = &cmi9880_no_line_mux; spec->multiout.dig_out_nid = CMI_DIG_OUT_NID; break; - case CMI_AUTO: - { - unsigned int port_e, port_f, port_g, port_h; - unsigned int port_spdifi, port_spdifo; - struct auto_pin_cfg cfg; - - /* collect pin default configuration */ - port_e = snd_hda_codec_get_pincfg(codec, 0x0f); - port_f = snd_hda_codec_get_pincfg(codec, 0x10); - spec->front_panel = 1; - if (get_defcfg_connect(port_e) == AC_JACK_PORT_NONE || - get_defcfg_connect(port_f) == AC_JACK_PORT_NONE) { - port_g = snd_hda_codec_get_pincfg(codec, 0x1f); - port_h = snd_hda_codec_get_pincfg(codec, 0x20); - spec->channel_modes = cmi9880_channel_modes; - /* no front panel */ - if (get_defcfg_connect(port_g) == AC_JACK_PORT_NONE || - get_defcfg_connect(port_h) == AC_JACK_PORT_NONE) { - /* no optional rear panel */ - spec->board_config = CMI_MINIMAL; - spec->front_panel = 0; - spec->num_channel_modes = 2; - } else { - spec->board_config = CMI_MIN_FP; - spec->num_channel_modes = 3; - } - spec->input_mux = &cmi9880_basic_mux; - spec->multiout.max_channels = cmi9880_channel_modes[0].channels; - } else { - spec->input_mux = &cmi9880_basic_mux; - port_spdifi = snd_hda_codec_get_pincfg(codec, 0x13); - port_spdifo = snd_hda_codec_get_pincfg(codec, 0x12); - if (get_defcfg_connect(port_spdifo) != AC_JACK_PORT_NONE) - spec->multiout.dig_out_nid = CMI_DIG_OUT_NID; - if (get_defcfg_connect(port_spdifi) != AC_JACK_PORT_NONE) - spec->dig_in_nid = CMI_DIG_IN_NID; - spec->multiout.max_channels = 8; - } - snd_hda_parse_pin_def_config(codec, &cfg, NULL); - if (cfg.line_outs) { - spec->multiout.max_channels = cfg.line_outs * 2; - cmi9880_fill_multi_dac_nids(codec, &cfg); - cmi9880_fill_multi_init(codec, &cfg); - } else - snd_printd("patch_cmedia: cannot detect association in defcfg\n"); - break; - } } spec->multiout.num_dacs = spec->num_dacs; -- cgit v1.2.3 From 8fadf1da3f370dacbeb4c30fd015a6d2cc47f2fa Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 19 Dec 2012 18:04:37 +0100 Subject: ALSA: hda - Use generic parser for CA0110 codec CA0110 codec is a fairly straightforward hardware implementation, and we can use the generic parser almost as is. Just set spec->multi_cap_vol flag to follow the current behavior. Signed-off-by: Takashi Iwai --- sound/pci/hda/Kconfig | 1 + sound/pci/hda/patch_ca0110.c | 488 ++----------------------------------------- 2 files changed, 16 insertions(+), 473 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig index 8136c4c5b0ce..9aff5cfafe2b 100644 --- a/sound/pci/hda/Kconfig +++ b/sound/pci/hda/Kconfig @@ -173,6 +173,7 @@ config SND_HDA_CODEC_CONEXANT config SND_HDA_CODEC_CA0110 bool "Build Creative CA0110-IBG codec support" default y + select SND_HDA_GENERIC help Say Y here to include Creative CA0110-IBG codec support in snd-hda-intel driver, found on some Creative X-Fi cards. diff --git a/sound/pci/hda/patch_ca0110.c b/sound/pci/hda/patch_ca0110.c index 19ae14f739cb..8d093254c204 100644 --- a/sound/pci/hda/patch_ca0110.c +++ b/sound/pci/hda/patch_ca0110.c @@ -27,502 +27,45 @@ #include "hda_codec.h" #include "hda_local.h" #include "hda_auto_parser.h" +#include "hda_jack.h" +#include "hda_generic.h" -/* - */ - -struct ca0110_spec { - struct auto_pin_cfg autocfg; - struct hda_multi_out multiout; - hda_nid_t out_pins[AUTO_CFG_MAX_OUTS]; - hda_nid_t dacs[AUTO_CFG_MAX_OUTS]; - hda_nid_t hp_dac; - hda_nid_t input_pins[AUTO_PIN_LAST]; - hda_nid_t adcs[AUTO_PIN_LAST]; - hda_nid_t dig_out; - hda_nid_t dig_in; - unsigned int num_inputs; - char input_labels[AUTO_PIN_LAST][32]; - struct hda_pcm pcm_rec[2]; /* PCM information */ -}; - -/* - * PCM callbacks - */ -static int ca0110_playback_pcm_open(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct ca0110_spec *spec = codec->spec; - return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream, - hinfo); -} - -static int ca0110_playback_pcm_prepare(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - unsigned int stream_tag, - unsigned int format, - struct snd_pcm_substream *substream) -{ - struct ca0110_spec *spec = codec->spec; - return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, - stream_tag, format, substream); -} - -static int ca0110_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct ca0110_spec *spec = codec->spec; - return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout); -} - -/* - * Digital out - */ -static int ca0110_dig_playback_pcm_open(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct ca0110_spec *spec = codec->spec; - return snd_hda_multi_out_dig_open(codec, &spec->multiout); -} - -static int ca0110_dig_playback_pcm_close(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct ca0110_spec *spec = codec->spec; - return snd_hda_multi_out_dig_close(codec, &spec->multiout); -} - -static int ca0110_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - unsigned int stream_tag, - unsigned int format, - struct snd_pcm_substream *substream) -{ - struct ca0110_spec *spec = codec->spec; - return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag, - format, substream); -} - -/* - * Analog capture - */ -static int ca0110_capture_pcm_prepare(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - unsigned int stream_tag, - unsigned int format, - struct snd_pcm_substream *substream) -{ - struct ca0110_spec *spec = codec->spec; - - snd_hda_codec_setup_stream(codec, spec->adcs[substream->number], - stream_tag, 0, format); - return 0; -} - -static int ca0110_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct ca0110_spec *spec = codec->spec; - - snd_hda_codec_cleanup_stream(codec, spec->adcs[substream->number]); - return 0; -} - -/* - */ - -static const char * const dirstr[2] = { "Playback", "Capture" }; - -static int _add_switch(struct hda_codec *codec, hda_nid_t nid, const char *pfx, - int chan, int dir) -{ - char namestr[44]; - int type = dir ? HDA_INPUT : HDA_OUTPUT; - struct snd_kcontrol_new knew = - HDA_CODEC_MUTE_MONO(namestr, nid, chan, 0, type); - sprintf(namestr, "%s %s Switch", pfx, dirstr[dir]); - return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec)); -} - -static int _add_volume(struct hda_codec *codec, hda_nid_t nid, const char *pfx, - int chan, int dir) -{ - char namestr[44]; - int type = dir ? HDA_INPUT : HDA_OUTPUT; - struct snd_kcontrol_new knew = - HDA_CODEC_VOLUME_MONO(namestr, nid, chan, 0, type); - sprintf(namestr, "%s %s Volume", pfx, dirstr[dir]); - return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec)); -} - -#define add_out_switch(codec, nid, pfx) _add_switch(codec, nid, pfx, 3, 0) -#define add_out_volume(codec, nid, pfx) _add_volume(codec, nid, pfx, 3, 0) -#define add_in_switch(codec, nid, pfx) _add_switch(codec, nid, pfx, 3, 1) -#define add_in_volume(codec, nid, pfx) _add_volume(codec, nid, pfx, 3, 1) -#define add_mono_switch(codec, nid, pfx, chan) \ - _add_switch(codec, nid, pfx, chan, 0) -#define add_mono_volume(codec, nid, pfx, chan) \ - _add_volume(codec, nid, pfx, chan, 0) - -static int ca0110_build_controls(struct hda_codec *codec) -{ - struct ca0110_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - static const char * const prefix[AUTO_CFG_MAX_OUTS] = { - "Front", "Surround", NULL, "Side", "Multi" - }; - hda_nid_t mutenid; - int i, err; - - for (i = 0; i < spec->multiout.num_dacs; i++) { - if (get_wcaps(codec, spec->out_pins[i]) & AC_WCAP_OUT_AMP) - mutenid = spec->out_pins[i]; - else - mutenid = spec->multiout.dac_nids[i]; - if (!prefix[i]) { - err = add_mono_switch(codec, mutenid, - "Center", 1); - if (err < 0) - return err; - err = add_mono_switch(codec, mutenid, - "LFE", 1); - if (err < 0) - return err; - err = add_mono_volume(codec, spec->multiout.dac_nids[i], - "Center", 1); - if (err < 0) - return err; - err = add_mono_volume(codec, spec->multiout.dac_nids[i], - "LFE", 1); - if (err < 0) - return err; - } else { - err = add_out_switch(codec, mutenid, - prefix[i]); - if (err < 0) - return err; - err = add_out_volume(codec, spec->multiout.dac_nids[i], - prefix[i]); - if (err < 0) - return err; - } - } - if (cfg->hp_outs) { - if (get_wcaps(codec, cfg->hp_pins[0]) & AC_WCAP_OUT_AMP) - mutenid = cfg->hp_pins[0]; - else - mutenid = spec->multiout.dac_nids[i]; - - err = add_out_switch(codec, mutenid, "Headphone"); - if (err < 0) - return err; - if (spec->hp_dac) { - err = add_out_volume(codec, spec->hp_dac, "Headphone"); - if (err < 0) - return err; - } - } - for (i = 0; i < spec->num_inputs; i++) { - const char *label = spec->input_labels[i]; - if (get_wcaps(codec, spec->input_pins[i]) & AC_WCAP_IN_AMP) - mutenid = spec->input_pins[i]; - else - mutenid = spec->adcs[i]; - err = add_in_switch(codec, mutenid, label); - if (err < 0) - return err; - err = add_in_volume(codec, spec->adcs[i], label); - if (err < 0) - return err; - } - - if (spec->dig_out) { - err = snd_hda_create_spdif_out_ctls(codec, spec->dig_out, - spec->dig_out); - if (err < 0) - return err; - err = snd_hda_create_spdif_share_sw(codec, &spec->multiout); - if (err < 0) - return err; - spec->multiout.share_spdif = 1; - } - if (spec->dig_in) { - err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in); - if (err < 0) - return err; - err = add_in_volume(codec, spec->dig_in, "IEC958"); - } - return 0; -} - -/* - */ -static const struct hda_pcm_stream ca0110_pcm_analog_playback = { - .substreams = 1, - .channels_min = 2, - .channels_max = 8, - .ops = { - .open = ca0110_playback_pcm_open, - .prepare = ca0110_playback_pcm_prepare, - .cleanup = ca0110_playback_pcm_cleanup - }, -}; - -static const struct hda_pcm_stream ca0110_pcm_analog_capture = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, - .ops = { - .prepare = ca0110_capture_pcm_prepare, - .cleanup = ca0110_capture_pcm_cleanup - }, -}; - -static const struct hda_pcm_stream ca0110_pcm_digital_playback = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, - .ops = { - .open = ca0110_dig_playback_pcm_open, - .close = ca0110_dig_playback_pcm_close, - .prepare = ca0110_dig_playback_pcm_prepare - }, -}; - -static const struct hda_pcm_stream ca0110_pcm_digital_capture = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, -}; - -static int ca0110_build_pcms(struct hda_codec *codec) -{ - struct ca0110_spec *spec = codec->spec; - struct hda_pcm *info = spec->pcm_rec; - - codec->pcm_info = info; - codec->num_pcms = 0; - - info->name = "CA0110 Analog"; - info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ca0110_pcm_analog_playback; - info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->dacs[0]; - info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = - spec->multiout.max_channels; - info->stream[SNDRV_PCM_STREAM_CAPTURE] = ca0110_pcm_analog_capture; - info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = spec->num_inputs; - info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adcs[0]; - codec->num_pcms++; - - if (!spec->dig_out && !spec->dig_in) - return 0; - - info++; - info->name = "CA0110 Digital"; - info->pcm_type = HDA_PCM_TYPE_SPDIF; - if (spec->dig_out) { - info->stream[SNDRV_PCM_STREAM_PLAYBACK] = - ca0110_pcm_digital_playback; - info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->dig_out; - } - if (spec->dig_in) { - info->stream[SNDRV_PCM_STREAM_CAPTURE] = - ca0110_pcm_digital_capture; - info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in; - } - codec->num_pcms++; - - return 0; -} - -static void init_output(struct hda_codec *codec, hda_nid_t pin, hda_nid_t dac) -{ - if (pin) { - snd_hda_set_pin_ctl(codec, pin, PIN_HP); - if (get_wcaps(codec, pin) & AC_WCAP_OUT_AMP) - snd_hda_codec_write(codec, pin, 0, - AC_VERB_SET_AMP_GAIN_MUTE, - AMP_OUT_UNMUTE); - } - if (dac) - snd_hda_codec_write(codec, dac, 0, - AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO); -} - -static void init_input(struct hda_codec *codec, hda_nid_t pin, hda_nid_t adc) -{ - if (pin) { - snd_hda_set_pin_ctl(codec, pin, PIN_IN | - snd_hda_get_default_vref(codec, pin)); - if (get_wcaps(codec, pin) & AC_WCAP_IN_AMP) - snd_hda_codec_write(codec, pin, 0, - AC_VERB_SET_AMP_GAIN_MUTE, - AMP_IN_UNMUTE(0)); - } - if (adc) - snd_hda_codec_write(codec, adc, 0, AC_VERB_SET_AMP_GAIN_MUTE, - AMP_IN_UNMUTE(0)); -} - -static int ca0110_init(struct hda_codec *codec) -{ - struct ca0110_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - int i; - - for (i = 0; i < spec->multiout.num_dacs; i++) - init_output(codec, spec->out_pins[i], - spec->multiout.dac_nids[i]); - init_output(codec, cfg->hp_pins[0], spec->hp_dac); - init_output(codec, cfg->dig_out_pins[0], spec->dig_out); - - for (i = 0; i < spec->num_inputs; i++) - init_input(codec, spec->input_pins[i], spec->adcs[i]); - init_input(codec, cfg->dig_in_pin, spec->dig_in); - return 0; -} - -static void ca0110_free(struct hda_codec *codec) -{ - kfree(codec->spec); -} static const struct hda_codec_ops ca0110_patch_ops = { - .build_controls = ca0110_build_controls, - .build_pcms = ca0110_build_pcms, - .init = ca0110_init, - .free = ca0110_free, + .build_controls = snd_hda_gen_build_controls, + .build_pcms = snd_hda_gen_build_pcms, + .init = snd_hda_gen_init, + .free = snd_hda_gen_free, }; - -static void parse_line_outs(struct hda_codec *codec) -{ - struct ca0110_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - int i, n; - unsigned int def_conf; - hda_nid_t nid; - - n = 0; - for (i = 0; i < cfg->line_outs; i++) { - nid = cfg->line_out_pins[i]; - def_conf = snd_hda_codec_get_pincfg(codec, nid); - if (!def_conf) - continue; /* invalid pin */ - if (snd_hda_get_connections(codec, nid, &spec->dacs[i], 1) != 1) - continue; - spec->out_pins[n++] = nid; - } - spec->multiout.dac_nids = spec->dacs; - spec->multiout.num_dacs = n; - spec->multiout.max_channels = n * 2; -} - -static void parse_hp_out(struct hda_codec *codec) -{ - struct ca0110_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - int i; - unsigned int def_conf; - hda_nid_t nid, dac; - - if (!cfg->hp_outs) - return; - nid = cfg->hp_pins[0]; - def_conf = snd_hda_codec_get_pincfg(codec, nid); - if (!def_conf) { - cfg->hp_outs = 0; - return; - } - if (snd_hda_get_connections(codec, nid, &dac, 1) != 1) - return; - - for (i = 0; i < cfg->line_outs; i++) - if (dac == spec->dacs[i]) - break; - if (i >= cfg->line_outs) { - spec->hp_dac = dac; - spec->multiout.hp_nid = dac; - } -} - -static void parse_input(struct hda_codec *codec) -{ - struct ca0110_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - hda_nid_t nid, pin; - int n, i, j; - - n = 0; - nid = codec->start_nid; - for (i = 0; i < codec->num_nodes; i++, nid++) { - unsigned int wcaps = get_wcaps(codec, nid); - unsigned int type = get_wcaps_type(wcaps); - if (type != AC_WID_AUD_IN) - continue; - if (snd_hda_get_connections(codec, nid, &pin, 1) != 1) - continue; - if (pin == cfg->dig_in_pin) { - spec->dig_in = nid; - continue; - } - for (j = 0; j < cfg->num_inputs; j++) - if (cfg->inputs[j].pin == pin) - break; - if (j >= cfg->num_inputs) - continue; - spec->input_pins[n] = pin; - snd_hda_get_pin_label(codec, pin, cfg, - spec->input_labels[n], - sizeof(spec->input_labels[n]), NULL); - spec->adcs[n] = nid; - n++; - } - spec->num_inputs = n; -} - -static void parse_digital(struct hda_codec *codec) -{ - struct ca0110_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - - if (cfg->dig_outs && - snd_hda_get_connections(codec, cfg->dig_out_pins[0], - &spec->dig_out, 1) == 1) - spec->multiout.dig_out_nid = spec->dig_out; -} - static int ca0110_parse_auto_config(struct hda_codec *codec) { - struct ca0110_spec *spec = codec->spec; + struct hda_gen_spec *spec = codec->spec; int err; - err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); + err = snd_hda_parse_pin_defcfg(codec, &spec->autocfg, NULL, 0); + if (err < 0) + return err; + err = snd_hda_gen_parse_auto_config(codec, &spec->autocfg); if (err < 0) return err; - parse_line_outs(codec); - parse_hp_out(codec); - parse_digital(codec); - parse_input(codec); return 0; } static int patch_ca0110(struct hda_codec *codec) { - struct ca0110_spec *spec; + struct hda_gen_spec *spec; int err; spec = kzalloc(sizeof(*spec), GFP_KERNEL); if (!spec) return -ENOMEM; + snd_hda_gen_spec_init(spec); codec->spec = spec; + spec->multi_cap_vol = 1; codec->bus->needs_damn_long_delay = 1; err = ca0110_parse_auto_config(codec); @@ -534,8 +77,7 @@ static int patch_ca0110(struct hda_codec *codec) return 0; error: - kfree(codec->spec); - codec->spec = NULL; + snd_hda_gen_free(codec); return err; } -- cgit v1.2.3 From 1077a024812d3b2d76a7a371df75795a276d9dd8 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 19 Dec 2012 16:39:18 +0100 Subject: ALSA: hda - Use generic parser for Cirrus codec driver This time, the target is Cirrus codec. Its parser is a subset of generic parser, so we can migrate fully with it now. The only tricky part is the handling of SPDIF automute. Cirrus driver sets the SPDIF out plug over the headphone. As a workaround, set spec->gen.master_mute for toggling the headphone (and other) mute. Signed-off-by: Takashi Iwai --- sound/pci/hda/Kconfig | 1 + sound/pci/hda/patch_cirrus.c | 1323 ++++-------------------------------------- 2 files changed, 117 insertions(+), 1207 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig index 9aff5cfafe2b..07025a9339ef 100644 --- a/sound/pci/hda/Kconfig +++ b/sound/pci/hda/Kconfig @@ -149,6 +149,7 @@ config SND_HDA_CODEC_HDMI config SND_HDA_CODEC_CIRRUS bool "Build Cirrus Logic codec support" default y + select SND_HDA_GENERIC help Say Y here to include Cirrus Logic codec support in snd-hda-intel driver, such as CS4206. diff --git a/sound/pci/hda/patch_cirrus.c b/sound/pci/hda/patch_cirrus.c index 7b0b8c305737..b9dfbd85d550 100644 --- a/sound/pci/hda/patch_cirrus.c +++ b/sound/pci/hda/patch_cirrus.c @@ -24,37 +24,18 @@ #include #include #include +#include #include "hda_codec.h" #include "hda_local.h" #include "hda_auto_parser.h" #include "hda_jack.h" -#include +#include "hda_generic.h" /* */ struct cs_spec { - struct auto_pin_cfg autocfg; - struct hda_multi_out multiout; - struct snd_kcontrol *vmaster_sw; - struct snd_kcontrol *vmaster_vol; - - hda_nid_t dac_nid[AUTO_CFG_MAX_OUTS]; - hda_nid_t slave_dig_outs[2]; - - unsigned int input_idx[AUTO_PIN_LAST]; - unsigned int capsrc_idx[AUTO_PIN_LAST]; - hda_nid_t adc_nid[AUTO_PIN_LAST]; - unsigned int adc_idx[AUTO_PIN_LAST]; - unsigned int num_inputs; - unsigned int cur_input; - unsigned int automic_idx; - hda_nid_t cur_adc; - unsigned int cur_adc_stream_tag; - unsigned int cur_adc_format; - hda_nid_t dig_in; - - const struct hda_bind_ctls *capture_bind[2]; + struct hda_gen_spec gen; unsigned int gpio_mask; unsigned int gpio_dir; @@ -62,17 +43,11 @@ struct cs_spec { unsigned int gpio_eapd_hp; /* EAPD GPIO bit for headphones */ unsigned int gpio_eapd_speaker; /* EAPD GPIO bit for speakers */ - struct hda_pcm pcm_rec[2]; /* PCM information */ - - unsigned int hp_detect:1; - unsigned int mic_detect:1; - unsigned int speaker_2_1:1; /* CS421x */ unsigned int spdif_detect:1; + unsigned int spdif_present:1; unsigned int sense_b:1; hda_nid_t vendor_nid; - struct hda_input_mux input_mux; - unsigned int last_input; }; /* available models with CS420x */ @@ -148,756 +123,34 @@ enum { #define CS421X_DMIC_PIN_NID 0x09 /* Port E */ #define CS421X_SPDIF_PIN_NID 0x0A /* Port H */ -#define CS421X_IDX_DEV_CFG 0x01 -#define CS421X_IDX_ADC_CFG 0x02 -#define CS421X_IDX_DAC_CFG 0x03 -#define CS421X_IDX_SPK_CTL 0x04 - -#define SPDIF_EVENT 0x04 - -/* Cirrus Logic CS4213 is like CS4210 but does not have SPDIF input/output */ -#define CS4213_VENDOR_NID 0x09 - - -static inline int cs_vendor_coef_get(struct hda_codec *codec, unsigned int idx) -{ - struct cs_spec *spec = codec->spec; - snd_hda_codec_write(codec, spec->vendor_nid, 0, - AC_VERB_SET_COEF_INDEX, idx); - return snd_hda_codec_read(codec, spec->vendor_nid, 0, - AC_VERB_GET_PROC_COEF, 0); -} - -static inline void cs_vendor_coef_set(struct hda_codec *codec, unsigned int idx, - unsigned int coef) -{ - struct cs_spec *spec = codec->spec; - snd_hda_codec_write(codec, spec->vendor_nid, 0, - AC_VERB_SET_COEF_INDEX, idx); - snd_hda_codec_write(codec, spec->vendor_nid, 0, - AC_VERB_SET_PROC_COEF, coef); -} - - -#define HP_EVENT 1 -#define MIC_EVENT 2 - -/* - * PCM callbacks - */ -static int cs_playback_pcm_open(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct cs_spec *spec = codec->spec; - return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream, - hinfo); -} - -static int cs_playback_pcm_prepare(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - unsigned int stream_tag, - unsigned int format, - struct snd_pcm_substream *substream) -{ - struct cs_spec *spec = codec->spec; - return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, - stream_tag, format, substream); -} - -static int cs_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct cs_spec *spec = codec->spec; - return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout); -} - -/* - * Digital out - */ -static int cs_dig_playback_pcm_open(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct cs_spec *spec = codec->spec; - return snd_hda_multi_out_dig_open(codec, &spec->multiout); -} - -static int cs_dig_playback_pcm_close(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct cs_spec *spec = codec->spec; - return snd_hda_multi_out_dig_close(codec, &spec->multiout); -} - -static int cs_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - unsigned int stream_tag, - unsigned int format, - struct snd_pcm_substream *substream) -{ - struct cs_spec *spec = codec->spec; - return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag, - format, substream); -} - -static int cs_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct cs_spec *spec = codec->spec; - return snd_hda_multi_out_dig_cleanup(codec, &spec->multiout); -} - -static void cs_update_input_select(struct hda_codec *codec) -{ - struct cs_spec *spec = codec->spec; - if (spec->cur_adc) - snd_hda_codec_write(codec, spec->cur_adc, 0, - AC_VERB_SET_CONNECT_SEL, - spec->adc_idx[spec->cur_input]); -} - -/* - * Analog capture - */ -static int cs_capture_pcm_prepare(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - unsigned int stream_tag, - unsigned int format, - struct snd_pcm_substream *substream) -{ - struct cs_spec *spec = codec->spec; - spec->cur_adc = spec->adc_nid[spec->cur_input]; - spec->cur_adc_stream_tag = stream_tag; - spec->cur_adc_format = format; - cs_update_input_select(codec); - snd_hda_codec_setup_stream(codec, spec->cur_adc, stream_tag, 0, format); - return 0; -} - -static int cs_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct cs_spec *spec = codec->spec; - snd_hda_codec_cleanup_stream(codec, spec->cur_adc); - spec->cur_adc = 0; - return 0; -} - -/* - */ -static const struct hda_pcm_stream cs_pcm_analog_playback = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, - .ops = { - .open = cs_playback_pcm_open, - .prepare = cs_playback_pcm_prepare, - .cleanup = cs_playback_pcm_cleanup - }, -}; - -static const struct hda_pcm_stream cs_pcm_analog_capture = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, - .ops = { - .prepare = cs_capture_pcm_prepare, - .cleanup = cs_capture_pcm_cleanup - }, -}; - -static const struct hda_pcm_stream cs_pcm_digital_playback = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, - .ops = { - .open = cs_dig_playback_pcm_open, - .close = cs_dig_playback_pcm_close, - .prepare = cs_dig_playback_pcm_prepare, - .cleanup = cs_dig_playback_pcm_cleanup - }, -}; - -static const struct hda_pcm_stream cs_pcm_digital_capture = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, -}; - -static int cs_build_pcms(struct hda_codec *codec) -{ - struct cs_spec *spec = codec->spec; - struct hda_pcm *info = spec->pcm_rec; - - codec->pcm_info = info; - codec->num_pcms = 0; - - info->name = "Cirrus Analog"; - info->stream[SNDRV_PCM_STREAM_PLAYBACK] = cs_pcm_analog_playback; - info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->dac_nid[0]; - info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = - spec->multiout.max_channels; - if (spec->speaker_2_1) - info->stream[SNDRV_PCM_STREAM_PLAYBACK].chmap = - snd_pcm_2_1_chmaps; - info->stream[SNDRV_PCM_STREAM_CAPTURE] = cs_pcm_analog_capture; - info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = - spec->adc_nid[spec->cur_input]; - codec->num_pcms++; - - if (!spec->multiout.dig_out_nid && !spec->dig_in) - return 0; - - info++; - info->name = "Cirrus Digital"; - info->pcm_type = spec->autocfg.dig_out_type[0]; - if (!info->pcm_type) - info->pcm_type = HDA_PCM_TYPE_SPDIF; - if (spec->multiout.dig_out_nid) { - info->stream[SNDRV_PCM_STREAM_PLAYBACK] = - cs_pcm_digital_playback; - info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = - spec->multiout.dig_out_nid; - } - if (spec->dig_in) { - info->stream[SNDRV_PCM_STREAM_CAPTURE] = - cs_pcm_digital_capture; - info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in; - } - codec->num_pcms++; - - return 0; -} - -/* - * parse codec topology - */ - -static hda_nid_t get_dac(struct hda_codec *codec, hda_nid_t pin) -{ - hda_nid_t dac; - if (!pin) - return 0; - if (snd_hda_get_connections(codec, pin, &dac, 1) != 1) - return 0; - return dac; -} - -static int is_ext_mic(struct hda_codec *codec, unsigned int idx) -{ - struct cs_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - hda_nid_t pin = cfg->inputs[idx].pin; - unsigned int val; - if (!is_jack_detectable(codec, pin)) - return 0; - val = snd_hda_codec_get_pincfg(codec, pin); - return (snd_hda_get_input_pin_attr(val) != INPUT_PIN_ATTR_INT); -} - -static hda_nid_t get_adc(struct hda_codec *codec, hda_nid_t pin, - unsigned int *idxp) -{ - int i, idx; - hda_nid_t nid; - - nid = codec->start_nid; - for (i = 0; i < codec->num_nodes; i++, nid++) { - unsigned int type; - type = get_wcaps_type(get_wcaps(codec, nid)); - if (type != AC_WID_AUD_IN) - continue; - idx = snd_hda_get_conn_index(codec, nid, pin, false); - if (idx >= 0) { - *idxp = idx; - return nid; - } - } - return 0; -} - -static int is_active_pin(struct hda_codec *codec, hda_nid_t nid) -{ - unsigned int val; - val = snd_hda_codec_get_pincfg(codec, nid); - return (get_defcfg_connect(val) != AC_JACK_PORT_NONE); -} - -static int parse_output(struct hda_codec *codec) -{ - struct cs_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - int i, extra_nids; - hda_nid_t dac; - - for (i = 0; i < cfg->line_outs; i++) { - dac = get_dac(codec, cfg->line_out_pins[i]); - if (!dac) - break; - spec->dac_nid[i] = dac; - } - spec->multiout.num_dacs = i; - spec->multiout.dac_nids = spec->dac_nid; - spec->multiout.max_channels = i * 2; - - if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT && i == 2) - spec->speaker_2_1 = 1; /* assume 2.1 speakers */ - - /* add HP and speakers */ - extra_nids = 0; - for (i = 0; i < cfg->hp_outs; i++) { - dac = get_dac(codec, cfg->hp_pins[i]); - if (!dac) - break; - if (!i) - spec->multiout.hp_nid = dac; - else - spec->multiout.extra_out_nid[extra_nids++] = dac; - } - for (i = 0; i < cfg->speaker_outs; i++) { - dac = get_dac(codec, cfg->speaker_pins[i]); - if (!dac) - break; - spec->multiout.extra_out_nid[extra_nids++] = dac; - } - - if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) { - cfg->speaker_outs = cfg->line_outs; - memcpy(cfg->speaker_pins, cfg->line_out_pins, - sizeof(cfg->speaker_pins)); - cfg->line_outs = 0; - memset(cfg->line_out_pins, 0, sizeof(cfg->line_out_pins)); - } - - return 0; -} - -static int parse_input(struct hda_codec *codec) -{ - struct cs_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - int i; - - for (i = 0; i < cfg->num_inputs; i++) { - hda_nid_t pin = cfg->inputs[i].pin; - spec->input_idx[spec->num_inputs] = i; - spec->capsrc_idx[i] = spec->num_inputs++; - spec->cur_input = i; - spec->adc_nid[i] = get_adc(codec, pin, &spec->adc_idx[i]); - } - if (!spec->num_inputs) - return 0; - - /* check whether the automatic mic switch is available */ - if (spec->num_inputs == 2 && - cfg->inputs[0].type == AUTO_PIN_MIC && - cfg->inputs[1].type == AUTO_PIN_MIC) { - if (is_ext_mic(codec, cfg->inputs[0].pin)) { - if (!is_ext_mic(codec, cfg->inputs[1].pin)) { - spec->mic_detect = 1; - spec->automic_idx = 0; - } - } else { - if (is_ext_mic(codec, cfg->inputs[1].pin)) { - spec->mic_detect = 1; - spec->automic_idx = 1; - } - } - } - return 0; -} - - -static int parse_digital_output(struct hda_codec *codec) -{ - struct cs_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - hda_nid_t nid; - - if (!cfg->dig_outs) - return 0; - if (snd_hda_get_connections(codec, cfg->dig_out_pins[0], &nid, 1) < 1) - return 0; - spec->multiout.dig_out_nid = nid; - spec->multiout.share_spdif = 1; - if (cfg->dig_outs > 1 && - snd_hda_get_connections(codec, cfg->dig_out_pins[1], &nid, 1) > 0) { - spec->slave_dig_outs[0] = nid; - codec->slave_dig_outs = spec->slave_dig_outs; - } - return 0; -} - -static int parse_digital_input(struct hda_codec *codec) -{ - struct cs_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - int idx; - - if (cfg->dig_in_pin) - spec->dig_in = get_adc(codec, cfg->dig_in_pin, &idx); - return 0; -} - -/* - * create mixer controls - */ - -static const char * const dir_sfx[2] = { "Playback", "Capture" }; - -static int add_mute(struct hda_codec *codec, const char *name, int index, - unsigned int pval, int dir, struct snd_kcontrol **kctlp) -{ - char tmp[44]; - struct snd_kcontrol_new knew = - HDA_CODEC_MUTE_IDX(tmp, index, 0, 0, HDA_OUTPUT); - knew.private_value = pval; - snprintf(tmp, sizeof(tmp), "%s %s Switch", name, dir_sfx[dir]); - *kctlp = snd_ctl_new1(&knew, codec); - (*kctlp)->id.subdevice = HDA_SUBDEV_AMP_FLAG; - return snd_hda_ctl_add(codec, 0, *kctlp); -} - -static int add_volume(struct hda_codec *codec, const char *name, - int index, unsigned int pval, int dir, - struct snd_kcontrol **kctlp) -{ - char tmp[44]; - struct snd_kcontrol_new knew = - HDA_CODEC_VOLUME_IDX(tmp, index, 0, 0, HDA_OUTPUT); - knew.private_value = pval; - snprintf(tmp, sizeof(tmp), "%s %s Volume", name, dir_sfx[dir]); - *kctlp = snd_ctl_new1(&knew, codec); - (*kctlp)->id.subdevice = HDA_SUBDEV_AMP_FLAG; - return snd_hda_ctl_add(codec, 0, *kctlp); -} - -static void fix_volume_caps(struct hda_codec *codec, hda_nid_t dac) -{ - unsigned int caps; - - /* set the upper-limit for mixer amp to 0dB */ - caps = query_amp_caps(codec, dac, HDA_OUTPUT); - caps &= ~(0x7f << AC_AMPCAP_NUM_STEPS_SHIFT); - caps |= ((caps >> AC_AMPCAP_OFFSET_SHIFT) & 0x7f) - << AC_AMPCAP_NUM_STEPS_SHIFT; - snd_hda_override_amp_caps(codec, dac, HDA_OUTPUT, caps); -} - -static int add_vmaster(struct hda_codec *codec, hda_nid_t dac) -{ - struct cs_spec *spec = codec->spec; - unsigned int tlv[4]; - int err; - - spec->vmaster_sw = - snd_ctl_make_virtual_master("Master Playback Switch", NULL); - err = snd_hda_ctl_add(codec, dac, spec->vmaster_sw); - if (err < 0) - return err; - - snd_hda_set_vmaster_tlv(codec, dac, HDA_OUTPUT, tlv); - spec->vmaster_vol = - snd_ctl_make_virtual_master("Master Playback Volume", tlv); - err = snd_hda_ctl_add(codec, dac, spec->vmaster_vol); - if (err < 0) - return err; - return 0; -} - -static int add_output(struct hda_codec *codec, hda_nid_t dac, int idx, - int num_ctls, int type) -{ - struct cs_spec *spec = codec->spec; - const char *name; - int err, index; - struct snd_kcontrol *kctl; - static const char * const speakers[] = { - "Front Speaker", "Surround Speaker", "Bass Speaker" - }; - static const char * const line_outs[] = { - "Front Line Out", "Surround Line Out", "Bass Line Out" - }; - - fix_volume_caps(codec, dac); - if (!spec->vmaster_sw) { - err = add_vmaster(codec, dac); - if (err < 0) - return err; - } - - index = 0; - switch (type) { - case AUTO_PIN_HP_OUT: - name = "Headphone"; - index = idx; - break; - case AUTO_PIN_SPEAKER_OUT: - if (spec->speaker_2_1) - name = idx ? "Bass Speaker" : "Speaker"; - else if (num_ctls > 1) - name = speakers[idx]; - else - name = "Speaker"; - break; - default: - if (num_ctls > 1) - name = line_outs[idx]; - else - name = "Line Out"; - break; - } - - err = add_mute(codec, name, index, - HDA_COMPOSE_AMP_VAL(dac, 3, 0, HDA_OUTPUT), 0, &kctl); - if (err < 0) - return err; - err = snd_ctl_add_slave(spec->vmaster_sw, kctl); - if (err < 0) - return err; - - err = add_volume(codec, name, index, - HDA_COMPOSE_AMP_VAL(dac, 3, 0, HDA_OUTPUT), 0, &kctl); - if (err < 0) - return err; - err = snd_ctl_add_slave(spec->vmaster_vol, kctl); - if (err < 0) - return err; - - return 0; -} - -static int build_output(struct hda_codec *codec) -{ - struct cs_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - int i, err; - - for (i = 0; i < cfg->line_outs; i++) { - err = add_output(codec, get_dac(codec, cfg->line_out_pins[i]), - i, cfg->line_outs, cfg->line_out_type); - if (err < 0) - return err; - } - for (i = 0; i < cfg->hp_outs; i++) { - err = add_output(codec, get_dac(codec, cfg->hp_pins[i]), - i, cfg->hp_outs, AUTO_PIN_HP_OUT); - if (err < 0) - return err; - } - for (i = 0; i < cfg->speaker_outs; i++) { - err = add_output(codec, get_dac(codec, cfg->speaker_pins[i]), - i, cfg->speaker_outs, AUTO_PIN_SPEAKER_OUT); - if (err < 0) - return err; - } - return 0; -} - -/* - */ - -static const struct snd_kcontrol_new cs_capture_ctls[] = { - HDA_BIND_SW("Capture Switch", 0), - HDA_BIND_VOL("Capture Volume", 0), -}; - -static int change_cur_input(struct hda_codec *codec, unsigned int idx, - int force) -{ - struct cs_spec *spec = codec->spec; - - if (spec->cur_input == idx && !force) - return 0; - if (spec->cur_adc && spec->cur_adc != spec->adc_nid[idx]) { - /* stream is running, let's swap the current ADC */ - __snd_hda_codec_cleanup_stream(codec, spec->cur_adc, 1); - spec->cur_adc = spec->adc_nid[idx]; - snd_hda_codec_setup_stream(codec, spec->cur_adc, - spec->cur_adc_stream_tag, 0, - spec->cur_adc_format); - } - spec->cur_input = idx; - cs_update_input_select(codec); - return 1; -} - -static int cs_capture_source_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct cs_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - unsigned int idx; - - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = spec->num_inputs; - if (uinfo->value.enumerated.item >= spec->num_inputs) - uinfo->value.enumerated.item = spec->num_inputs - 1; - idx = spec->input_idx[uinfo->value.enumerated.item]; - snd_hda_get_pin_label(codec, cfg->inputs[idx].pin, cfg, - uinfo->value.enumerated.name, - sizeof(uinfo->value.enumerated.name), NULL); - return 0; -} - -static int cs_capture_source_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct cs_spec *spec = codec->spec; - ucontrol->value.enumerated.item[0] = spec->capsrc_idx[spec->cur_input]; - return 0; -} - -static int cs_capture_source_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct cs_spec *spec = codec->spec; - unsigned int idx = ucontrol->value.enumerated.item[0]; - - if (idx >= spec->num_inputs) - return -EINVAL; - idx = spec->input_idx[idx]; - return change_cur_input(codec, idx, 0); -} - -static const struct snd_kcontrol_new cs_capture_source = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Capture Source", - .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, - .info = cs_capture_source_info, - .get = cs_capture_source_get, - .put = cs_capture_source_put, -}; - -static const struct hda_bind_ctls *make_bind_capture(struct hda_codec *codec, - struct hda_ctl_ops *ops) -{ - struct cs_spec *spec = codec->spec; - struct hda_bind_ctls *bind; - int i, n; - - bind = kzalloc(sizeof(*bind) + sizeof(long) * (spec->num_inputs + 1), - GFP_KERNEL); - if (!bind) - return NULL; - bind->ops = ops; - n = 0; - for (i = 0; i < AUTO_PIN_LAST; i++) { - if (!spec->adc_nid[i]) - continue; - bind->values[n++] = - HDA_COMPOSE_AMP_VAL(spec->adc_nid[i], 3, - spec->adc_idx[i], HDA_INPUT); - } - return bind; -} - -/* add a (input-boost) volume control to the given input pin */ -static int add_input_volume_control(struct hda_codec *codec, - struct auto_pin_cfg *cfg, - int item) -{ - hda_nid_t pin = cfg->inputs[item].pin; - u32 caps; - const char *label; - struct snd_kcontrol *kctl; - - if (!(get_wcaps(codec, pin) & AC_WCAP_IN_AMP)) - return 0; - caps = query_amp_caps(codec, pin, HDA_INPUT); - caps = (caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT; - if (caps <= 1) - return 0; - label = hda_get_autocfg_input_label(codec, cfg, item); - return add_volume(codec, label, 0, - HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_INPUT), 1, &kctl); -} - -static int build_input(struct hda_codec *codec) -{ - struct cs_spec *spec = codec->spec; - int i, err; - - if (!spec->num_inputs) - return 0; - - /* make bind-capture */ - spec->capture_bind[0] = make_bind_capture(codec, &snd_hda_bind_sw); - spec->capture_bind[1] = make_bind_capture(codec, &snd_hda_bind_vol); - for (i = 0; i < 2; i++) { - struct snd_kcontrol *kctl; - int n; - if (!spec->capture_bind[i]) - return -ENOMEM; - kctl = snd_ctl_new1(&cs_capture_ctls[i], codec); - if (!kctl) - return -ENOMEM; - kctl->private_value = (long)spec->capture_bind[i]; - err = snd_hda_ctl_add(codec, 0, kctl); - if (err < 0) - return err; - for (n = 0; n < AUTO_PIN_LAST; n++) { - if (!spec->adc_nid[n]) - continue; - err = snd_hda_add_nid(codec, kctl, 0, spec->adc_nid[n]); - if (err < 0) - return err; - } - } - - if (spec->num_inputs > 1 && !spec->mic_detect) { - err = snd_hda_ctl_add(codec, 0, - snd_ctl_new1(&cs_capture_source, codec)); - if (err < 0) - return err; - } +#define CS421X_IDX_DEV_CFG 0x01 +#define CS421X_IDX_ADC_CFG 0x02 +#define CS421X_IDX_DAC_CFG 0x03 +#define CS421X_IDX_SPK_CTL 0x04 - for (i = 0; i < spec->num_inputs; i++) { - err = add_input_volume_control(codec, &spec->autocfg, i); - if (err < 0) - return err; - } +#define SPDIF_EVENT 0x04 - return 0; -} +/* Cirrus Logic CS4213 is like CS4210 but does not have SPDIF input/output */ +#define CS4213_VENDOR_NID 0x09 -/* - */ -static int build_digital_output(struct hda_codec *codec) +static inline int cs_vendor_coef_get(struct hda_codec *codec, unsigned int idx) { struct cs_spec *spec = codec->spec; - int err; - - if (!spec->multiout.dig_out_nid) - return 0; - - err = snd_hda_create_dig_out_ctls(codec, spec->multiout.dig_out_nid, - spec->multiout.dig_out_nid, - spec->pcm_rec[1].pcm_type); - if (err < 0) - return err; - err = snd_hda_create_spdif_share_sw(codec, &spec->multiout); - if (err < 0) - return err; - return 0; + snd_hda_codec_write(codec, spec->vendor_nid, 0, + AC_VERB_SET_COEF_INDEX, idx); + return snd_hda_codec_read(codec, spec->vendor_nid, 0, + AC_VERB_GET_PROC_COEF, 0); } -static int build_digital_input(struct hda_codec *codec) +static inline void cs_vendor_coef_set(struct hda_codec *codec, unsigned int idx, + unsigned int coef) { struct cs_spec *spec = codec->spec; - if (spec->dig_in) - return snd_hda_create_spdif_in_ctls(codec, spec->dig_in); - return 0; + snd_hda_codec_write(codec, spec->vendor_nid, 0, + AC_VERB_SET_COEF_INDEX, idx); + snd_hda_codec_write(codec, spec->vendor_nid, 0, + AC_VERB_SET_PROC_COEF, coef); } /* @@ -906,187 +159,37 @@ static int build_digital_input(struct hda_codec *codec) * HP/SPK/SPDIF */ -static void cs_automute(struct hda_codec *codec, struct hda_jack_tbl *tbl) +static void cs_automute(struct hda_codec *codec) { struct cs_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - unsigned int hp_present; - unsigned int spdif_present; - hda_nid_t nid; - int i; - spdif_present = 0; - if (cfg->dig_outs) { - nid = cfg->dig_out_pins[0]; - if (is_jack_detectable(codec, nid)) { - /* - TODO: SPDIF output redirect when SENSE_B is enabled. - Shared (SENSE_A) jack (e.g HP/mini-TOSLINK) - assumed. - */ - if (snd_hda_jack_detect(codec, nid) - /* && spec->sense_b */) - spdif_present = 1; - } - } - - hp_present = 0; - for (i = 0; i < cfg->hp_outs; i++) { - nid = cfg->hp_pins[i]; - if (!is_jack_detectable(codec, nid)) - continue; - hp_present = snd_hda_jack_detect(codec, nid); - if (hp_present) - break; - } + /* mute HPs if spdif jack (SENSE_B) is present */ + spec->gen.master_mute = !!(spec->spdif_present && spec->sense_b); - /* mute speakers if spdif or hp jack is plugged in */ - for (i = 0; i < cfg->speaker_outs; i++) { - int pin_ctl = hp_present ? 0 : PIN_OUT; - /* detect on spdif is specific to CS4210 */ - if (spdif_present && (spec->vendor_nid == CS4210_VENDOR_NID)) - pin_ctl = 0; + snd_hda_gen_update_outputs(codec); - nid = cfg->speaker_pins[i]; - snd_hda_set_pin_ctl(codec, nid, pin_ctl); - } if (spec->gpio_eapd_hp) { - unsigned int gpio = hp_present ? + unsigned int gpio = spec->gen.hp_jack_present ? spec->gpio_eapd_hp : spec->gpio_eapd_speaker; snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, gpio); } - - /* specific to CS4210 */ - if (spec->vendor_nid == CS4210_VENDOR_NID) { - /* mute HPs if spdif jack (SENSE_B) is present */ - for (i = 0; i < cfg->hp_outs; i++) { - nid = cfg->hp_pins[i]; - snd_hda_set_pin_ctl(codec, nid, - (spdif_present && spec->sense_b) ? 0 : PIN_HP); - } - - /* SPDIF TX on/off */ - if (cfg->dig_outs) { - nid = cfg->dig_out_pins[0]; - snd_hda_set_pin_ctl(codec, nid, - spdif_present ? PIN_OUT : 0); - - } - /* Update board GPIOs if neccessary ... */ - } -} - -/* - * Auto-input redirect for CS421x - * Switch max 3 inputs of a single ADC (nid 3) -*/ - -static void cs_automic(struct hda_codec *codec, struct hda_jack_tbl *tbl) -{ - struct cs_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - hda_nid_t nid; - unsigned int present; - - nid = cfg->inputs[spec->automic_idx].pin; - present = snd_hda_jack_detect(codec, nid); - - /* specific to CS421x, single ADC */ - if (spec->vendor_nid == CS420X_VENDOR_NID) { - if (present) - change_cur_input(codec, spec->automic_idx, 0); - else - change_cur_input(codec, !spec->automic_idx, 0); - } else { - if (present) { - if (spec->cur_input != spec->automic_idx) { - spec->last_input = spec->cur_input; - spec->cur_input = spec->automic_idx; - } - } else { - spec->cur_input = spec->last_input; - } - cs_update_input_select(codec); - } } -/* - */ - -static void init_output(struct hda_codec *codec) +static bool is_active_pin(struct hda_codec *codec, hda_nid_t nid) { - struct cs_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - int i; - - /* mute first */ - for (i = 0; i < spec->multiout.num_dacs; i++) - snd_hda_codec_write(codec, spec->multiout.dac_nids[i], 0, - AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); - if (spec->multiout.hp_nid) - snd_hda_codec_write(codec, spec->multiout.hp_nid, 0, - AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); - for (i = 0; i < ARRAY_SIZE(spec->multiout.extra_out_nid); i++) { - if (!spec->multiout.extra_out_nid[i]) - break; - snd_hda_codec_write(codec, spec->multiout.extra_out_nid[i], 0, - AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); - } - - /* set appropriate pin controls */ - for (i = 0; i < cfg->line_outs; i++) - snd_hda_set_pin_ctl(codec, cfg->line_out_pins[i], PIN_OUT); - /* HP */ - for (i = 0; i < cfg->hp_outs; i++) { - hda_nid_t nid = cfg->hp_pins[i]; - snd_hda_set_pin_ctl(codec, nid, PIN_HP); - if (!cfg->speaker_outs) - continue; - if (get_wcaps(codec, nid) & AC_WCAP_UNSOL_CAP) { - snd_hda_jack_detect_enable_callback(codec, nid, HP_EVENT, cs_automute); - spec->hp_detect = 1; - } - } - - /* Speaker */ - for (i = 0; i < cfg->speaker_outs; i++) - snd_hda_set_pin_ctl(codec, cfg->speaker_pins[i], PIN_OUT); - - /* SPDIF is enabled on presence detect for CS421x */ - if (spec->hp_detect || spec->spdif_detect) - cs_automute(codec, NULL); + unsigned int val; + val = snd_hda_codec_get_pincfg(codec, nid); + return (get_defcfg_connect(val) != AC_JACK_PORT_NONE); } -static void init_input(struct hda_codec *codec) +static void init_input_coef(struct hda_codec *codec) { struct cs_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; unsigned int coef; - int i; - for (i = 0; i < cfg->num_inputs; i++) { - unsigned int ctl; - hda_nid_t pin = cfg->inputs[i].pin; - if (!spec->adc_nid[i]) - continue; - /* set appropriate pin control and mute first */ - ctl = PIN_IN; - if (cfg->inputs[i].type == AUTO_PIN_MIC) - ctl |= snd_hda_get_default_vref(codec, pin); - snd_hda_set_pin_ctl(codec, pin, ctl); - snd_hda_codec_write(codec, spec->adc_nid[i], 0, - AC_VERB_SET_AMP_GAIN_MUTE, - AMP_IN_MUTE(spec->adc_idx[i])); - if (spec->mic_detect && spec->automic_idx == i) - snd_hda_jack_detect_enable_callback(codec, pin, MIC_EVENT, cs_automic); - } /* CS420x has multiple ADC, CS421x has single ADC */ if (spec->vendor_nid == CS420X_VENDOR_NID) { - change_cur_input(codec, spec->cur_input, 1); - if (spec->mic_detect) - cs_automic(codec, NULL); - coef = cs_vendor_coef_get(codec, IDX_BEEP_CFG); if (is_active_pin(codec, CS_DMIC2_PIN_NID)) coef |= 1 << 4; /* DMIC2 2 chan on, GPIO1 off */ @@ -1097,13 +200,6 @@ static void init_input(struct hda_codec *codec) */ cs_vendor_coef_set(codec, IDX_BEEP_CFG, coef); - } else { - if (spec->mic_detect) - cs_automic(codec, NULL); - else { - spec->cur_adc = spec->adc_nid[spec->cur_input]; - cs_update_input_select(codec); - } } } @@ -1176,7 +272,7 @@ static const struct hda_verb cs_errata_init_verbs[] = { }; /* SPDIF setup */ -static void init_digital(struct hda_codec *codec) +static void init_digital_coef(struct hda_codec *codec) { unsigned int coef; @@ -1199,7 +295,7 @@ static int cs_init(struct hda_codec *codec) snd_hda_sequence_write(codec, cs_coef_init_verbs); - snd_hda_apply_verbs(codec); + snd_hda_gen_init(codec); if (spec->gpio_mask) { snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_MASK, @@ -1210,52 +306,17 @@ static int cs_init(struct hda_codec *codec) spec->gpio_data); } - init_output(codec); - init_input(codec); - init_digital(codec); - - return 0; -} - -static int cs_build_controls(struct hda_codec *codec) -{ - struct cs_spec *spec = codec->spec; - int err; - - err = build_output(codec); - if (err < 0) - return err; - err = build_input(codec); - if (err < 0) - return err; - err = build_digital_output(codec); - if (err < 0) - return err; - err = build_digital_input(codec); - if (err < 0) - return err; - err = cs_init(codec); - if (err < 0) - return err; - - err = snd_hda_jack_add_kctls(codec, &spec->autocfg); - if (err < 0) - return err; + init_input_coef(codec); + init_digital_coef(codec); return 0; } -static void cs_free(struct hda_codec *codec) -{ - struct cs_spec *spec = codec->spec; - kfree(spec->capture_bind[0]); - kfree(spec->capture_bind[1]); - kfree(codec->spec); -} +#define cs_free snd_hda_gen_free static const struct hda_codec_ops cs_patch_ops = { - .build_controls = cs_build_controls, - .build_pcms = cs_build_pcms, + .build_controls = snd_hda_gen_build_controls, + .build_pcms = snd_hda_gen_build_pcms, .init = cs_init, .free = cs_free, .unsol_event = snd_hda_jack_unsol_event, @@ -1266,22 +327,14 @@ static int cs_parse_auto_config(struct hda_codec *codec) struct cs_spec *spec = codec->spec; int err; - err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); + err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL, 0); if (err < 0) return err; - err = parse_output(codec); - if (err < 0) - return err; - err = parse_input(codec); - if (err < 0) - return err; - err = parse_digital_output(codec); - if (err < 0) - return err; - err = parse_digital_input(codec); + err = snd_hda_gen_parse_auto_config(codec, &spec->gen.autocfg); if (err < 0) return err; + return 0; } @@ -1431,17 +484,28 @@ static const struct hda_fixup cs420x_fixups[] = { }, }; -static int patch_cs420x(struct hda_codec *codec) +static struct cs_spec *cs_alloc_spec(struct hda_codec *codec, int vendor_nid) { struct cs_spec *spec; - int err; spec = kzalloc(sizeof(*spec), GFP_KERNEL); if (!spec) - return -ENOMEM; + return NULL; codec->spec = spec; + spec->vendor_nid = vendor_nid; + snd_hda_gen_spec_init(&spec->gen); + + return spec; +} + +static int patch_cs420x(struct hda_codec *codec) +{ + struct cs_spec *spec; + int err; - spec->vendor_nid = CS420X_VENDOR_NID; + spec = cs_alloc_spec(codec, CS420X_VENDOR_NID); + if (!spec) + return -ENOMEM; snd_hda_pick_fixup(codec, cs420x_models, cs420x_fixup_tbl, cs420x_fixups); @@ -1459,7 +523,6 @@ static int patch_cs420x(struct hda_codec *codec) error: cs_free(codec); - codec->spec = NULL; return err; } @@ -1618,7 +681,7 @@ static int cs421x_boost_vol_put(struct snd_kcontrol *kcontrol, } } -static const struct snd_kcontrol_new cs421x_speaker_bost_ctl = { +static const struct snd_kcontrol_new cs421x_speaker_boost_ctl = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | @@ -1663,20 +726,44 @@ static void cs4210_pinmux_init(struct hda_codec *codec) } } -static void init_cs421x_digital(struct hda_codec *codec) +static void cs4210_spdif_automute(struct hda_codec *codec, + struct hda_jack_tbl *tbl) { struct cs_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - int i; + bool spdif_present = false; + hda_nid_t spdif_pin = spec->gen.autocfg.dig_out_pins[0]; + + /* detect on spdif is specific to CS4210 */ + if (!spec->spdif_detect || + spec->vendor_nid != CS4210_VENDOR_NID) + return; + + spdif_present = snd_hda_jack_detect(codec, spdif_pin); + if (spdif_present == spec->spdif_present) + return; + + spec->spdif_present = spdif_present; + /* SPDIF TX on/off */ + if (spdif_present) + snd_hda_set_pin_ctl(codec, spdif_pin, + spdif_present ? PIN_OUT : 0); + cs_automute(codec); +} + +static void parse_cs421x_digital(struct hda_codec *codec) +{ + struct cs_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->gen.autocfg; + int i; for (i = 0; i < cfg->dig_outs; i++) { hda_nid_t nid = cfg->dig_out_pins[i]; - if (!cfg->speaker_outs) - continue; if (get_wcaps(codec, nid) & AC_WCAP_UNSOL_CAP) { - snd_hda_jack_detect_enable_callback(codec, nid, SPDIF_EVENT, cs_automute); spec->spdif_detect = 1; + snd_hda_jack_detect_enable_callback(codec, nid, + SPDIF_EVENT, + cs4210_spdif_automute); } } } @@ -1691,6 +778,8 @@ static int cs421x_init(struct hda_codec *codec) cs4210_pinmux_init(codec); } + snd_hda_gen_init(codec); + if (spec->gpio_mask) { snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_MASK, spec->gpio_mask); @@ -1700,233 +789,61 @@ static int cs421x_init(struct hda_codec *codec) spec->gpio_data); } - init_output(codec); - init_input(codec); - init_cs421x_digital(codec); - - return 0; -} - -/* - * CS4210 Input MUX (1 ADC) - */ -static int cs421x_mux_enum_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct cs_spec *spec = codec->spec; - - return snd_hda_input_mux_info(&spec->input_mux, uinfo); -} - -static int cs421x_mux_enum_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct cs_spec *spec = codec->spec; - - ucontrol->value.enumerated.item[0] = spec->cur_input; - return 0; -} - -static int cs421x_mux_enum_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct cs_spec *spec = codec->spec; - - return snd_hda_input_mux_put(codec, &spec->input_mux, ucontrol, - spec->adc_nid[0], &spec->cur_input); - -} - -static const struct snd_kcontrol_new cs421x_capture_source = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Capture Source", - .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, - .info = cs421x_mux_enum_info, - .get = cs421x_mux_enum_get, - .put = cs421x_mux_enum_put, -}; - -static int cs421x_add_input_volume_control(struct hda_codec *codec, int item) -{ - struct cs_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - const struct hda_input_mux *imux = &spec->input_mux; - hda_nid_t pin = cfg->inputs[item].pin; - struct snd_kcontrol *kctl; - u32 caps; - - if (!(get_wcaps(codec, pin) & AC_WCAP_IN_AMP)) - return 0; - - caps = query_amp_caps(codec, pin, HDA_INPUT); - caps = (caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT; - if (caps <= 1) - return 0; - - return add_volume(codec, imux->items[item].label, 0, - HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_INPUT), 1, &kctl); -} - -/* add a (input-boost) volume control to the given input pin */ -static int build_cs421x_input(struct hda_codec *codec) -{ - struct cs_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - struct hda_input_mux *imux = &spec->input_mux; - int i, err, type_idx; - const char *label; - - if (!spec->num_inputs) - return 0; - - /* make bind-capture */ - spec->capture_bind[0] = make_bind_capture(codec, &snd_hda_bind_sw); - spec->capture_bind[1] = make_bind_capture(codec, &snd_hda_bind_vol); - for (i = 0; i < 2; i++) { - struct snd_kcontrol *kctl; - int n; - if (!spec->capture_bind[i]) - return -ENOMEM; - kctl = snd_ctl_new1(&cs_capture_ctls[i], codec); - if (!kctl) - return -ENOMEM; - kctl->private_value = (long)spec->capture_bind[i]; - err = snd_hda_ctl_add(codec, 0, kctl); - if (err < 0) - return err; - for (n = 0; n < AUTO_PIN_LAST; n++) { - if (!spec->adc_nid[n]) - continue; - err = snd_hda_add_nid(codec, kctl, 0, spec->adc_nid[n]); - if (err < 0) - return err; - } - } - - /* Add Input MUX Items + Capture Volume/Switch */ - for (i = 0; i < spec->num_inputs; i++) { - label = hda_get_autocfg_input_label(codec, cfg, i); - snd_hda_add_imux_item(imux, label, spec->adc_idx[i], &type_idx); - - err = cs421x_add_input_volume_control(codec, i); - if (err < 0) - return err; - } - - /* - Add 'Capture Source' Switch if - * 2 inputs and no mic detec - * 3 inputs - */ - if ((spec->num_inputs == 2 && !spec->mic_detect) || - (spec->num_inputs == 3)) { + init_input_coef(codec); - err = snd_hda_ctl_add(codec, spec->adc_nid[0], - snd_ctl_new1(&cs421x_capture_source, codec)); - if (err < 0) - return err; - } + cs4210_spdif_automute(codec, NULL); return 0; } -/* Single DAC (Mute/Gain) */ -static int build_cs421x_output(struct hda_codec *codec) +static int cs421x_build_controls(struct hda_codec *codec) { - hda_nid_t dac = CS4210_DAC_NID; struct cs_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - struct snd_kcontrol *kctl; int err; - char *name = "Master"; - - fix_volume_caps(codec, dac); - err = add_mute(codec, name, 0, - HDA_COMPOSE_AMP_VAL(dac, 3, 0, HDA_OUTPUT), 0, &kctl); + err = snd_hda_gen_build_controls(codec); if (err < 0) return err; - err = add_volume(codec, name, 0, - HDA_COMPOSE_AMP_VAL(dac, 3, 0, HDA_OUTPUT), 0, &kctl); - if (err < 0) - return err; - - if (cfg->speaker_outs && (spec->vendor_nid == CS4210_VENDOR_NID)) { + if (spec->gen.autocfg.speaker_outs && + spec->vendor_nid == CS4210_VENDOR_NID) { err = snd_hda_ctl_add(codec, 0, - snd_ctl_new1(&cs421x_speaker_bost_ctl, codec)); + snd_ctl_new1(&cs421x_speaker_boost_ctl, codec)); if (err < 0) return err; } - return err; -} - -static int cs421x_build_controls(struct hda_codec *codec) -{ - struct cs_spec *spec = codec->spec; - int err; - - err = build_cs421x_output(codec); - if (err < 0) - return err; - err = build_cs421x_input(codec); - if (err < 0) - return err; - err = build_digital_output(codec); - if (err < 0) - return err; - err = cs421x_init(codec); - if (err < 0) - return err; - - err = snd_hda_jack_add_kctls(codec, &spec->autocfg); - if (err < 0) - return err; - return 0; } -static int parse_cs421x_input(struct hda_codec *codec) +static void fix_volume_caps(struct hda_codec *codec, hda_nid_t dac) { - struct cs_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - int i; - - for (i = 0; i < cfg->num_inputs; i++) { - hda_nid_t pin = cfg->inputs[i].pin; - spec->adc_nid[i] = get_adc(codec, pin, &spec->adc_idx[i]); - spec->cur_input = spec->last_input = i; - spec->num_inputs++; + unsigned int caps; - /* check whether the automatic mic switch is available */ - if (is_ext_mic(codec, i) && cfg->num_inputs >= 2) { - spec->mic_detect = 1; - spec->automic_idx = i; - } - } - return 0; + /* set the upper-limit for mixer amp to 0dB */ + caps = query_amp_caps(codec, dac, HDA_OUTPUT); + caps &= ~(0x7f << AC_AMPCAP_NUM_STEPS_SHIFT); + caps |= ((caps >> AC_AMPCAP_OFFSET_SHIFT) & 0x7f) + << AC_AMPCAP_NUM_STEPS_SHIFT; + snd_hda_override_amp_caps(codec, dac, HDA_OUTPUT, caps); } static int cs421x_parse_auto_config(struct hda_codec *codec) { struct cs_spec *spec = codec->spec; + hda_nid_t dac = CS4210_DAC_NID; int err; - err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); - if (err < 0) - return err; - err = parse_output(codec); - if (err < 0) - return err; - err = parse_cs421x_input(codec); + fix_volume_caps(codec, dac); + + err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL, 0); if (err < 0) return err; - err = parse_digital_output(codec); + + err = snd_hda_gen_parse_auto_config(codec, &spec->gen.autocfg); if (err < 0) return err; + + parse_cs421x_digital(codec); return 0; } @@ -1959,7 +876,7 @@ static int cs421x_suspend(struct hda_codec *codec) static const struct hda_codec_ops cs421x_patch_ops = { .build_controls = cs421x_build_controls, - .build_pcms = cs_build_pcms, + .build_pcms = snd_hda_gen_build_pcms, .init = cs421x_init, .free = cs_free, .unsol_event = snd_hda_jack_unsol_event, @@ -1973,12 +890,9 @@ static int patch_cs4210(struct hda_codec *codec) struct cs_spec *spec; int err; - spec = kzalloc(sizeof(*spec), GFP_KERNEL); + spec = cs_alloc_spec(codec, CS4210_VENDOR_NID); if (!spec) return -ENOMEM; - codec->spec = spec; - - spec->vendor_nid = CS4210_VENDOR_NID; snd_hda_pick_fixup(codec, cs421x_models, cs421x_fixup_tbl, cs421x_fixups); @@ -2003,7 +917,6 @@ static int patch_cs4210(struct hda_codec *codec) error: cs_free(codec); - codec->spec = NULL; return err; } @@ -2012,12 +925,9 @@ static int patch_cs4213(struct hda_codec *codec) struct cs_spec *spec; int err; - spec = kzalloc(sizeof(*spec), GFP_KERNEL); + spec = cs_alloc_spec(codec, CS4213_VENDOR_NID); if (!spec) return -ENOMEM; - codec->spec = spec; - - spec->vendor_nid = CS4213_VENDOR_NID; err = cs421x_parse_auto_config(codec); if (err < 0) @@ -2028,7 +938,6 @@ static int patch_cs4213(struct hda_codec *codec) error: cs_free(codec); - codec->spec = NULL; return err; } -- cgit v1.2.3 From aed523f193ed0a208d93c764e5372ac645cc0402 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 20 Dec 2012 16:34:12 +0100 Subject: ALSA: hda - Use generic parser in Conexant codec driver ... and drop most of own parser code. It doesn't replace any present static quirks, though. Signed-off-by: Takashi Iwai --- sound/pci/hda/Kconfig | 1 + sound/pci/hda/patch_conexant.c | 1314 ++-------------------------------------- 2 files changed, 62 insertions(+), 1253 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig index 07025a9339ef..206f678f41d1 100644 --- a/sound/pci/hda/Kconfig +++ b/sound/pci/hda/Kconfig @@ -162,6 +162,7 @@ config SND_HDA_CODEC_CIRRUS config SND_HDA_CODEC_CONEXANT bool "Build Conexant HD-audio codec support" default y + select SND_HDA_GENERIC help Say Y here to include Conexant HD-audio codec support in snd-hda-intel driver, such as CX20549. diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index a52f5662f69c..1ff5f3cc96b1 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -33,6 +33,7 @@ #include "hda_auto_parser.h" #include "hda_beep.h" #include "hda_jack.h" +#include "hda_generic.h" #define CXT_PIN_DIR_IN 0x00 #define CXT_PIN_DIR_OUT 0x01 @@ -53,25 +54,12 @@ #define AUTO_MIC_PORTB (1 << 1) #define AUTO_MIC_PORTC (1 << 2) -struct pin_dac_pair { - hda_nid_t pin; - hda_nid_t dac; - int type; -}; - -struct imux_info { - hda_nid_t pin; /* input pin NID */ - hda_nid_t adc; /* connected ADC NID */ - hda_nid_t boost; /* optional boost volume NID */ - int index; /* corresponding to autocfg.input */ -}; - struct conexant_spec { + struct hda_gen_spec gen; + const struct snd_kcontrol_new *mixers[5]; int num_mixers; hda_nid_t vmaster_nid; - struct hda_vmaster_mute_hook vmaster_mute; - bool vmaster_mute_led; const struct hda_verb *init_verbs[5]; /* initialization verbs * don't forget NULL @@ -88,11 +76,6 @@ struct conexant_spec { unsigned int hp_present; unsigned int line_present; unsigned int auto_mic; - int auto_mic_ext; /* imux_pins[] index for ext mic */ - int auto_mic_dock; /* imux_pins[] index for dock mic */ - int auto_mic_int; /* imux_pins[] index for int mic */ - unsigned int need_dac_fix; - hda_nid_t slave_dig_outs[2]; /* capture */ unsigned int num_adc_nids; @@ -120,30 +103,13 @@ struct conexant_spec { unsigned int spdif_route; - /* dynamic controls, init_verbs and input_mux */ - struct auto_pin_cfg autocfg; - struct hda_input_mux private_imux; - struct imux_info imux_info[HDA_MAX_NUM_INPUTS]; - hda_nid_t private_adc_nids[HDA_MAX_NUM_INPUTS]; - hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS]; - struct pin_dac_pair dac_info[8]; - int dac_info_filled; - unsigned int port_d_mode; - unsigned int auto_mute:1; /* used in auto-parser */ - unsigned int detect_line:1; /* Line-out detection enabled */ - unsigned int automute_lines:1; /* automute line-out as well */ - unsigned int automute_hp_lo:1; /* both HP and LO available */ unsigned int dell_automute:1; unsigned int dell_vostro:1; unsigned int ideapad:1; unsigned int thinkpad:1; unsigned int hp_laptop:1; unsigned int asus:1; - unsigned int pin_eapd_ctrls:1; - unsigned int fixup_stereo_dmic:1; - - unsigned int adc_switching:1; unsigned int ext_mic_present; unsigned int recording; @@ -335,8 +301,6 @@ static const struct hda_pcm_stream cx5051_pcm_analog_capture = { }, }; -static bool is_2_1_speaker(struct conexant_spec *spec); - static int conexant_build_pcms(struct hda_codec *codec) { struct conexant_spec *spec = codec->spec; @@ -351,9 +315,6 @@ static int conexant_build_pcms(struct hda_codec *codec) spec->multiout.max_channels; info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dac_nids[0]; - if (is_2_1_speaker(spec)) - info->stream[SNDRV_PCM_STREAM_PLAYBACK].chmap = - snd_pcm_2_1_chmaps; if (spec->capture_stream) info->stream[SNDRV_PCM_STREAM_CAPTURE] = *spec->capture_stream; else { @@ -384,8 +345,6 @@ static int conexant_build_pcms(struct hda_codec *codec) info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in_nid; } - if (spec->slave_dig_outs[0]) - codec->slave_dig_outs = spec->slave_dig_outs; } return 0; @@ -471,6 +430,29 @@ static const struct snd_kcontrol_new cxt_beep_mixer[] = { HDA_CODEC_MUTE_BEEP_MONO("Beep Playback Switch", 0, 1, 0, HDA_OUTPUT), { } /* end */ }; +/* create beep controls if needed */ +static int add_beep_ctls(struct hda_codec *codec) +{ + struct conexant_spec *spec = codec->spec; + int err; + + if (spec->beep_amp) { + const struct snd_kcontrol_new *knew; + for (knew = cxt_beep_mixer; knew->name; knew++) { + struct snd_kcontrol *kctl; + kctl = snd_ctl_new1(knew, codec); + if (!kctl) + return -ENOMEM; + kctl->private_value = spec->beep_amp; + err = snd_hda_ctl_add(codec, 0, kctl); + if (err < 0) + return err; + } + } + return 0; +} +#else +#define add_beep_ctls(codec) 0 #endif static const char * const slave_pfxs[] = { @@ -521,10 +503,9 @@ static int conexant_build_controls(struct hda_codec *codec) } if (spec->vmaster_nid && !snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) { - err = __snd_hda_add_vmaster(codec, "Master Playback Switch", - NULL, slave_pfxs, - "Playback Switch", true, - &spec->vmaster_mute.sw_kctl); + err = snd_hda_add_vmaster(codec, "Master Playback Switch", + NULL, slave_pfxs, + "Playback Switch"); if (err < 0) return err; } @@ -535,22 +516,9 @@ static int conexant_build_controls(struct hda_codec *codec) return err; } -#ifdef CONFIG_SND_HDA_INPUT_BEEP - /* create beep controls if needed */ - if (spec->beep_amp) { - const struct snd_kcontrol_new *knew; - for (knew = cxt_beep_mixer; knew->name; knew++) { - struct snd_kcontrol *kctl; - kctl = snd_ctl_new1(knew, codec); - if (!kctl) - return -ENOMEM; - kctl->private_value = spec->beep_amp; - err = snd_hda_ctl_add(codec, 0, kctl); - if (err < 0) - return err; - } - } -#endif + err = add_beep_ctls(codec); + if (err < 0) + return err; return 0; } @@ -653,8 +621,6 @@ static int conexant_ch_mode_put(struct snd_kcontrol *kcontrol, int err = snd_hda_ch_mode_put(codec, ucontrol, spec->channel_mode, spec->num_channel_mode, &spec->multiout.max_channels); - if (err >= 0 && spec->need_dac_fix) - spec->multiout.num_dacs = spec->multiout.max_channels / 2; return err; } @@ -2493,10 +2459,6 @@ static void conexant_check_dig_outs(struct hda_codec *codec, continue; if (snd_hda_get_connections(codec, *dig_pins, nid_loc, 1) != 1) continue; - if (spec->slave_dig_outs[0]) - nid_loc++; - else - nid_loc = spec->slave_dig_outs; } } @@ -3142,623 +3104,6 @@ static int patch_cxt5066(struct hda_codec *codec) * Automatic parser for CX20641 & co */ -static int cx_auto_capture_pcm_prepare(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - unsigned int stream_tag, - unsigned int format, - struct snd_pcm_substream *substream) -{ - struct conexant_spec *spec = codec->spec; - hda_nid_t adc = spec->imux_info[spec->cur_mux[0]].adc; - if (spec->adc_switching) { - spec->cur_adc = adc; - spec->cur_adc_stream_tag = stream_tag; - spec->cur_adc_format = format; - } - snd_hda_codec_setup_stream(codec, adc, stream_tag, 0, format); - return 0; -} - -static int cx_auto_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct conexant_spec *spec = codec->spec; - snd_hda_codec_cleanup_stream(codec, spec->cur_adc); - spec->cur_adc = 0; - return 0; -} - -static const struct hda_pcm_stream cx_auto_pcm_analog_capture = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, - .nid = 0, /* fill later */ - .ops = { - .prepare = cx_auto_capture_pcm_prepare, - .cleanup = cx_auto_capture_pcm_cleanup - }, -}; - -static const hda_nid_t cx_auto_adc_nids[] = { 0x14 }; - -#define get_connection_index(codec, mux, nid)\ - snd_hda_get_conn_index(codec, mux, nid, 0) - -/* get an unassigned DAC from the given list. - * Return the nid if found and reduce the DAC list, or return zero if - * not found - */ -static hda_nid_t get_unassigned_dac(struct hda_codec *codec, hda_nid_t pin, - hda_nid_t *dacs, int *num_dacs) -{ - int i, nums = *num_dacs; - hda_nid_t ret = 0; - - for (i = 0; i < nums; i++) { - if (get_connection_index(codec, pin, dacs[i]) >= 0) { - ret = dacs[i]; - break; - } - } - if (!ret) - return 0; - if (--nums > 0) - memmove(dacs, dacs + 1, nums * sizeof(hda_nid_t)); - *num_dacs = nums; - return ret; -} - -#define MAX_AUTO_DACS 5 - -#define DAC_SLAVE_FLAG 0x8000 /* filled dac is a slave */ - -/* fill analog DAC list from the widget tree */ -static int fill_cx_auto_dacs(struct hda_codec *codec, hda_nid_t *dacs) -{ - hda_nid_t nid, end_nid; - int nums = 0; - - end_nid = codec->start_nid + codec->num_nodes; - for (nid = codec->start_nid; nid < end_nid; nid++) { - unsigned int wcaps = get_wcaps(codec, nid); - unsigned int type = get_wcaps_type(wcaps); - if (type == AC_WID_AUD_OUT && !(wcaps & AC_WCAP_DIGITAL)) { - dacs[nums++] = nid; - if (nums >= MAX_AUTO_DACS) - break; - } - } - return nums; -} - -/* fill pin_dac_pair list from the pin and dac list */ -static int fill_dacs_for_pins(struct hda_codec *codec, hda_nid_t *pins, - int num_pins, hda_nid_t *dacs, int *rest, - struct pin_dac_pair *filled, int nums, - int type) -{ - int i, start = nums; - - for (i = 0; i < num_pins; i++, nums++) { - filled[nums].pin = pins[i]; - filled[nums].type = type; - filled[nums].dac = get_unassigned_dac(codec, pins[i], dacs, rest); - if (filled[nums].dac) - continue; - if (filled[start].dac && get_connection_index(codec, pins[i], filled[start].dac) >= 0) { - filled[nums].dac = filled[start].dac | DAC_SLAVE_FLAG; - continue; - } - if (filled[0].dac && get_connection_index(codec, pins[i], filled[0].dac) >= 0) { - filled[nums].dac = filled[0].dac | DAC_SLAVE_FLAG; - continue; - } - snd_printdd("Failed to find a DAC for pin 0x%x", pins[i]); - } - return nums; -} - -/* parse analog output paths */ -static void cx_auto_parse_output(struct hda_codec *codec) -{ - struct conexant_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - hda_nid_t dacs[MAX_AUTO_DACS]; - int i, j, nums, rest; - - rest = fill_cx_auto_dacs(codec, dacs); - /* parse all analog output pins */ - nums = fill_dacs_for_pins(codec, cfg->line_out_pins, cfg->line_outs, - dacs, &rest, spec->dac_info, 0, - AUTO_PIN_LINE_OUT); - nums = fill_dacs_for_pins(codec, cfg->hp_pins, cfg->hp_outs, - dacs, &rest, spec->dac_info, nums, - AUTO_PIN_HP_OUT); - nums = fill_dacs_for_pins(codec, cfg->speaker_pins, cfg->speaker_outs, - dacs, &rest, spec->dac_info, nums, - AUTO_PIN_SPEAKER_OUT); - spec->dac_info_filled = nums; - /* fill multiout struct */ - for (i = 0; i < nums; i++) { - hda_nid_t dac = spec->dac_info[i].dac; - if (!dac || (dac & DAC_SLAVE_FLAG)) - continue; - switch (spec->dac_info[i].type) { - case AUTO_PIN_LINE_OUT: - spec->private_dac_nids[spec->multiout.num_dacs] = dac; - spec->multiout.num_dacs++; - break; - case AUTO_PIN_HP_OUT: - case AUTO_PIN_SPEAKER_OUT: - if (!spec->multiout.hp_nid) { - spec->multiout.hp_nid = dac; - break; - } - for (j = 0; j < ARRAY_SIZE(spec->multiout.extra_out_nid); j++) - if (!spec->multiout.extra_out_nid[j]) { - spec->multiout.extra_out_nid[j] = dac; - break; - } - break; - } - } - spec->multiout.dac_nids = spec->private_dac_nids; - spec->multiout.max_channels = spec->multiout.num_dacs * 2; - - for (i = 0; i < cfg->hp_outs; i++) { - if (is_jack_detectable(codec, cfg->hp_pins[i])) { - spec->auto_mute = 1; - break; - } - } - if (spec->auto_mute && - cfg->line_out_pins[0] && - cfg->line_out_type != AUTO_PIN_SPEAKER_OUT && - cfg->line_out_pins[0] != cfg->hp_pins[0] && - cfg->line_out_pins[0] != cfg->speaker_pins[0]) { - for (i = 0; i < cfg->line_outs; i++) { - if (is_jack_detectable(codec, cfg->line_out_pins[i])) { - spec->detect_line = 1; - break; - } - } - spec->automute_lines = spec->detect_line; - } - - spec->vmaster_nid = spec->private_dac_nids[0]; -} - -static void cx_auto_turn_eapd(struct hda_codec *codec, int num_pins, - hda_nid_t *pins, bool on); - -static void do_automute(struct hda_codec *codec, int num_pins, - hda_nid_t *pins, bool on) -{ - struct conexant_spec *spec = codec->spec; - int i; - for (i = 0; i < num_pins; i++) - snd_hda_set_pin_ctl(codec, pins[i], on ? PIN_OUT : 0); - if (spec->pin_eapd_ctrls) - cx_auto_turn_eapd(codec, num_pins, pins, on); -} - -static int detect_jacks(struct hda_codec *codec, int num_pins, hda_nid_t *pins) -{ - int i, present = 0; - - for (i = 0; i < num_pins; i++) { - hda_nid_t nid = pins[i]; - if (!nid || !is_jack_detectable(codec, nid)) - break; - present |= snd_hda_jack_detect(codec, nid); - } - return present; -} - -/* auto-mute/unmute speaker and line outs according to headphone jack */ -static void cx_auto_update_speakers(struct hda_codec *codec) -{ - struct conexant_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - int on = 1; - - /* turn on HP EAPD when HP jacks are present */ - if (spec->pin_eapd_ctrls) { - if (spec->auto_mute) - on = spec->hp_present; - cx_auto_turn_eapd(codec, cfg->hp_outs, cfg->hp_pins, on); - } - - /* mute speakers in auto-mode if HP or LO jacks are plugged */ - if (spec->auto_mute) - on = !(spec->hp_present || - (spec->detect_line && spec->line_present)); - do_automute(codec, cfg->speaker_outs, cfg->speaker_pins, on); - - /* toggle line-out mutes if needed, too */ - /* if LO is a copy of either HP or Speaker, don't need to handle it */ - if (cfg->line_out_pins[0] == cfg->hp_pins[0] || - cfg->line_out_pins[0] == cfg->speaker_pins[0]) - return; - if (spec->auto_mute) { - /* mute LO in auto-mode when HP jack is present */ - if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT || - spec->automute_lines) - on = !spec->hp_present; - else - on = 1; - } - do_automute(codec, cfg->line_outs, cfg->line_out_pins, on); -} - -static void cx_auto_hp_automute(struct hda_codec *codec, struct hda_jack_tbl *jack) -{ - struct conexant_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - - if (!spec->auto_mute) - return; - spec->hp_present = detect_jacks(codec, cfg->hp_outs, cfg->hp_pins); - cx_auto_update_speakers(codec); -} - -static void cx_auto_line_automute(struct hda_codec *codec, struct hda_jack_tbl *jack) -{ - struct conexant_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - - if (!spec->auto_mute || !spec->detect_line) - return; - spec->line_present = detect_jacks(codec, cfg->line_outs, - cfg->line_out_pins); - cx_auto_update_speakers(codec); -} - -static int cx_automute_mode_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct conexant_spec *spec = codec->spec; - static const char * const texts3[] = { - "Disabled", "Speaker Only", "Line Out+Speaker" - }; - - if (spec->automute_hp_lo) - return snd_hda_enum_helper_info(kcontrol, uinfo, 3, texts3); - return snd_hda_enum_bool_helper_info(kcontrol, uinfo); -} - -static int cx_automute_mode_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct conexant_spec *spec = codec->spec; - unsigned int val; - if (!spec->auto_mute) - val = 0; - else if (!spec->automute_lines) - val = 1; - else - val = 2; - ucontrol->value.enumerated.item[0] = val; - return 0; -} - -static int cx_automute_mode_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct conexant_spec *spec = codec->spec; - - switch (ucontrol->value.enumerated.item[0]) { - case 0: - if (!spec->auto_mute) - return 0; - spec->auto_mute = 0; - break; - case 1: - if (spec->auto_mute && !spec->automute_lines) - return 0; - spec->auto_mute = 1; - spec->automute_lines = 0; - break; - case 2: - if (!spec->automute_hp_lo) - return -EINVAL; - if (spec->auto_mute && spec->automute_lines) - return 0; - spec->auto_mute = 1; - spec->automute_lines = 1; - break; - default: - return -EINVAL; - } - cx_auto_update_speakers(codec); - return 1; -} - -static const struct snd_kcontrol_new cx_automute_mode_enum[] = { - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Auto-Mute Mode", - .info = cx_automute_mode_info, - .get = cx_automute_mode_get, - .put = cx_automute_mode_put, - }, - { } -}; - -static int cx_auto_mux_enum_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct conexant_spec *spec = codec->spec; - - return snd_hda_input_mux_info(&spec->private_imux, uinfo); -} - -static int cx_auto_mux_enum_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct conexant_spec *spec = codec->spec; - - ucontrol->value.enumerated.item[0] = spec->cur_mux[0]; - return 0; -} - -/* look for the route the given pin from mux and return the index; - * if do_select is set, actually select the route. - */ -static int __select_input_connection(struct hda_codec *codec, hda_nid_t mux, - hda_nid_t pin, hda_nid_t *srcp, - bool do_select, int depth) -{ - struct conexant_spec *spec = codec->spec; - hda_nid_t conn[HDA_MAX_NUM_INPUTS]; - int startidx, i, nums; - - switch (get_wcaps_type(get_wcaps(codec, mux))) { - case AC_WID_AUD_IN: - case AC_WID_AUD_SEL: - case AC_WID_AUD_MIX: - break; - default: - return -1; - } - - nums = snd_hda_get_connections(codec, mux, conn, ARRAY_SIZE(conn)); - for (i = 0; i < nums; i++) - if (conn[i] == pin) { - if (do_select) - snd_hda_codec_write(codec, mux, 0, - AC_VERB_SET_CONNECT_SEL, i); - if (srcp) - *srcp = mux; - return i; - } - depth++; - if (depth == 2) - return -1; - - /* Try to rotate around connections to avoid one boost controlling - another input path as well */ - startidx = 0; - for (i = 0; i < spec->private_imux.num_items; i++) - if (spec->imux_info[i].pin == pin) { - startidx = i; - break; - } - - for (i = 0; i < nums; i++) { - int j = (i + startidx) % nums; - int ret = __select_input_connection(codec, conn[j], pin, srcp, - do_select, depth); - if (ret >= 0) { - if (do_select) - snd_hda_codec_write(codec, mux, 0, - AC_VERB_SET_CONNECT_SEL, j); - return j; - } - } - return -1; -} - -static void select_input_connection(struct hda_codec *codec, hda_nid_t mux, - hda_nid_t pin) -{ - __select_input_connection(codec, mux, pin, NULL, true, 0); -} - -static int get_input_connection(struct hda_codec *codec, hda_nid_t mux, - hda_nid_t pin) -{ - return __select_input_connection(codec, mux, pin, NULL, false, 0); -} - -static int cx_auto_mux_enum_update(struct hda_codec *codec, - const struct hda_input_mux *imux, - unsigned int idx) -{ - struct conexant_spec *spec = codec->spec; - hda_nid_t adc; - int changed = 1; - - if (!imux->num_items) - return 0; - if (idx >= imux->num_items) - idx = imux->num_items - 1; - if (spec->cur_mux[0] == idx) - changed = 0; - adc = spec->imux_info[idx].adc; - select_input_connection(codec, spec->imux_info[idx].adc, - spec->imux_info[idx].pin); - if (spec->cur_adc && spec->cur_adc != adc) { - /* stream is running, let's swap the current ADC */ - __snd_hda_codec_cleanup_stream(codec, spec->cur_adc, 1); - spec->cur_adc = adc; - snd_hda_codec_setup_stream(codec, adc, - spec->cur_adc_stream_tag, 0, - spec->cur_adc_format); - } - spec->cur_mux[0] = idx; - return changed; -} - -static int cx_auto_mux_enum_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct conexant_spec *spec = codec->spec; - - return cx_auto_mux_enum_update(codec, &spec->private_imux, - ucontrol->value.enumerated.item[0]); -} - -static const struct snd_kcontrol_new cx_auto_capture_mixers[] = { - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Capture Source", - .info = cx_auto_mux_enum_info, - .get = cx_auto_mux_enum_get, - .put = cx_auto_mux_enum_put - }, - {} -}; - -static bool select_automic(struct hda_codec *codec, int idx, bool detect) -{ - struct conexant_spec *spec = codec->spec; - if (idx < 0) - return false; - if (detect && !snd_hda_jack_detect(codec, spec->imux_info[idx].pin)) - return false; - cx_auto_mux_enum_update(codec, &spec->private_imux, idx); - return true; -} - -/* automatic switch internal and external mic */ -static void cx_auto_automic(struct hda_codec *codec, struct hda_jack_tbl *jack) -{ - struct conexant_spec *spec = codec->spec; - - if (!spec->auto_mic) - return; - if (!select_automic(codec, spec->auto_mic_ext, true)) - if (!select_automic(codec, spec->auto_mic_dock, true)) - select_automic(codec, spec->auto_mic_int, false); -} - -/* check whether the pin config is suitable for auto-mic switching; - * auto-mic is enabled only when one int-mic and one ext- and/or - * one dock-mic exist - */ -static void cx_auto_check_auto_mic(struct hda_codec *codec) -{ - struct conexant_spec *spec = codec->spec; - int pset[INPUT_PIN_ATTR_NORMAL + 1]; - int i; - - for (i = 0; i < ARRAY_SIZE(pset); i++) - pset[i] = -1; - for (i = 0; i < spec->private_imux.num_items; i++) { - hda_nid_t pin = spec->imux_info[i].pin; - unsigned int def_conf = snd_hda_codec_get_pincfg(codec, pin); - int type, attr; - attr = snd_hda_get_input_pin_attr(def_conf); - if (attr == INPUT_PIN_ATTR_UNUSED) - return; /* invalid entry */ - if (attr > INPUT_PIN_ATTR_NORMAL) - attr = INPUT_PIN_ATTR_NORMAL; - if (attr != INPUT_PIN_ATTR_INT && - !is_jack_detectable(codec, pin)) - return; /* non-detectable pin */ - type = get_defcfg_device(def_conf); - if (type != AC_JACK_MIC_IN && - (attr != INPUT_PIN_ATTR_DOCK || type != AC_JACK_LINE_IN)) - return; /* no valid input type */ - if (pset[attr] >= 0) - return; /* already occupied */ - pset[attr] = i; - } - if (pset[INPUT_PIN_ATTR_INT] < 0 || - (pset[INPUT_PIN_ATTR_NORMAL] < 0 && pset[INPUT_PIN_ATTR_DOCK])) - return; /* no input to switch*/ - spec->auto_mic = 1; - spec->auto_mic_ext = pset[INPUT_PIN_ATTR_NORMAL]; - spec->auto_mic_dock = pset[INPUT_PIN_ATTR_DOCK]; - spec->auto_mic_int = pset[INPUT_PIN_ATTR_INT]; -} - -static void cx_auto_parse_input(struct hda_codec *codec) -{ - struct conexant_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - struct hda_input_mux *imux; - int i, j; - - imux = &spec->private_imux; - for (i = 0; i < cfg->num_inputs; i++) { - for (j = 0; j < spec->num_adc_nids; j++) { - hda_nid_t adc = spec->adc_nids[j]; - int idx = get_input_connection(codec, adc, - cfg->inputs[i].pin); - if (idx >= 0) { - const char *label; - label = hda_get_autocfg_input_label(codec, cfg, i); - spec->imux_info[imux->num_items].index = i; - spec->imux_info[imux->num_items].boost = 0; - spec->imux_info[imux->num_items].adc = adc; - spec->imux_info[imux->num_items].pin = - cfg->inputs[i].pin; - snd_hda_add_imux_item(imux, label, idx, NULL); - break; - } - } - } - if (imux->num_items >= 2 && cfg->num_inputs == imux->num_items) - cx_auto_check_auto_mic(codec); - if (imux->num_items > 1) { - for (i = 1; i < imux->num_items; i++) { - if (spec->imux_info[i].adc != spec->imux_info[0].adc) { - spec->adc_switching = 1; - break; - } - } - } -} - -/* get digital-input audio widget corresponding to the given pin */ -static hda_nid_t cx_auto_get_dig_in(struct hda_codec *codec, hda_nid_t pin) -{ - hda_nid_t nid, end_nid; - - end_nid = codec->start_nid + codec->num_nodes; - for (nid = codec->start_nid; nid < end_nid; nid++) { - unsigned int wcaps = get_wcaps(codec, nid); - unsigned int type = get_wcaps_type(wcaps); - if (type == AC_WID_AUD_IN && (wcaps & AC_WCAP_DIGITAL)) { - if (get_connection_index(codec, nid, pin) >= 0) - return nid; - } - } - return 0; -} - -static void cx_auto_parse_digital(struct hda_codec *codec) -{ - struct conexant_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - hda_nid_t nid; - - if (cfg->dig_outs && - snd_hda_get_connections(codec, cfg->dig_out_pins[0], &nid, 1) == 1) - spec->multiout.dig_out_nid = nid; - if (cfg->dig_in_pin) - spec->dig_in_nid = cx_auto_get_dig_in(codec, cfg->dig_in_pin); -} - #ifdef CONFIG_SND_HDA_INPUT_BEEP static void cx_auto_parse_beep(struct hda_codec *codec) { @@ -3799,24 +3144,8 @@ static void cx_auto_parse_eapd(struct hda_codec *codec) * OTOH, if only one or two EAPDs are found, it's an old chip, * thus it might control over all pins. */ - spec->pin_eapd_ctrls = spec->num_eapds > 2; -} - -static int cx_auto_parse_auto_config(struct hda_codec *codec) -{ - struct conexant_spec *spec = codec->spec; - int err; - - err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); - if (err < 0) - return err; - - cx_auto_parse_output(codec); - cx_auto_parse_input(codec); - cx_auto_parse_digital(codec); - cx_auto_parse_beep(codec); - cx_auto_parse_eapd(codec); - return 0; + if (spec->num_eapds > 2) + spec->gen.own_eapd_ctl = 1; } static void cx_auto_turn_eapd(struct hda_codec *codec, int num_pins, @@ -3831,564 +3160,35 @@ static void cx_auto_turn_eapd(struct hda_codec *codec, int num_pins, } } -static void select_connection(struct hda_codec *codec, hda_nid_t pin, - hda_nid_t src) -{ - int idx = get_connection_index(codec, pin, src); - if (idx >= 0) - snd_hda_codec_write(codec, pin, 0, - AC_VERB_SET_CONNECT_SEL, idx); -} - -static void mute_outputs(struct hda_codec *codec, int num_nids, - const hda_nid_t *nids) -{ - int i, val; - - for (i = 0; i < num_nids; i++) { - hda_nid_t nid = nids[i]; - if (!(get_wcaps(codec, nid) & AC_WCAP_OUT_AMP)) - continue; - if (query_amp_caps(codec, nid, HDA_OUTPUT) & AC_AMPCAP_MUTE) - val = AMP_OUT_MUTE; - else - val = AMP_OUT_ZERO; - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_AMP_GAIN_MUTE, val); - } -} - -static void enable_unsol_pins(struct hda_codec *codec, int num_pins, - hda_nid_t *pins, unsigned int action, - hda_jack_callback cb) -{ - int i; - for (i = 0; i < num_pins; i++) - snd_hda_jack_detect_enable_callback(codec, pins[i], action, cb); -} - -static bool found_in_nid_list(hda_nid_t nid, const hda_nid_t *list, int nums) -{ - int i; - for (i = 0; i < nums; i++) - if (list[i] == nid) - return true; - return false; -} - -/* is the given NID found in any of autocfg items? */ -static bool found_in_autocfg(struct auto_pin_cfg *cfg, hda_nid_t nid) -{ - int i; - - if (found_in_nid_list(nid, cfg->line_out_pins, cfg->line_outs) || - found_in_nid_list(nid, cfg->hp_pins, cfg->hp_outs) || - found_in_nid_list(nid, cfg->speaker_pins, cfg->speaker_outs) || - found_in_nid_list(nid, cfg->dig_out_pins, cfg->dig_outs)) - return true; - for (i = 0; i < cfg->num_inputs; i++) - if (cfg->inputs[i].pin == nid) - return true; - if (cfg->dig_in_pin == nid) - return true; - return false; -} - -/* clear unsol-event tags on unused pins; Conexant codecs seem to leave - * invalid unsol tags by some reason - */ -static void clear_unsol_on_unused_pins(struct hda_codec *codec) -{ - struct conexant_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - int i; - - for (i = 0; i < codec->init_pins.used; i++) { - struct hda_pincfg *pin = snd_array_elem(&codec->init_pins, i); - if (!found_in_autocfg(cfg, pin->nid)) - snd_hda_codec_write(codec, pin->nid, 0, - AC_VERB_SET_UNSOLICITED_ENABLE, 0); - } -} - /* turn on/off EAPD according to Master switch */ static void cx_auto_vmaster_hook(void *private_data, int enabled) { struct hda_codec *codec = private_data; struct conexant_spec *spec = codec->spec; - if (enabled && spec->pin_eapd_ctrls) { - cx_auto_update_speakers(codec); - return; - } cx_auto_turn_eapd(codec, spec->num_eapds, spec->eapds, enabled); } -static void cx_auto_init_output(struct hda_codec *codec) -{ - struct conexant_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - hda_nid_t nid; - int i; - - mute_outputs(codec, spec->multiout.num_dacs, spec->multiout.dac_nids); - for (i = 0; i < cfg->hp_outs; i++) { - unsigned int val = PIN_OUT; - if (snd_hda_query_pin_caps(codec, cfg->hp_pins[i]) & - AC_PINCAP_HP_DRV) - val |= AC_PINCTL_HP_EN; - snd_hda_set_pin_ctl(codec, cfg->hp_pins[i], val); - } - mute_outputs(codec, cfg->hp_outs, cfg->hp_pins); - mute_outputs(codec, cfg->line_outs, cfg->line_out_pins); - mute_outputs(codec, cfg->speaker_outs, cfg->speaker_pins); - for (i = 0; i < spec->dac_info_filled; i++) { - nid = spec->dac_info[i].dac; - if (!nid) - nid = spec->multiout.dac_nids[0]; - else if (nid & DAC_SLAVE_FLAG) - nid &= ~DAC_SLAVE_FLAG; - select_connection(codec, spec->dac_info[i].pin, nid); - } - if (spec->auto_mute) { - enable_unsol_pins(codec, cfg->hp_outs, cfg->hp_pins, - CONEXANT_HP_EVENT, cx_auto_hp_automute); - spec->hp_present = detect_jacks(codec, cfg->hp_outs, - cfg->hp_pins); - if (spec->detect_line) { - enable_unsol_pins(codec, cfg->line_outs, - cfg->line_out_pins, - CONEXANT_LINE_EVENT, - cx_auto_line_automute); - spec->line_present = - detect_jacks(codec, cfg->line_outs, - cfg->line_out_pins); - } - } - cx_auto_update_speakers(codec); - /* turn on all EAPDs if no individual EAPD control is available */ - if (!spec->pin_eapd_ctrls) - cx_auto_turn_eapd(codec, spec->num_eapds, spec->eapds, true); - clear_unsol_on_unused_pins(codec); -} - -static void cx_auto_init_input(struct hda_codec *codec) -{ - struct conexant_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - int i, val; - - for (i = 0; i < spec->num_adc_nids; i++) { - hda_nid_t nid = spec->adc_nids[i]; - if (!(get_wcaps(codec, nid) & AC_WCAP_IN_AMP)) - continue; - if (query_amp_caps(codec, nid, HDA_INPUT) & AC_AMPCAP_MUTE) - val = AMP_IN_MUTE(0); - else - val = AMP_IN_UNMUTE(0); - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, - val); - } - - for (i = 0; i < cfg->num_inputs; i++) { - hda_nid_t pin = cfg->inputs[i].pin; - unsigned int type = PIN_IN; - if (cfg->inputs[i].type == AUTO_PIN_MIC) - type |= snd_hda_get_default_vref(codec, pin); - snd_hda_set_pin_ctl(codec, pin, type); - } - - if (spec->auto_mic) { - if (spec->auto_mic_ext >= 0) { - snd_hda_jack_detect_enable_callback(codec, - cfg->inputs[spec->auto_mic_ext].pin, - CONEXANT_MIC_EVENT, cx_auto_automic); - } - if (spec->auto_mic_dock >= 0) { - snd_hda_jack_detect_enable_callback(codec, - cfg->inputs[spec->auto_mic_dock].pin, - CONEXANT_MIC_EVENT, cx_auto_automic); - } - cx_auto_automic(codec, NULL); - } else { - select_input_connection(codec, spec->imux_info[0].adc, - spec->imux_info[0].pin); - } -} - -static void cx_auto_init_digital(struct hda_codec *codec) -{ - struct conexant_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - - if (spec->multiout.dig_out_nid) - snd_hda_set_pin_ctl(codec, cfg->dig_out_pins[0], PIN_OUT); - if (spec->dig_in_nid) - snd_hda_set_pin_ctl(codec, cfg->dig_in_pin, PIN_IN); -} - -static int cx_auto_init(struct hda_codec *codec) -{ - struct conexant_spec *spec = codec->spec; - snd_hda_apply_verbs(codec); - cx_auto_init_output(codec); - cx_auto_init_input(codec); - cx_auto_init_digital(codec); - snd_hda_sync_vmaster_hook(&spec->vmaster_mute); - return 0; -} - -static int cx_auto_add_volume_idx(struct hda_codec *codec, const char *basename, - const char *dir, int cidx, - hda_nid_t nid, int hda_dir, int amp_idx, int chs) -{ - static char name[44]; - static struct snd_kcontrol_new knew[] = { - HDA_CODEC_VOLUME(name, 0, 0, 0), - HDA_CODEC_MUTE(name, 0, 0, 0), - }; - static const char * const sfx[2] = { "Volume", "Switch" }; - int i, err; - - for (i = 0; i < 2; i++) { - struct snd_kcontrol *kctl; - knew[i].private_value = HDA_COMPOSE_AMP_VAL(nid, chs, amp_idx, - hda_dir); - knew[i].subdevice = HDA_SUBDEV_AMP_FLAG; - knew[i].index = cidx; - snprintf(name, sizeof(name), "%s%s %s", basename, dir, sfx[i]); - kctl = snd_ctl_new1(&knew[i], codec); - if (!kctl) - return -ENOMEM; - err = snd_hda_ctl_add(codec, nid, kctl); - if (err < 0) - return err; - if (!(query_amp_caps(codec, nid, hda_dir) & - (AC_AMPCAP_MUTE | AC_AMPCAP_MIN_MUTE))) - break; - } - return 0; -} - -#define cx_auto_add_volume(codec, str, dir, cidx, nid, hda_dir) \ - cx_auto_add_volume_idx(codec, str, dir, cidx, nid, hda_dir, 0, 3) - -#define cx_auto_add_pb_volume(codec, nid, str, idx) \ - cx_auto_add_volume(codec, str, " Playback", idx, nid, HDA_OUTPUT) - -static int try_add_pb_volume(struct hda_codec *codec, hda_nid_t dac, - hda_nid_t pin, const char *name, int idx) -{ - unsigned int caps; - if (dac && !(dac & DAC_SLAVE_FLAG)) { - caps = query_amp_caps(codec, dac, HDA_OUTPUT); - if (caps & AC_AMPCAP_NUM_STEPS) - return cx_auto_add_pb_volume(codec, dac, name, idx); - } - caps = query_amp_caps(codec, pin, HDA_OUTPUT); - if (caps & AC_AMPCAP_NUM_STEPS) - return cx_auto_add_pb_volume(codec, pin, name, idx); - return 0; -} - -static bool is_2_1_speaker(struct conexant_spec *spec) -{ - int i, type, num_spk = 0; - - for (i = 0; i < spec->dac_info_filled; i++) { - type = spec->dac_info[i].type; - if (type == AUTO_PIN_LINE_OUT) - type = spec->autocfg.line_out_type; - if (type == AUTO_PIN_SPEAKER_OUT) - num_spk++; - } - return (num_spk == 2 && spec->autocfg.line_out_type != AUTO_PIN_LINE_OUT); -} - -static int cx_auto_build_output_controls(struct hda_codec *codec) -{ - struct conexant_spec *spec = codec->spec; - int i, err; - int num_line = 0, num_hp = 0, num_spk = 0; - bool speaker_2_1; - static const char * const texts[3] = { "Front", "Surround", "CLFE" }; - - if (spec->dac_info_filled == 1) - return try_add_pb_volume(codec, spec->dac_info[0].dac, - spec->dac_info[0].pin, - "Master", 0); - - speaker_2_1 = is_2_1_speaker(spec); - - for (i = 0; i < spec->dac_info_filled; i++) { - const char *label; - int idx, type; - hda_nid_t dac = spec->dac_info[i].dac; - type = spec->dac_info[i].type; - if (type == AUTO_PIN_LINE_OUT) - type = spec->autocfg.line_out_type; - switch (type) { - case AUTO_PIN_LINE_OUT: - default: - label = texts[num_line++]; - idx = 0; - break; - case AUTO_PIN_HP_OUT: - label = "Headphone"; - idx = num_hp++; - break; - case AUTO_PIN_SPEAKER_OUT: - if (speaker_2_1) { - label = num_spk++ ? "Bass Speaker" : "Speaker"; - idx = 0; - } else { - label = "Speaker"; - idx = num_spk++; - } - break; - } - err = try_add_pb_volume(codec, dac, - spec->dac_info[i].pin, - label, idx); - if (err < 0) - return err; - } - - if (spec->auto_mute) { - err = snd_hda_add_new_ctls(codec, cx_automute_mode_enum); - if (err < 0) - return err; - } - - return 0; -} - -/* Returns zero if this is a normal stereo channel, and non-zero if it should - be split in two independent channels. - dest_label must be at least 44 characters. */ -static int cx_auto_get_rightch_label(struct hda_codec *codec, const char *label, - char *dest_label, int nid) -{ - struct conexant_spec *spec = codec->spec; - int i; - - if (!spec->fixup_stereo_dmic) - return 0; - - for (i = 0; i < AUTO_CFG_MAX_INS; i++) { - int def_conf; - if (spec->autocfg.inputs[i].pin != nid) - continue; - - if (spec->autocfg.inputs[i].type != AUTO_PIN_MIC) - return 0; - def_conf = snd_hda_codec_get_pincfg(codec, nid); - if (snd_hda_get_input_pin_attr(def_conf) != INPUT_PIN_ATTR_INT) - return 0; - - /* Finally found the inverted internal mic! */ - snprintf(dest_label, 44, "Inverted %s", label); - return 1; - } - return 0; -} - -static int cx_auto_add_capture_volume(struct hda_codec *codec, hda_nid_t nid, - const char *label, const char *pfx, - int cidx) -{ - struct conexant_spec *spec = codec->spec; - int i; - - for (i = 0; i < spec->num_adc_nids; i++) { - char rightch_label[44]; - hda_nid_t adc_nid = spec->adc_nids[i]; - int idx = get_input_connection(codec, adc_nid, nid); - if (idx < 0) - continue; - if (codec->single_adc_amp) - idx = 0; - - if (cx_auto_get_rightch_label(codec, label, rightch_label, nid)) { - /* Make two independent kcontrols for left and right */ - int err = cx_auto_add_volume_idx(codec, label, pfx, - cidx, adc_nid, HDA_INPUT, idx, 1); - if (err < 0) - return err; - return cx_auto_add_volume_idx(codec, rightch_label, pfx, - cidx, adc_nid, HDA_INPUT, idx, 2); - } - return cx_auto_add_volume_idx(codec, label, pfx, - cidx, adc_nid, HDA_INPUT, idx, 3); - } - return 0; -} - -static int cx_auto_add_boost_volume(struct hda_codec *codec, int idx, - const char *label, int cidx) -{ - struct conexant_spec *spec = codec->spec; - hda_nid_t mux, nid; - int i, con; - - nid = spec->imux_info[idx].pin; - if (get_wcaps(codec, nid) & AC_WCAP_IN_AMP) { - char rightch_label[44]; - if (cx_auto_get_rightch_label(codec, label, rightch_label, nid)) { - int err = cx_auto_add_volume_idx(codec, label, " Boost", - cidx, nid, HDA_INPUT, 0, 1); - if (err < 0) - return err; - return cx_auto_add_volume_idx(codec, rightch_label, " Boost", - cidx, nid, HDA_INPUT, 0, 2); - } - return cx_auto_add_volume(codec, label, " Boost", cidx, - nid, HDA_INPUT); - } - con = __select_input_connection(codec, spec->imux_info[idx].adc, nid, - &mux, false, 0); - if (con < 0) - return 0; - for (i = 0; i < idx; i++) { - if (spec->imux_info[i].boost == mux) - return 0; /* already present */ - } - - if (get_wcaps(codec, mux) & AC_WCAP_OUT_AMP) { - spec->imux_info[idx].boost = mux; - return cx_auto_add_volume(codec, label, " Boost", cidx, - mux, HDA_OUTPUT); - } - return 0; -} - -static int cx_auto_build_input_controls(struct hda_codec *codec) -{ - struct conexant_spec *spec = codec->spec; - struct hda_input_mux *imux = &spec->private_imux; - const char *prev_label; - int input_conn[HDA_MAX_NUM_INPUTS]; - int i, j, err, cidx; - int multi_connection; - - if (!imux->num_items) - return 0; - - multi_connection = 0; - for (i = 0; i < imux->num_items; i++) { - cidx = get_input_connection(codec, spec->imux_info[i].adc, - spec->imux_info[i].pin); - if (cidx < 0) - continue; - input_conn[i] = spec->imux_info[i].adc; - if (!codec->single_adc_amp) - input_conn[i] |= cidx << 8; - if (i > 0 && input_conn[i] != input_conn[0]) - multi_connection = 1; - } - - prev_label = NULL; - cidx = 0; - for (i = 0; i < imux->num_items; i++) { - hda_nid_t nid = spec->imux_info[i].pin; - const char *label; - - label = hda_get_autocfg_input_label(codec, &spec->autocfg, - spec->imux_info[i].index); - if (label == prev_label) - cidx++; - else - cidx = 0; - prev_label = label; - - err = cx_auto_add_boost_volume(codec, i, label, cidx); - if (err < 0) - return err; - - if (!multi_connection) { - if (i > 0) - continue; - err = cx_auto_add_capture_volume(codec, nid, - "Capture", "", cidx); - } else { - bool dup_found = false; - for (j = 0; j < i; j++) { - if (input_conn[j] == input_conn[i]) { - dup_found = true; - break; - } - } - if (dup_found) - continue; - err = cx_auto_add_capture_volume(codec, nid, - label, " Capture", cidx); - } - if (err < 0) - return err; - } - - if (spec->private_imux.num_items > 1 && !spec->auto_mic) { - err = snd_hda_add_new_ctls(codec, cx_auto_capture_mixers); - if (err < 0) - return err; - } - - return 0; -} - static int cx_auto_build_controls(struct hda_codec *codec) { - struct conexant_spec *spec = codec->spec; int err; - err = cx_auto_build_output_controls(codec); - if (err < 0) - return err; - err = cx_auto_build_input_controls(codec); + err = snd_hda_gen_build_controls(codec); if (err < 0) return err; - err = conexant_build_controls(codec); - if (err < 0) - return err; - err = snd_hda_jack_add_kctls(codec, &spec->autocfg); + + err = add_beep_ctls(codec); if (err < 0) return err; - if (spec->vmaster_mute.sw_kctl) { - spec->vmaster_mute.hook = cx_auto_vmaster_hook; - err = snd_hda_add_vmaster_hook(codec, &spec->vmaster_mute, - spec->vmaster_mute_led); - if (err < 0) - return err; - } - return 0; -} - -static int cx_auto_search_adcs(struct hda_codec *codec) -{ - struct conexant_spec *spec = codec->spec; - hda_nid_t nid, end_nid; - end_nid = codec->start_nid + codec->num_nodes; - for (nid = codec->start_nid; nid < end_nid; nid++) { - unsigned int caps = get_wcaps(codec, nid); - if (get_wcaps_type(caps) != AC_WID_AUD_IN) - continue; - if (caps & AC_WCAP_DIGITAL) - continue; - if (snd_BUG_ON(spec->num_adc_nids >= - ARRAY_SIZE(spec->private_adc_nids))) - break; - spec->private_adc_nids[spec->num_adc_nids++] = nid; - } - spec->adc_nids = spec->private_adc_nids; return 0; } static const struct hda_codec_ops cx_auto_patch_ops = { .build_controls = cx_auto_build_controls, - .build_pcms = conexant_build_pcms, - .init = cx_auto_init, - .free = conexant_free, + .build_pcms = snd_hda_gen_build_pcms, + .init = snd_hda_gen_init, + .free = snd_hda_gen_free, .unsol_event = snd_hda_jack_unsol_event, }; @@ -4408,7 +3208,7 @@ static void cxt_fixup_stereo_dmic(struct hda_codec *codec, const struct hda_fixup *fix, int action) { struct conexant_spec *spec = codec->spec; - spec->fixup_stereo_dmic = 1; + spec->gen.inv_dmic_split = 1; } static void cxt5066_increase_mic_boost(struct hda_codec *codec, @@ -4529,8 +3329,14 @@ static int patch_conexant_auto(struct hda_codec *codec) spec = kzalloc(sizeof(*spec), GFP_KERNEL); if (!spec) return -ENOMEM; + snd_hda_gen_spec_init(&spec->gen); codec->spec = spec; + cx_auto_parse_beep(codec); + cx_auto_parse_eapd(codec); + if (spec->gen.own_eapd_ctl) + spec->gen.vmaster_mute.hook = cx_auto_vmaster_hook; + switch (codec->vendor_id) { case 0x14f15045: codec->single_adc_amp = 1; @@ -4546,8 +3352,6 @@ static int patch_conexant_auto(struct hda_codec *codec) break; } - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); - /* Show mute-led control only on HP laptops * This is a sort of white-list: on HP laptops, EAPD corresponds * only to the mute-LED without actualy amp function. Meanwhile, @@ -4556,20 +3360,20 @@ static int patch_conexant_auto(struct hda_codec *codec) */ switch (codec->subsystem_id >> 16) { case 0x103c: - spec->vmaster_mute_led = 1; + spec->gen.vmaster_mute_enum = 1; break; } - err = cx_auto_search_adcs(codec); + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); + + err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL, 0); if (err < 0) - return err; - err = cx_auto_parse_auto_config(codec); - if (err < 0) { - kfree(codec->spec); - codec->spec = NULL; - return err; - } - spec->capture_stream = &cx_auto_pcm_analog_capture; + goto error; + + err = snd_hda_gen_parse_auto_config(codec, &spec->gen.autocfg); + if (err < 0) + goto error; + codec->patch_ops = cx_auto_patch_ops; if (spec->beep_amp) snd_hda_attach_beep_device(codec, spec->beep_amp); @@ -4586,6 +3390,10 @@ static int patch_conexant_auto(struct hda_codec *codec) } return 0; + + error: + snd_hda_gen_free(codec); + return err; } /* -- cgit v1.2.3 From bf92d1d5032d82e507cb328810cd7e1a046c1d0f Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 20 Dec 2012 16:38:19 +0100 Subject: ALSA: hda - Rearrange for dropping static quirk codes in Coexant driver Just shuffle the codes and add ifdefs for testing to drop the static quirk codes from patch_conexant.c. By commenting out ENABLE_CXT_STATIC_QUIRKS define at the beginning of the file, you can disable the whole static codes. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_conexant.c | 100 +++++++++++++++++++++++------------------ 1 file changed, 57 insertions(+), 43 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index 1ff5f3cc96b1..2f94acb16bde 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -35,6 +35,8 @@ #include "hda_jack.h" #include "hda_generic.h" +#define ENABLE_CXT_STATIC_QUIRKS + #define CXT_PIN_DIR_IN 0x00 #define CXT_PIN_DIR_OUT 0x01 #define CXT_PIN_DIR_INOUT 0x02 @@ -57,6 +59,13 @@ struct conexant_spec { struct hda_gen_spec gen; + unsigned int beep_amp; + + /* extra EAPD pins */ + unsigned int num_eapds; + hda_nid_t eapds[4]; + +#ifdef ENABLE_CXT_STATIC_QUIRKS const struct snd_kcontrol_new *mixers[5]; int num_mixers; hda_nid_t vmaster_nid; @@ -125,14 +134,48 @@ struct conexant_spec { unsigned int dc_enable; unsigned int dc_input_bias; /* offset into cxt5066_olpc_dc_bias */ unsigned int mic_boost; /* offset into cxt5066_analog_mic_boost */ +#endif /* ENABLE_CXT_STATIC_QUIRKS */ +}; - unsigned int beep_amp; - /* extra EAPD pins */ - unsigned int num_eapds; - hda_nid_t eapds[4]; +#ifdef CONFIG_SND_HDA_INPUT_BEEP +#define set_beep_amp(spec, nid, idx, dir) \ + ((spec)->beep_amp = HDA_COMPOSE_AMP_VAL(nid, 1, idx, dir)) +/* additional beep mixers; the actual parameters are overwritten at build */ +static const struct snd_kcontrol_new cxt_beep_mixer[] = { + HDA_CODEC_VOLUME_MONO("Beep Playback Volume", 0, 1, 0, HDA_OUTPUT), + HDA_CODEC_MUTE_BEEP_MONO("Beep Playback Switch", 0, 1, 0, HDA_OUTPUT), + { } /* end */ }; +/* create beep controls if needed */ +static int add_beep_ctls(struct hda_codec *codec) +{ + struct conexant_spec *spec = codec->spec; + int err; + + if (spec->beep_amp) { + const struct snd_kcontrol_new *knew; + for (knew = cxt_beep_mixer; knew->name; knew++) { + struct snd_kcontrol *kctl; + kctl = snd_ctl_new1(knew, codec); + if (!kctl) + return -ENOMEM; + kctl->private_value = spec->beep_amp; + err = snd_hda_ctl_add(codec, 0, kctl); + if (err < 0) + return err; + } + } + return 0; +} +#else +#define set_beep_amp(spec, nid, idx, dir) /* NOP */ +#define add_beep_ctls(codec) 0 +#endif + + +#ifdef ENABLE_CXT_STATIC_QUIRKS static int conexant_playback_pcm_open(struct hda_pcm_stream *hinfo, struct hda_codec *codec, struct snd_pcm_substream *substream) @@ -423,38 +466,6 @@ static const struct snd_kcontrol_new cxt_capture_mixers[] = { {} }; -#ifdef CONFIG_SND_HDA_INPUT_BEEP -/* additional beep mixers; the actual parameters are overwritten at build */ -static const struct snd_kcontrol_new cxt_beep_mixer[] = { - HDA_CODEC_VOLUME_MONO("Beep Playback Volume", 0, 1, 0, HDA_OUTPUT), - HDA_CODEC_MUTE_BEEP_MONO("Beep Playback Switch", 0, 1, 0, HDA_OUTPUT), - { } /* end */ -}; -/* create beep controls if needed */ -static int add_beep_ctls(struct hda_codec *codec) -{ - struct conexant_spec *spec = codec->spec; - int err; - - if (spec->beep_amp) { - const struct snd_kcontrol_new *knew; - for (knew = cxt_beep_mixer; knew->name; knew++) { - struct snd_kcontrol *kctl; - kctl = snd_ctl_new1(knew, codec); - if (!kctl) - return -ENOMEM; - kctl->private_value = spec->beep_amp; - err = snd_hda_ctl_add(codec, 0, kctl); - if (err < 0) - return err; - } - } - return 0; -} -#else -#define add_beep_ctls(codec) 0 -#endif - static const char * const slave_pfxs[] = { "Headphone", "Speaker", "Bass Speaker", "Front", "Surround", "CLFE", NULL @@ -531,13 +542,6 @@ static const struct hda_codec_ops conexant_patch_ops = { .set_power_state = conexant_set_power, }; -#ifdef CONFIG_SND_HDA_INPUT_BEEP -#define set_beep_amp(spec, nid, idx, dir) \ - ((spec)->beep_amp = HDA_COMPOSE_AMP_VAL(nid, 1, idx, dir)) -#else -#define set_beep_amp(spec, nid, idx, dir) /* NOP */ -#endif - static int patch_conexant_auto(struct hda_codec *codec); /* * EAPD control @@ -3100,6 +3104,9 @@ static int patch_cxt5066(struct hda_codec *codec) return 0; } +#endif /* ENABLE_CXT_STATIC_QUIRKS */ + + /* * Automatic parser for CX20641 & co */ @@ -3396,6 +3403,13 @@ static int patch_conexant_auto(struct hda_codec *codec) return err; } +#ifndef ENABLE_CXT_STATIC_QUIRKS +#define patch_cxt5045 patch_conexant_auto +#define patch_cxt5047 patch_conexant_auto +#define patch_cxt5051 patch_conexant_auto +#define patch_cxt5066 patch_conexant_auto +#endif + /* */ -- cgit v1.2.3 From 78bb3cb0e24066f78168968de5f8e19615f1854e Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 21 Dec 2012 15:17:06 +0100 Subject: ALSA: hda - Add generic parser support to Analog Device codec driver This patch adds the support for the generic auto-parser to AD codec driver. For AD1988, the old code is replaced simply with the new generic parser. For other codecs, new model "auto" is added and directed to use the generic parser. No fixup codes have been implemented yet as of now. Eventually we'd replace each static quirk with the generic parser + fixup. Signed-off-by: Takashi Iwai --- sound/pci/hda/Kconfig | 1 + sound/pci/hda/patch_analog.c | 915 ++++++++++++++----------------------------- 2 files changed, 292 insertions(+), 624 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig index 206f678f41d1..4962441e6cf7 100644 --- a/sound/pci/hda/Kconfig +++ b/sound/pci/hda/Kconfig @@ -99,6 +99,7 @@ config SND_HDA_CODEC_REALTEK config SND_HDA_CODEC_ANALOG bool "Build Analog Device HD-audio codec support" default y + select SND_HDA_GENERIC help Say Y here to include Analog Device HD-audio codec support in snd-hda-intel driver, such as AD1986A. diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index 308a5b9e6b9d..02fe0d1da6e0 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c @@ -31,11 +31,15 @@ #include "hda_auto_parser.h" #include "hda_beep.h" #include "hda_jack.h" +#include "hda_generic.h" struct ad198x_spec { + struct hda_gen_spec gen; + const struct snd_kcontrol_new *mixers[6]; int num_mixers; unsigned int beep_amp; /* beep amp value, set via set_beep_amp() */ + hda_nid_t beep_dev_nid; const struct hda_verb *init_verbs[6]; /* initialization verbs * don't forget NULL termination! */ @@ -49,11 +53,6 @@ struct ad198x_spec { unsigned int cur_eapd; unsigned int need_dac_fix; - const hda_nid_t *alt_dac_nid; - const struct hda_pcm_stream *stream_analog_alt_playback; - int independent_hp; - int num_active_streams; - /* capture */ unsigned int num_adc_nids; const hda_nid_t *adc_nids; @@ -73,15 +72,8 @@ struct ad198x_spec { unsigned int spdif_route; - /* dynamic controls, init_verbs and input_mux */ - struct auto_pin_cfg autocfg; - struct snd_array kctls; - struct hda_input_mux private_imux; - hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS]; - unsigned int jack_present: 1; unsigned int inv_jack_detect: 1;/* inverted jack-detection */ - unsigned int inv_eapd: 1; /* inverted EAPD implementation */ unsigned int analog_beep: 1; /* analog beep input present */ unsigned int avoid_init_slave_vol:1; @@ -150,8 +142,6 @@ static const char * const ad1988_6stack_fp_slave_pfxs[] = { NULL }; -static void ad198x_free_kctls(struct hda_codec *codec); - #ifdef CONFIG_SND_HDA_INPUT_BEEP /* additional beep mixers; the actual parameters are overwritten at build */ static const struct snd_kcontrol_new ad_beep_mixer[] = { @@ -172,6 +162,33 @@ static const struct snd_kcontrol_new ad_beep2_mixer[] = { #define set_beep_amp(spec, nid, idx, dir) /* NOP */ #endif +#ifdef CONFIG_SND_HDA_INPUT_BEEP +static int create_beep_ctls(struct hda_codec *codec) +{ + struct ad198x_spec *spec = codec->spec; + const struct snd_kcontrol_new *knew; + + if (!spec->beep_amp) + return 0; + + knew = spec->analog_beep ? ad_beep2_mixer : ad_beep_mixer; + for ( ; knew->name; knew++) { + int err; + struct snd_kcontrol *kctl; + kctl = snd_ctl_new1(knew, codec); + if (!kctl) + return -ENOMEM; + kctl->private_value = spec->beep_amp; + err = snd_hda_ctl_add(codec, 0, kctl); + if (err < 0) + return err; + } + return 0; +} +#else +#define create_beep_ctls(codec) 0 +#endif + static int ad198x_build_controls(struct hda_codec *codec) { struct ad198x_spec *spec = codec->spec; @@ -203,22 +220,9 @@ static int ad198x_build_controls(struct hda_codec *codec) } /* create beep controls if needed */ -#ifdef CONFIG_SND_HDA_INPUT_BEEP - if (spec->beep_amp) { - const struct snd_kcontrol_new *knew; - knew = spec->analog_beep ? ad_beep2_mixer : ad_beep_mixer; - for ( ; knew->name; knew++) { - struct snd_kcontrol *kctl; - kctl = snd_ctl_new1(knew, codec); - if (!kctl) - return -ENOMEM; - kctl->private_value = spec->beep_amp; - err = snd_hda_ctl_add(codec, 0, kctl); - if (err < 0) - return err; - } - } -#endif + err = create_beep_ctls(codec); + if (err < 0) + return err; /* if we have no master control, let's create it */ if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) { @@ -244,8 +248,6 @@ static int ad198x_build_controls(struct hda_codec *codec) return err; } - ad198x_free_kctls(codec); /* no longer needed */ - /* assign Capture Source enums to NID */ kctl = snd_hda_find_mixer_ctl(codec, "Capture Source"); if (!kctl) @@ -277,72 +279,6 @@ static int ad198x_check_power_status(struct hda_codec *codec, hda_nid_t nid) } #endif -static void activate_ctl(struct hda_codec *codec, const char *name, int active) -{ - struct snd_kcontrol *ctl = snd_hda_find_mixer_ctl(codec, name); - if (ctl) { - ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE; - ctl->vd[0].access |= active ? 0 : - SNDRV_CTL_ELEM_ACCESS_INACTIVE; - ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_WRITE; - ctl->vd[0].access |= active ? - SNDRV_CTL_ELEM_ACCESS_WRITE : 0; - snd_ctl_notify(codec->bus->card, - SNDRV_CTL_EVENT_MASK_INFO, &ctl->id); - } -} - -static void set_stream_active(struct hda_codec *codec, bool active) -{ - struct ad198x_spec *spec = codec->spec; - if (active) - spec->num_active_streams++; - else - spec->num_active_streams--; - activate_ctl(codec, "Independent HP", spec->num_active_streams == 0); -} - -static int ad1988_independent_hp_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - static const char * const texts[] = { "OFF", "ON", NULL}; - int index; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 2; - index = uinfo->value.enumerated.item; - if (index >= 2) - index = 1; - strcpy(uinfo->value.enumerated.name, texts[index]); - return 0; -} - -static int ad1988_independent_hp_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct ad198x_spec *spec = codec->spec; - ucontrol->value.enumerated.item[0] = spec->independent_hp; - return 0; -} - -static int ad1988_independent_hp_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct ad198x_spec *spec = codec->spec; - unsigned int select = ucontrol->value.enumerated.item[0]; - if (spec->independent_hp != select) { - spec->independent_hp = select; - if (spec->independent_hp) - spec->multiout.hp_nid = 0; - else - spec->multiout.hp_nid = spec->alt_dac_nid[0]; - return 1; - } - return 0; -} - /* * Analog playback callbacks */ @@ -351,15 +287,8 @@ static int ad198x_playback_pcm_open(struct hda_pcm_stream *hinfo, struct snd_pcm_substream *substream) { struct ad198x_spec *spec = codec->spec; - int err; - set_stream_active(codec, true); - err = snd_hda_multi_out_analog_open(codec, &spec->multiout, substream, + return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream, hinfo); - if (err < 0) { - set_stream_active(codec, false); - return err; - } - return 0; } static int ad198x_playback_pcm_prepare(struct hda_pcm_stream *hinfo, @@ -381,43 +310,6 @@ static int ad198x_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout); } -static int ad198x_playback_pcm_close(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - set_stream_active(codec, false); - return 0; -} - -static int ad1988_alt_playback_pcm_open(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct ad198x_spec *spec = codec->spec; - if (!spec->independent_hp) - return -EBUSY; - set_stream_active(codec, true); - return 0; -} - -static int ad1988_alt_playback_pcm_close(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - set_stream_active(codec, false); - return 0; -} - -static const struct hda_pcm_stream ad198x_pcm_analog_alt_playback = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, - .ops = { - .open = ad1988_alt_playback_pcm_open, - .close = ad1988_alt_playback_pcm_close - }, -}; - /* * Digital out */ @@ -491,7 +383,6 @@ static const struct hda_pcm_stream ad198x_pcm_analog_playback = { .open = ad198x_playback_pcm_open, .prepare = ad198x_playback_pcm_prepare, .cleanup = ad198x_playback_pcm_cleanup, - .close = ad198x_playback_pcm_close }, }; @@ -556,43 +447,18 @@ static int ad198x_build_pcms(struct hda_codec *codec) } } - if (spec->alt_dac_nid && spec->stream_analog_alt_playback) { - codec->num_pcms++; - info = spec->pcm_rec + 2; - info->name = "AD198x Headphone"; - info->pcm_type = HDA_PCM_TYPE_AUDIO; - info->stream[SNDRV_PCM_STREAM_PLAYBACK] = - *spec->stream_analog_alt_playback; - info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = - spec->alt_dac_nid[0]; - } - return 0; } -static void ad198x_free_kctls(struct hda_codec *codec) -{ - struct ad198x_spec *spec = codec->spec; - - if (spec->kctls.list) { - struct snd_kcontrol_new *kctl = spec->kctls.list; - int i; - for (i = 0; i < spec->kctls.used; i++) - kfree(kctl[i].name); - } - snd_array_free(&spec->kctls); -} - static void ad198x_power_eapd_write(struct hda_codec *codec, hda_nid_t front, hda_nid_t hp) { - struct ad198x_spec *spec = codec->spec; if (snd_hda_query_pin_caps(codec, front) & AC_PINCAP_EAPD) snd_hda_codec_write(codec, front, 0, AC_VERB_SET_EAPD_BTLENABLE, - !spec->inv_eapd ? 0x00 : 0x02); + !codec->inv_eapd ? 0x00 : 0x02); if (snd_hda_query_pin_caps(codec, hp) & AC_PINCAP_EAPD) snd_hda_codec_write(codec, hp, 0, AC_VERB_SET_EAPD_BTLENABLE, - !spec->inv_eapd ? 0x00 : 0x02); + !codec->inv_eapd ? 0x00 : 0x02); } static void ad198x_power_eapd(struct hda_codec *codec) @@ -636,7 +502,7 @@ static void ad198x_free(struct hda_codec *codec) if (!spec) return; - ad198x_free_kctls(codec); + snd_hda_gen_spec_free(&spec->gen); kfree(spec); snd_hda_detach_beep_device(codec); } @@ -673,7 +539,7 @@ static int ad198x_eapd_get(struct snd_kcontrol *kcontrol, { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct ad198x_spec *spec = codec->spec; - if (spec->inv_eapd) + if (codec->inv_eapd) ucontrol->value.integer.value[0] = ! spec->cur_eapd; else ucontrol->value.integer.value[0] = spec->cur_eapd; @@ -688,7 +554,7 @@ static int ad198x_eapd_put(struct snd_kcontrol *kcontrol, hda_nid_t nid = kcontrol->private_value & 0xff; unsigned int eapd; eapd = !!ucontrol->value.integer.value[0]; - if (spec->inv_eapd) + if (codec->inv_eapd) eapd = !eapd; if (eapd == spec->cur_eapd) return 0; @@ -707,6 +573,66 @@ static int ad198x_ch_mode_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); +/* + * Automatic parse of I/O pins from the BIOS configuration + */ + +static int ad198x_auto_build_controls(struct hda_codec *codec) +{ + int err; + + err = snd_hda_gen_build_controls(codec); + if (err < 0) + return err; + err = create_beep_ctls(codec); + if (err < 0) + return err; + return 0; +} + +static const struct hda_codec_ops ad198x_auto_patch_ops = { + .build_controls = ad198x_auto_build_controls, + .build_pcms = snd_hda_gen_build_pcms, + .init = snd_hda_gen_init, + .free = ad198x_free, +#ifdef CONFIG_PM + .check_power_status = snd_hda_gen_check_power_status, + .suspend = ad198x_suspend, +#endif + .reboot_notify = ad198x_shutup, +}; + + +static int ad198x_parse_auto_config(struct hda_codec *codec) +{ + struct ad198x_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->gen.autocfg; + int err; + + codec->spdif_status_reset = 1; + codec->no_trigger_sense = 1; + codec->no_sticky_stream = 1; + + spec->gen.indep_hp = 1; + + err = snd_hda_parse_pin_defcfg(codec, cfg, NULL, 0); + if (err < 0) + return err; + err = snd_hda_gen_parse_auto_config(codec, cfg); + if (err < 0) + return err; + + if (spec->beep_dev_nid) { + err = snd_hda_attach_beep_device(codec, spec->beep_dev_nid); + if (err < 0) + return err; + } + + codec->patch_ops = ad198x_auto_patch_ops; + + return 0; +} + /* * AD1986A specific */ @@ -1168,6 +1094,7 @@ static int ad1986a_samsung_p50_init(struct hda_codec *codec) /* models */ enum { + AD1986A_AUTO, AD1986A_6STACK, AD1986A_3STACK, AD1986A_LAPTOP, @@ -1180,6 +1107,7 @@ enum { }; static const char * const ad1986a_models[AD1986A_MODELS] = { + [AD1986A_AUTO] = "auto", [AD1986A_6STACK] = "6stack", [AD1986A_3STACK] = "3stack", [AD1986A_LAPTOP] = "laptop", @@ -1246,10 +1174,33 @@ static int alloc_ad_spec(struct hda_codec *codec) if (!spec) return -ENOMEM; codec->spec = spec; - snd_array_init(&spec->kctls, sizeof(struct snd_kcontrol_new), 32); + snd_hda_gen_spec_init(&spec->gen); return 0; } +/* + */ +static int ad1986a_parse_auto_config(struct hda_codec *codec) +{ + struct ad198x_spec *spec = codec->spec; + + /* AD1986A has the inverted EAPD implementation */ + codec->inv_eapd = 1; + + spec->beep_dev_nid = 0x19; + set_beep_amp(spec, 0x18, 0, HDA_OUTPUT); + + /* AD1986A has a hardware problem that it can't share a stream + * with multiple output pins. The copy of front to surrounds + * causes noisy or silent outputs at a certain timing, e.g. + * changing the volume. + * So, let's disable the shared stream. + */ + spec->gen.multiout.no_share_stream = 1; + + return ad198x_parse_auto_config(codec); +} + static int patch_ad1986a(struct hda_codec *codec) { struct ad198x_spec *spec; @@ -1260,6 +1211,18 @@ static int patch_ad1986a(struct hda_codec *codec) return err; spec = codec->spec; + board_config = snd_hda_check_board_config(codec, AD1986A_MODELS, + ad1986a_models, + ad1986a_cfg_tbl); + if (board_config == AD1986A_AUTO) { + err = ad1986a_parse_auto_config(codec); + if (err < 0) { + ad198x_free(codec); + return err; + } + return 0; + } + err = snd_hda_attach_beep_device(codec, 0x19); if (err < 0) { ad198x_free(codec); @@ -1283,14 +1246,11 @@ static int patch_ad1986a(struct hda_codec *codec) spec->loopback.amplist = ad1986a_loopbacks; #endif spec->vmaster_nid = 0x1b; - spec->inv_eapd = 1; /* AD1986A has the inverted EAPD implementation */ + codec->inv_eapd = 1; /* AD1986A has the inverted EAPD implementation */ codec->patch_ops = ad198x_patch_ops; /* override some parameters */ - board_config = snd_hda_check_board_config(codec, AD1986A_MODELS, - ad1986a_models, - ad1986a_cfg_tbl); switch (board_config) { case AD1986A_3STACK: spec->num_mixers = 2; @@ -1546,9 +1506,31 @@ static const struct hda_amp_list ad1983_loopbacks[] = { }; #endif +/* models */ +enum { + AD1983_AUTO, + AD1983_BASIC, + AD1983_MODELS +}; + +static const char * const ad1983_models[AD1983_MODELS] = { + [AD1983_AUTO] = "auto", + [AD1983_BASIC] = "basic", +}; + +static int ad1983_parse_auto_config(struct hda_codec *codec) +{ + struct ad198x_spec *spec = codec->spec; + + spec->beep_dev_nid = 0x10; + set_beep_amp(spec, 0x10, 0, HDA_OUTPUT); + return ad198x_parse_auto_config(codec); +} + static int patch_ad1983(struct hda_codec *codec) { struct ad198x_spec *spec; + int board_config; int err; err = alloc_ad_spec(codec); @@ -1556,6 +1538,17 @@ static int patch_ad1983(struct hda_codec *codec) return err; spec = codec->spec; + board_config = snd_hda_check_board_config(codec, AD1983_MODELS, + ad1983_models, NULL); + if (board_config == AD1983_AUTO) { + err = ad1983_parse_auto_config(codec); + if (err < 0) { + ad198x_free(codec); + return err; + } + return 0; + } + err = snd_hda_attach_beep_device(codec, 0x10); if (err < 0) { ad198x_free(codec); @@ -1924,6 +1917,7 @@ static const struct hda_input_mux ad1981_thinkpad_capture_source = { /* models */ enum { + AD1981_AUTO, AD1981_BASIC, AD1981_HP, AD1981_THINKPAD, @@ -1932,6 +1926,7 @@ enum { }; static const char * const ad1981_models[AD1981_MODELS] = { + [AD1981_AUTO] = "auto", [AD1981_HP] = "hp", [AD1981_THINKPAD] = "thinkpad", [AD1981_BASIC] = "basic", @@ -1951,6 +1946,15 @@ static const struct snd_pci_quirk ad1981_cfg_tbl[] = { {} }; +static int ad1981_parse_auto_config(struct hda_codec *codec) +{ + struct ad198x_spec *spec = codec->spec; + + spec->beep_dev_nid = 0x10; + set_beep_amp(spec, 0x0d, 0, HDA_OUTPUT); + return ad198x_parse_auto_config(codec); +} + static int patch_ad1981(struct hda_codec *codec) { struct ad198x_spec *spec; @@ -1961,6 +1965,18 @@ static int patch_ad1981(struct hda_codec *codec) return -ENOMEM; spec = codec->spec; + board_config = snd_hda_check_board_config(codec, AD1981_MODELS, + ad1981_models, + ad1981_cfg_tbl); + if (board_config == AD1981_AUTO) { + err = ad1981_parse_auto_config(codec); + if (err < 0) { + ad198x_free(codec); + return err; + } + return 0; + } + err = snd_hda_attach_beep_device(codec, 0x10); if (err < 0) { ad198x_free(codec); @@ -1989,9 +2005,6 @@ static int patch_ad1981(struct hda_codec *codec) codec->patch_ops = ad198x_patch_ops; /* override some parameters */ - board_config = snd_hda_check_board_config(codec, AD1981_MODELS, - ad1981_models, - ad1981_cfg_tbl); switch (board_config) { case AD1981_HP: spec->mixers[0] = ad1981_hp_mixers; @@ -2131,13 +2144,13 @@ static int patch_ad1981(struct hda_codec *codec) /* models */ enum { + AD1988_AUTO, AD1988_6STACK, AD1988_6STACK_DIG, AD1988_3STACK, AD1988_3STACK_DIG, AD1988_LAPTOP, AD1988_LAPTOP_DIG, - AD1988_AUTO, AD1988_MODEL_LAST, }; @@ -2242,17 +2255,6 @@ static int ad198x_ch_mode_put(struct snd_kcontrol *kcontrol, return err; } -static const struct snd_kcontrol_new ad1988_hp_mixers[] = { - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Independent HP", - .info = ad1988_independent_hp_info, - .get = ad1988_independent_hp_get, - .put = ad1988_independent_hp_put, - }, - { } /* end */ -}; - /* 6-stack mode */ static const struct snd_kcontrol_new ad1988_6stack_mixers1[] = { HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT), @@ -2817,414 +2819,15 @@ static const struct hda_amp_list ad1988_loopbacks[] = { #endif /* - * Automatic parse of I/O pins from the BIOS configuration */ -enum { - AD_CTL_WIDGET_VOL, - AD_CTL_WIDGET_MUTE, - AD_CTL_BIND_MUTE, -}; -static const struct snd_kcontrol_new ad1988_control_templates[] = { - HDA_CODEC_VOLUME(NULL, 0, 0, 0), - HDA_CODEC_MUTE(NULL, 0, 0, 0), - HDA_BIND_MUTE(NULL, 0, 0, 0), -}; - -/* add dynamic controls */ -static int add_control(struct ad198x_spec *spec, int type, const char *name, - unsigned long val) -{ - struct snd_kcontrol_new *knew; - - knew = snd_array_new(&spec->kctls); - if (!knew) - return -ENOMEM; - *knew = ad1988_control_templates[type]; - knew->name = kstrdup(name, GFP_KERNEL); - if (! knew->name) - return -ENOMEM; - if (get_amp_nid_(val)) - knew->subdevice = HDA_SUBDEV_AMP_FLAG; - knew->private_value = val; - return 0; -} - -#define AD1988_PIN_CD_NID 0x18 -#define AD1988_PIN_BEEP_NID 0x10 - -static const hda_nid_t ad1988_mixer_nids[8] = { - /* A B C D E F G H */ - 0x22, 0x2b, 0x2c, 0x29, 0x26, 0x2a, 0x27, 0x28 -}; - -static inline hda_nid_t ad1988_idx_to_dac(struct hda_codec *codec, int idx) -{ - static const hda_nid_t idx_to_dac[8] = { - /* A B C D E F G H */ - 0x03, 0x06, 0x05, 0x04, 0x0a, 0x06, 0x05, 0x0a - }; - static const hda_nid_t idx_to_dac_rev2[8] = { - /* A B C D E F G H */ - 0x03, 0x05, 0x0a, 0x04, 0x06, 0x05, 0x0a, 0x06 - }; - if (is_rev2(codec)) - return idx_to_dac_rev2[idx]; - else - return idx_to_dac[idx]; -} - -static const hda_nid_t ad1988_boost_nids[8] = { - 0x38, 0x39, 0x3a, 0x3d, 0x3c, 0x3b, 0, 0 -}; - -static int ad1988_pin_idx(hda_nid_t nid) -{ - static const hda_nid_t ad1988_io_pins[8] = { - 0x11, 0x14, 0x15, 0x12, 0x17, 0x16, 0x24, 0x25 - }; - int i; - for (i = 0; i < ARRAY_SIZE(ad1988_io_pins); i++) - if (ad1988_io_pins[i] == nid) - return i; - return 0; /* should be -1 */ -} - -static int ad1988_pin_to_loopback_idx(hda_nid_t nid) -{ - static const int loopback_idx[8] = { - 2, 0, 1, 3, 4, 5, 1, 4 - }; - switch (nid) { - case AD1988_PIN_CD_NID: - return 6; - default: - return loopback_idx[ad1988_pin_idx(nid)]; - } -} - -static int ad1988_pin_to_adc_idx(hda_nid_t nid) -{ - static const int adc_idx[8] = { - 0, 1, 2, 8, 4, 3, 6, 7 - }; - switch (nid) { - case AD1988_PIN_CD_NID: - return 5; - default: - return adc_idx[ad1988_pin_idx(nid)]; - } -} - -/* fill in the dac_nids table from the parsed pin configuration */ -static int ad1988_auto_fill_dac_nids(struct hda_codec *codec, - const struct auto_pin_cfg *cfg) -{ - struct ad198x_spec *spec = codec->spec; - int i, idx; - - spec->multiout.dac_nids = spec->private_dac_nids; - - /* check the pins hardwired to audio widget */ - for (i = 0; i < cfg->line_outs; i++) { - idx = ad1988_pin_idx(cfg->line_out_pins[i]); - spec->private_dac_nids[i] = ad1988_idx_to_dac(codec, idx); - } - spec->multiout.num_dacs = cfg->line_outs; - return 0; -} - -/* add playback controls from the parsed DAC table */ -static int ad1988_auto_create_multi_out_ctls(struct ad198x_spec *spec, - const struct auto_pin_cfg *cfg) -{ - char name[32]; - static const char * const chname[4] = { - "Front", "Surround", NULL /*CLFE*/, "Side" - }; - hda_nid_t nid; - int i, err; - - for (i = 0; i < cfg->line_outs; i++) { - hda_nid_t dac = spec->multiout.dac_nids[i]; - if (! dac) - continue; - nid = ad1988_mixer_nids[ad1988_pin_idx(cfg->line_out_pins[i])]; - if (i == 2) { - /* Center/LFE */ - err = add_control(spec, AD_CTL_WIDGET_VOL, - "Center Playback Volume", - HDA_COMPOSE_AMP_VAL(dac, 1, 0, HDA_OUTPUT)); - if (err < 0) - return err; - err = add_control(spec, AD_CTL_WIDGET_VOL, - "LFE Playback Volume", - HDA_COMPOSE_AMP_VAL(dac, 2, 0, HDA_OUTPUT)); - if (err < 0) - return err; - err = add_control(spec, AD_CTL_BIND_MUTE, - "Center Playback Switch", - HDA_COMPOSE_AMP_VAL(nid, 1, 2, HDA_INPUT)); - if (err < 0) - return err; - err = add_control(spec, AD_CTL_BIND_MUTE, - "LFE Playback Switch", - HDA_COMPOSE_AMP_VAL(nid, 2, 2, HDA_INPUT)); - if (err < 0) - return err; - } else { - sprintf(name, "%s Playback Volume", chname[i]); - err = add_control(spec, AD_CTL_WIDGET_VOL, name, - HDA_COMPOSE_AMP_VAL(dac, 3, 0, HDA_OUTPUT)); - if (err < 0) - return err; - sprintf(name, "%s Playback Switch", chname[i]); - err = add_control(spec, AD_CTL_BIND_MUTE, name, - HDA_COMPOSE_AMP_VAL(nid, 3, 2, HDA_INPUT)); - if (err < 0) - return err; - } - } - return 0; -} - -/* add playback controls for speaker and HP outputs */ -static int ad1988_auto_create_extra_out(struct hda_codec *codec, hda_nid_t pin, - const char *pfx) -{ - struct ad198x_spec *spec = codec->spec; - hda_nid_t nid; - int i, idx, err; - char name[32]; - - if (! pin) - return 0; - - idx = ad1988_pin_idx(pin); - nid = ad1988_idx_to_dac(codec, idx); - /* check whether the corresponding DAC was already taken */ - for (i = 0; i < spec->autocfg.line_outs; i++) { - hda_nid_t pin = spec->autocfg.line_out_pins[i]; - hda_nid_t dac = ad1988_idx_to_dac(codec, ad1988_pin_idx(pin)); - if (dac == nid) - break; - } - if (i >= spec->autocfg.line_outs) { - /* specify the DAC as the extra output */ - if (!spec->multiout.hp_nid) - spec->multiout.hp_nid = nid; - else - spec->multiout.extra_out_nid[0] = nid; - /* control HP volume/switch on the output mixer amp */ - sprintf(name, "%s Playback Volume", pfx); - err = add_control(spec, AD_CTL_WIDGET_VOL, name, - HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT)); - if (err < 0) - return err; - } - nid = ad1988_mixer_nids[idx]; - sprintf(name, "%s Playback Switch", pfx); - if ((err = add_control(spec, AD_CTL_BIND_MUTE, name, - HDA_COMPOSE_AMP_VAL(nid, 3, 2, HDA_INPUT))) < 0) - return err; - return 0; -} - -/* create input playback/capture controls for the given pin */ -static int new_analog_input(struct ad198x_spec *spec, hda_nid_t pin, - const char *ctlname, int ctlidx, int boost) -{ - char name[32]; - int err, idx; - - sprintf(name, "%s Playback Volume", ctlname); - idx = ad1988_pin_to_loopback_idx(pin); - if ((err = add_control(spec, AD_CTL_WIDGET_VOL, name, - HDA_COMPOSE_AMP_VAL(0x20, 3, idx, HDA_INPUT))) < 0) - return err; - sprintf(name, "%s Playback Switch", ctlname); - if ((err = add_control(spec, AD_CTL_WIDGET_MUTE, name, - HDA_COMPOSE_AMP_VAL(0x20, 3, idx, HDA_INPUT))) < 0) - return err; - if (boost) { - hda_nid_t bnid; - idx = ad1988_pin_idx(pin); - bnid = ad1988_boost_nids[idx]; - if (bnid) { - sprintf(name, "%s Boost Volume", ctlname); - return add_control(spec, AD_CTL_WIDGET_VOL, name, - HDA_COMPOSE_AMP_VAL(bnid, 3, idx, HDA_OUTPUT)); - - } - } - return 0; -} - -/* create playback/capture controls for input pins */ -static int ad1988_auto_create_analog_input_ctls(struct hda_codec *codec, - const struct auto_pin_cfg *cfg) -{ - struct ad198x_spec *spec = codec->spec; - struct hda_input_mux *imux = &spec->private_imux; - int i, err, type, type_idx; - - for (i = 0; i < cfg->num_inputs; i++) { - const char *label; - type = cfg->inputs[i].type; - label = hda_get_autocfg_input_label(codec, cfg, i); - snd_hda_add_imux_item(imux, label, - ad1988_pin_to_adc_idx(cfg->inputs[i].pin), - &type_idx); - err = new_analog_input(spec, cfg->inputs[i].pin, - label, type_idx, - type == AUTO_PIN_MIC); - if (err < 0) - return err; - } - snd_hda_add_imux_item(imux, "Mix", 9, NULL); - - if ((err = add_control(spec, AD_CTL_WIDGET_VOL, - "Analog Mix Playback Volume", - HDA_COMPOSE_AMP_VAL(0x21, 3, 0x0, HDA_OUTPUT))) < 0) - return err; - if ((err = add_control(spec, AD_CTL_WIDGET_MUTE, - "Analog Mix Playback Switch", - HDA_COMPOSE_AMP_VAL(0x21, 3, 0x0, HDA_OUTPUT))) < 0) - return err; - - return 0; -} - -static void ad1988_auto_set_output_and_unmute(struct hda_codec *codec, - hda_nid_t nid, int pin_type, - int dac_idx) -{ - /* set as output */ - snd_hda_set_pin_ctl(codec, nid, pin_type); - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE); - switch (nid) { - case 0x11: /* port-A - DAC 03 */ - snd_hda_codec_write(codec, 0x37, 0, AC_VERB_SET_CONNECT_SEL, 0x00); - break; - case 0x14: /* port-B - DAC 06 */ - snd_hda_codec_write(codec, 0x30, 0, AC_VERB_SET_CONNECT_SEL, 0x02); - break; - case 0x15: /* port-C - DAC 05 */ - snd_hda_codec_write(codec, 0x31, 0, AC_VERB_SET_CONNECT_SEL, 0x00); - break; - case 0x17: /* port-E - DAC 0a */ - snd_hda_codec_write(codec, 0x32, 0, AC_VERB_SET_CONNECT_SEL, 0x01); - break; - case 0x13: /* mono - DAC 04 */ - snd_hda_codec_write(codec, 0x36, 0, AC_VERB_SET_CONNECT_SEL, 0x01); - break; - } -} - -static void ad1988_auto_init_multi_out(struct hda_codec *codec) -{ - struct ad198x_spec *spec = codec->spec; - int i; - - for (i = 0; i < spec->autocfg.line_outs; i++) { - hda_nid_t nid = spec->autocfg.line_out_pins[i]; - ad1988_auto_set_output_and_unmute(codec, nid, PIN_OUT, i); - } -} - -static void ad1988_auto_init_extra_out(struct hda_codec *codec) -{ - struct ad198x_spec *spec = codec->spec; - hda_nid_t pin; - - pin = spec->autocfg.speaker_pins[0]; - if (pin) /* connect to front */ - ad1988_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0); - pin = spec->autocfg.hp_pins[0]; - if (pin) /* connect to front */ - ad1988_auto_set_output_and_unmute(codec, pin, PIN_HP, 0); -} - -static void ad1988_auto_init_analog_input(struct hda_codec *codec) -{ - struct ad198x_spec *spec = codec->spec; - const struct auto_pin_cfg *cfg = &spec->autocfg; - int i, idx; - - for (i = 0; i < cfg->num_inputs; i++) { - hda_nid_t nid = cfg->inputs[i].pin; - int type = cfg->inputs[i].type; - int val; - switch (nid) { - case 0x15: /* port-C */ - snd_hda_codec_write(codec, 0x33, 0, AC_VERB_SET_CONNECT_SEL, 0x0); - break; - case 0x17: /* port-E */ - snd_hda_codec_write(codec, 0x34, 0, AC_VERB_SET_CONNECT_SEL, 0x0); - break; - } - val = PIN_IN; - if (type == AUTO_PIN_MIC) - val |= snd_hda_get_default_vref(codec, nid); - snd_hda_set_pin_ctl(codec, nid, val); - if (nid != AD1988_PIN_CD_NID) - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, - AMP_OUT_MUTE); - idx = ad1988_pin_idx(nid); - if (ad1988_boost_nids[idx]) - snd_hda_codec_write(codec, ad1988_boost_nids[idx], 0, - AC_VERB_SET_AMP_GAIN_MUTE, - AMP_OUT_ZERO); - } -} - -/* parse the BIOS configuration and set up the alc_spec */ -/* return 1 if successful, 0 if the proper config is not found, or a negative error code */ static int ad1988_parse_auto_config(struct hda_codec *codec) { struct ad198x_spec *spec = codec->spec; - int err; - - if ((err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL)) < 0) - return err; - if ((err = ad1988_auto_fill_dac_nids(codec, &spec->autocfg)) < 0) - return err; - if (! spec->autocfg.line_outs) - return 0; /* can't find valid BIOS pin config */ - if ((err = ad1988_auto_create_multi_out_ctls(spec, &spec->autocfg)) < 0 || - (err = ad1988_auto_create_extra_out(codec, - spec->autocfg.speaker_pins[0], - "Speaker")) < 0 || - (err = ad1988_auto_create_extra_out(codec, spec->autocfg.hp_pins[0], - "Headphone")) < 0 || - (err = ad1988_auto_create_analog_input_ctls(codec, &spec->autocfg)) < 0) - return err; - - spec->multiout.max_channels = spec->multiout.num_dacs * 2; - - if (spec->autocfg.dig_outs) - spec->multiout.dig_out_nid = AD1988_SPDIF_OUT; - if (spec->autocfg.dig_in_pin) - spec->dig_in_nid = AD1988_SPDIF_IN; - - if (spec->kctls.list) - spec->mixers[spec->num_mixers++] = spec->kctls.list; - - spec->init_verbs[spec->num_init_verbs++] = ad1988_6stack_init_verbs; - - spec->input_mux = &spec->private_imux; - return 1; -} - -/* init callback for auto-configuration model -- overriding the default init */ -static int ad1988_auto_init(struct hda_codec *codec) -{ - ad198x_init(codec); - ad1988_auto_init_multi_out(codec); - ad1988_auto_init_extra_out(codec); - ad1988_auto_init_analog_input(codec); - return 0; + spec->beep_dev_nid = 0x10; + set_beep_amp(spec, 0x10, 0, HDA_OUTPUT); + return ad198x_parse_auto_config(codec); } /* @@ -3259,9 +2862,6 @@ static int patch_ad1988(struct hda_codec *codec) return err; spec = codec->spec; - if (is_rev2(codec)) - snd_printk(KERN_INFO "patch_analog: AD1988A rev.2 is detected, enable workarounds\n"); - board_config = snd_hda_check_board_config(codec, AD1988_MODEL_LAST, ad1988_models, ad1988_cfg_tbl); if (board_config < 0) { @@ -3276,12 +2876,13 @@ static int patch_ad1988(struct hda_codec *codec) if (err < 0) { ad198x_free(codec); return err; - } else if (! err) { - printk(KERN_INFO "hda_codec: Cannot set up configuration from BIOS. Using 6-stack mode...\n"); - board_config = AD1988_6STACK; } + return 0; } + if (is_rev2(codec)) + snd_printk(KERN_INFO "patch_analog: AD1988A rev.2 is detected, enable workarounds\n"); + err = snd_hda_attach_beep_device(codec, 0x10); if (err < 0) { ad198x_free(codec); @@ -3344,7 +2945,7 @@ static int patch_ad1988(struct hda_codec *codec) spec->input_mux = &ad1988_laptop_capture_source; spec->num_mixers = 1; spec->mixers[0] = ad1988_laptop_mixers; - spec->inv_eapd = 1; /* inverted EAPD */ + codec->inv_eapd = 1; /* inverted EAPD */ spec->num_init_verbs = 1; spec->init_verbs[0] = ad1988_laptop_init_verbs; if (board_config == AD1988_LAPTOP_DIG) @@ -3352,15 +2953,6 @@ static int patch_ad1988(struct hda_codec *codec) break; } - if (spec->autocfg.hp_pins[0]) { - spec->mixers[spec->num_mixers++] = ad1988_hp_mixers; - spec->slave_vols = ad1988_6stack_fp_slave_pfxs; - spec->slave_sws = ad1988_6stack_fp_slave_pfxs; - spec->alt_dac_nid = ad1988_alt_dac_nid; - spec->stream_analog_alt_playback = - &ad198x_pcm_analog_alt_playback; - } - spec->num_adc_nids = ARRAY_SIZE(ad1988_adc_nids); spec->adc_nids = ad1988_adc_nids; spec->capsrc_nids = ad1988_capsrc_nids; @@ -3388,9 +2980,6 @@ static int patch_ad1988(struct hda_codec *codec) codec->patch_ops = ad198x_patch_ops; switch (board_config) { - case AD1988_AUTO: - codec->patch_ops.init = ad1988_auto_init; - break; case AD1988_LAPTOP: case AD1988_LAPTOP_DIG: codec->patch_ops.unsol_event = ad1988_laptop_unsol_event; @@ -3568,7 +3157,43 @@ static const char * const ad1884_slave_vols[] = { NULL }; -static int patch_ad1884(struct hda_codec *codec) +enum { + AD1884_AUTO, + AD1884_BASIC, + AD1884_MODELS +}; + +static const char * const ad1884_models[AD1884_MODELS] = { + [AD1884_AUTO] = "auto", + [AD1884_BASIC] = "basic", +}; + +static int ad1884_parse_auto_config(struct hda_codec *codec) +{ + struct ad198x_spec *spec = codec->spec; + + spec->beep_dev_nid = 0x10; + set_beep_amp(spec, 0x10, 0, HDA_OUTPUT); + return ad198x_parse_auto_config(codec); +} + +static int patch_ad1884_auto(struct hda_codec *codec) +{ + int err; + + err = alloc_ad_spec(codec); + if (err < 0) + return err; + + err = ad1884_parse_auto_config(codec); + if (err < 0) { + ad198x_free(codec); + return err; + } + return 0; +} + +static int patch_ad1884_basic(struct hda_codec *codec) { struct ad198x_spec *spec; int err; @@ -3615,6 +3240,18 @@ static int patch_ad1884(struct hda_codec *codec) return 0; } +static int patch_ad1884(struct hda_codec *codec) +{ + int board_config; + + board_config = snd_hda_check_board_config(codec, AD1884_MODELS, + ad1884_models, NULL); + if (board_config == AD1884_AUTO) + return patch_ad1884_auto(codec); + else + return patch_ad1884_basic(codec); +} + /* * Lenovo Thinkpad T61/X61 */ @@ -3787,6 +3424,7 @@ static int ad1984_build_pcms(struct hda_codec *codec) /* models */ enum { + AD1984_AUTO, AD1984_BASIC, AD1984_THINKPAD, AD1984_DELL_DESKTOP, @@ -3794,6 +3432,7 @@ enum { }; static const char * const ad1984_models[AD1984_MODELS] = { + [AD1984_AUTO] = "auto", [AD1984_BASIC] = "basic", [AD1984_THINKPAD] = "thinkpad", [AD1984_DELL_DESKTOP] = "dell_desktop", @@ -3812,12 +3451,16 @@ static int patch_ad1984(struct hda_codec *codec) struct ad198x_spec *spec; int board_config, err; - err = patch_ad1884(codec); + board_config = snd_hda_check_board_config(codec, AD1984_MODELS, + ad1984_models, ad1984_cfg_tbl); + if (board_config == AD1984_AUTO) + return patch_ad1884_auto(codec); + + err = patch_ad1884_basic(codec); if (err < 0) return err; spec = codec->spec; - board_config = snd_hda_check_board_config(codec, AD1984_MODELS, - ad1984_models, ad1984_cfg_tbl); + switch (board_config) { case AD1984_BASIC: /* additional digital mics */ @@ -4534,6 +4177,7 @@ static int ad1984a_touchsmart_init(struct hda_codec *codec) */ enum { + AD1884A_AUTO, AD1884A_DESKTOP, AD1884A_LAPTOP, AD1884A_MOBILE, @@ -4544,6 +4188,7 @@ enum { }; static const char * const ad1884a_models[AD1884A_MODELS] = { + [AD1884A_AUTO] = "auto", [AD1884A_DESKTOP] = "desktop", [AD1884A_LAPTOP] = "laptop", [AD1884A_MOBILE] = "mobile", @@ -4572,6 +4217,12 @@ static int patch_ad1884a(struct hda_codec *codec) struct ad198x_spec *spec; int err, board_config; + board_config = snd_hda_check_board_config(codec, AD1884A_MODELS, + ad1884a_models, + ad1884a_cfg_tbl); + if (board_config == AD1884_AUTO) + return patch_ad1884_auto(codec); + err = alloc_ad_spec(codec); if (err < 0) return err; @@ -4603,9 +4254,6 @@ static int patch_ad1884a(struct hda_codec *codec) codec->patch_ops = ad198x_patch_ops; /* override some parameters */ - board_config = snd_hda_check_board_config(codec, AD1884A_MODELS, - ad1884a_models, - ad1884a_cfg_tbl); switch (board_config) { case AD1884A_LAPTOP: spec->mixers[0] = ad1884a_laptop_mixers; @@ -4966,6 +4614,7 @@ static const struct hda_amp_list ad1882_loopbacks[] = { /* models */ enum { + AD1882_AUTO, AD1882_3STACK, AD1882_6STACK, AD1882_3STACK_AUTOMUTE, @@ -4973,11 +4622,20 @@ enum { }; static const char * const ad1882_models[AD1986A_MODELS] = { + [AD1882_AUTO] = "auto", [AD1882_3STACK] = "3stack", [AD1882_6STACK] = "6stack", [AD1882_3STACK_AUTOMUTE] = "3stack-automute", }; +static int ad1882_parse_auto_config(struct hda_codec *codec) +{ + struct ad198x_spec *spec = codec->spec; + + spec->beep_dev_nid = 0x10; + set_beep_amp(spec, 0x10, 0, HDA_OUTPUT); + return ad198x_parse_auto_config(codec); +} static int patch_ad1882(struct hda_codec *codec) { @@ -4989,6 +4647,17 @@ static int patch_ad1882(struct hda_codec *codec) return err; spec = codec->spec; + board_config = snd_hda_check_board_config(codec, AD1882_MODELS, + ad1882_models, NULL); + if (board_config == AD1882_AUTO) { + err = ad1882_parse_auto_config(codec); + if (err < 0) { + ad198x_free(codec); + return err; + } + return 0; + } + err = snd_hda_attach_beep_device(codec, 0x10); if (err < 0) { ad198x_free(codec); @@ -5024,8 +4693,6 @@ static int patch_ad1882(struct hda_codec *codec) codec->patch_ops = ad198x_patch_ops; /* override some parameters */ - board_config = snd_hda_check_board_config(codec, AD1882_MODELS, - ad1882_models, NULL); switch (board_config) { default: case AD1882_3STACK: -- cgit v1.2.3 From b3f6008f2d511133e0f04782c437a13b6865d26b Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 7 Jan 2013 12:27:36 +0100 Subject: ALSA: hda - Use generic parser for VIA codec driver Yet another step forward. As all features for VIA codecs have been implemented in the generic driver, we can move on to migrate the VIA codec parser, too. Signed-off-by: Takashi Iwai --- sound/pci/hda/Kconfig | 1 + sound/pci/hda/patch_via.c | 2784 +++++---------------------------------------- 2 files changed, 293 insertions(+), 2492 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig index 4962441e6cf7..4004d405e082 100644 --- a/sound/pci/hda/Kconfig +++ b/sound/pci/hda/Kconfig @@ -124,6 +124,7 @@ config SND_HDA_CODEC_SIGMATEL config SND_HDA_CODEC_VIA bool "Build VIA HD-audio codec support" default y + select SND_HDA_GENERIC help Say Y here to include VIA HD-audio codec support in snd-hda-intel driver, such as VT1708. diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index d3c852ab105e..eade21c3e0b1 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -56,6 +56,7 @@ #include "hda_local.h" #include "hda_auto_parser.h" #include "hda_jack.h" +#include "hda_generic.h" /* Pin Widget NID */ #define VT1708_HP_PIN_NID 0x20 @@ -86,40 +87,9 @@ enum VIA_HDA_CODEC { (spec)->codec_type == VT1812 ||\ (spec)->codec_type == VT1802) -#define MAX_NID_PATH_DEPTH 5 - -/* output-path: DAC -> ... -> pin - * idx[] contains the source index number of the next widget; - * e.g. idx[0] is the index of the DAC selected by path[1] widget - * multi[] indicates whether it's a selector widget with multi-connectors - * (i.e. the connection selection is mandatory) - * vol_ctl and mute_ctl contains the NIDs for the assigned mixers - */ -struct nid_path { - int depth; - hda_nid_t path[MAX_NID_PATH_DEPTH]; - unsigned char idx[MAX_NID_PATH_DEPTH]; - unsigned char multi[MAX_NID_PATH_DEPTH]; - unsigned int vol_ctl; - unsigned int mute_ctl; -}; - -/* input-path */ -struct via_input { - hda_nid_t pin; /* input-pin or aa-mix */ - int adc_idx; /* ADC index to be used */ - int mux_idx; /* MUX index (if any) */ - const char *label; /* input-source label */ -}; - -#define VIA_MAX_ADCS 3 - -enum { - STREAM_MULTI_OUT = (1 << 0), - STREAM_INDEP_HP = (1 << 1), -}; - struct via_spec { + struct hda_gen_spec gen; + /* codec parameterization */ const struct snd_kcontrol_new *mixers[6]; unsigned int num_mixers; @@ -127,77 +97,7 @@ struct via_spec { const struct hda_verb *init_verbs[5]; unsigned int num_iverbs; - char stream_name_analog[32]; - char stream_name_hp[32]; - const struct hda_pcm_stream *stream_analog_playback; - const struct hda_pcm_stream *stream_analog_capture; - - char stream_name_digital[32]; - const struct hda_pcm_stream *stream_digital_playback; - const struct hda_pcm_stream *stream_digital_capture; - - /* playback */ - struct hda_multi_out multiout; - hda_nid_t slave_dig_outs[2]; - hda_nid_t hp_dac_nid; - hda_nid_t speaker_dac_nid; - int hp_indep_shared; /* indep HP-DAC is shared with side ch */ - int opened_streams; /* STREAM_* bits */ - int active_streams; /* STREAM_* bits */ - int aamix_mode; /* loopback is enabled for output-path? */ - - /* Output-paths: - * There are different output-paths depending on the setup. - * out_path, hp_path and speaker_path are primary paths. If both - * direct DAC and aa-loopback routes are available, these contain - * the former paths. Meanwhile *_mix_path contain the paths with - * loopback mixer. (Since the loopback is only for front channel, - * no out_mix_path for surround channels.) - * The HP output has another path, hp_indep_path, which is used in - * the independent-HP mode. - */ - struct nid_path out_path[HDA_SIDE + 1]; - struct nid_path out_mix_path; - struct nid_path hp_path; - struct nid_path hp_mix_path; - struct nid_path hp_indep_path; - struct nid_path speaker_path; - struct nid_path speaker_mix_path; - - /* capture */ - unsigned int num_adc_nids; - hda_nid_t adc_nids[VIA_MAX_ADCS]; - hda_nid_t mux_nids[VIA_MAX_ADCS]; - hda_nid_t aa_mix_nid; - hda_nid_t dig_in_nid; - - /* capture source */ - bool dyn_adc_switch; - int num_inputs; - struct via_input inputs[AUTO_CFG_MAX_INS + 1]; - unsigned int cur_mux[VIA_MAX_ADCS]; - - /* dynamic DAC switching */ - unsigned int cur_dac_stream_tag; - unsigned int cur_dac_format; - unsigned int cur_hp_stream_tag; - unsigned int cur_hp_format; - - /* dynamic ADC switching */ - hda_nid_t cur_adc; - unsigned int cur_adc_stream_tag; - unsigned int cur_adc_format; - - /* PCM information */ - struct hda_pcm pcm_rec[3]; - - /* dynamic controls, init_verbs and input_mux */ - struct auto_pin_cfg autocfg; - struct snd_array kctls; - hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS]; - /* HP mode source */ - unsigned int hp_independent_mode; unsigned int dmic_enabled; unsigned int no_pin_power_ctl; enum VIA_HDA_CODEC codec_type; @@ -205,36 +105,22 @@ struct via_spec { /* analog low-power control */ bool alc_mode; - /* smart51 setup */ - unsigned int smart51_nums; - hda_nid_t smart51_pins[2]; - int smart51_idxs[2]; - const char *smart51_labels[2]; - unsigned int smart51_enabled; - /* work to check hp jack state */ - struct hda_codec *codec; - struct delayed_work vt1708_hp_work; int hp_work_active; int vt1708_jack_detect; - int vt1708_hp_present; void (*set_widgets_power_state)(struct hda_codec *codec); unsigned int dac_stream_tag[4]; - - struct hda_loopback_check loopback; - int num_loopbacks; - struct hda_amp_list loopback_list[8]; - - /* bind capture-volume */ - struct hda_bind_ctls *bind_cap_vol; - struct hda_bind_ctls *bind_cap_sw; - - struct mutex config_mutex; }; static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec); -static struct via_spec * via_new_spec(struct hda_codec *codec) +static void via_playback_pcm_hook(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream, + int action); +static void via_hp_automute(struct hda_codec *codec, struct hda_jack_tbl *tbl); + +static struct via_spec *via_new_spec(struct hda_codec *codec) { struct via_spec *spec; @@ -242,14 +128,14 @@ static struct via_spec * via_new_spec(struct hda_codec *codec) if (spec == NULL) return NULL; - snd_array_init(&spec->kctls, sizeof(struct snd_kcontrol_new), 32); - mutex_init(&spec->config_mutex); codec->spec = spec; - spec->codec = codec; + snd_hda_gen_spec_init(&spec->gen); spec->codec_type = get_codec_type(codec); /* VT1708BCE & VT1708S are almost same */ if (spec->codec_type == VT1708BCE) spec->codec_type = VT1708S; + spec->no_pin_power_ctl = 1; + spec->gen.pcm_playback_hook = via_playback_pcm_hook; return spec; } @@ -305,16 +191,6 @@ static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec) return codec_type; }; -#define VIA_JACK_EVENT 0x20 -#define VIA_HP_EVENT 0x01 -#define VIA_LINE_EVENT 0x03 - -enum { - VIA_CTL_WIDGET_VOL, - VIA_CTL_WIDGET_MUTE, - VIA_CTL_WIDGET_ANALOG_MUTE, -}; - static void analog_low_current_mode(struct hda_codec *codec); static bool is_aa_path_mute(struct hda_codec *codec); @@ -322,31 +198,35 @@ static bool is_aa_path_mute(struct hda_codec *codec); (snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") == 1 && \ !is_aa_path_mute(codec)) -static void vt1708_stop_hp_work(struct via_spec *spec) +static void vt1708_stop_hp_work(struct hda_codec *codec) { - if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0) + struct via_spec *spec = codec->spec; + if (spec->codec_type != VT1708 || !spec->gen.autocfg.hp_outs) return; if (spec->hp_work_active) { - snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81, 1); - cancel_delayed_work_sync(&spec->vt1708_hp_work); - spec->hp_work_active = 0; + snd_hda_codec_write(codec, 0x1, 0, 0xf81, 1); + cancel_delayed_work_sync(&codec->jackpoll_work); + spec->hp_work_active = false; + codec->jackpoll_interval = 0; } } -static void vt1708_update_hp_work(struct via_spec *spec) +static void vt1708_update_hp_work(struct hda_codec *codec) { - if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0) + struct via_spec *spec = codec->spec; + if (spec->codec_type != VT1708 || !spec->gen.autocfg.hp_outs) return; if (spec->vt1708_jack_detect && - (spec->active_streams || hp_detect_with_aa(spec->codec))) { + (spec->gen.active_streams || hp_detect_with_aa(codec))) { if (!spec->hp_work_active) { - snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81, 0); - schedule_delayed_work(&spec->vt1708_hp_work, - msecs_to_jiffies(100)); - spec->hp_work_active = 1; + codec->jackpoll_interval = msecs_to_jiffies(100); + snd_hda_codec_write(codec, 0x1, 0, 0xf81, 0); + queue_delayed_work(codec->bus->workq, + &codec->jackpoll_work, 0); + spec->hp_work_active = true; } - } else if (!hp_detect_with_aa(spec->codec)) - vt1708_stop_hp_work(spec); + } else if (!hp_detect_with_aa(codec)) + vt1708_stop_hp_work(codec); } static void set_widgets_power_state(struct hda_codec *codec) @@ -356,356 +236,6 @@ static void set_widgets_power_state(struct hda_codec *codec) spec->set_widgets_power_state(codec); } -static int analog_input_switch_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - int change = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol); - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - - set_widgets_power_state(codec); - analog_low_current_mode(snd_kcontrol_chip(kcontrol)); - vt1708_update_hp_work(codec->spec); - return change; -} - -/* modify .put = snd_hda_mixer_amp_switch_put */ -#define ANALOG_INPUT_MUTE \ - { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ - .name = NULL, \ - .index = 0, \ - .info = snd_hda_mixer_amp_switch_info, \ - .get = snd_hda_mixer_amp_switch_get, \ - .put = analog_input_switch_put, \ - .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) } - -static const struct snd_kcontrol_new via_control_templates[] = { - HDA_CODEC_VOLUME(NULL, 0, 0, 0), - HDA_CODEC_MUTE(NULL, 0, 0, 0), - ANALOG_INPUT_MUTE, -}; - - -/* add dynamic controls */ -static struct snd_kcontrol_new *__via_clone_ctl(struct via_spec *spec, - const struct snd_kcontrol_new *tmpl, - const char *name) -{ - struct snd_kcontrol_new *knew; - - knew = snd_array_new(&spec->kctls); - if (!knew) - return NULL; - *knew = *tmpl; - if (!name) - name = tmpl->name; - if (name) { - knew->name = kstrdup(name, GFP_KERNEL); - if (!knew->name) - return NULL; - } - return knew; -} - -static int __via_add_control(struct via_spec *spec, int type, const char *name, - int idx, unsigned long val) -{ - struct snd_kcontrol_new *knew; - - knew = __via_clone_ctl(spec, &via_control_templates[type], name); - if (!knew) - return -ENOMEM; - knew->index = idx; - if (get_amp_nid_(val)) - knew->subdevice = HDA_SUBDEV_AMP_FLAG; - knew->private_value = val; - return 0; -} - -#define via_add_control(spec, type, name, val) \ - __via_add_control(spec, type, name, 0, val) - -#define via_clone_control(spec, tmpl) __via_clone_ctl(spec, tmpl, NULL) - -static void via_free_kctls(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - - if (spec->kctls.list) { - struct snd_kcontrol_new *kctl = spec->kctls.list; - int i; - for (i = 0; i < spec->kctls.used; i++) - kfree(kctl[i].name); - } - snd_array_free(&spec->kctls); -} - -/* create input playback/capture controls for the given pin */ -static int via_new_analog_input(struct via_spec *spec, const char *ctlname, - int type_idx, int idx, int mix_nid) -{ - char name[32]; - int err; - - sprintf(name, "%s Playback Volume", ctlname); - err = __via_add_control(spec, VIA_CTL_WIDGET_VOL, name, type_idx, - HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT)); - if (err < 0) - return err; - sprintf(name, "%s Playback Switch", ctlname); - err = __via_add_control(spec, VIA_CTL_WIDGET_ANALOG_MUTE, name, type_idx, - HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT)); - if (err < 0) - return err; - return 0; -} - -#define get_connection_index(codec, mux, nid) \ - snd_hda_get_conn_index(codec, mux, nid, 0) - -static bool check_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir, - unsigned int mask) -{ - unsigned int caps; - if (!nid) - return false; - caps = get_wcaps(codec, nid); - if (dir == HDA_INPUT) - caps &= AC_WCAP_IN_AMP; - else - caps &= AC_WCAP_OUT_AMP; - if (!caps) - return false; - if (query_amp_caps(codec, nid, dir) & mask) - return true; - return false; -} - -#define have_mute(codec, nid, dir) \ - check_amp_caps(codec, nid, dir, AC_AMPCAP_MUTE) - -/* enable/disable the output-route mixers */ -static void activate_output_mix(struct hda_codec *codec, struct nid_path *path, - hda_nid_t mix_nid, int idx, bool enable) -{ - int i, num, val; - - if (!path) - return; - num = snd_hda_get_num_conns(codec, mix_nid); - for (i = 0; i < num; i++) { - if (i == idx) - val = AMP_IN_UNMUTE(i); - else - val = AMP_IN_MUTE(i); - snd_hda_codec_write(codec, mix_nid, 0, - AC_VERB_SET_AMP_GAIN_MUTE, val); - } -} - -/* enable/disable the output-route */ -static void activate_output_path(struct hda_codec *codec, struct nid_path *path, - bool enable, bool force) -{ - struct via_spec *spec = codec->spec; - int i; - for (i = 0; i < path->depth; i++) { - hda_nid_t src, dst; - int idx = path->idx[i]; - src = path->path[i]; - if (i < path->depth - 1) - dst = path->path[i + 1]; - else - dst = 0; - if (enable && path->multi[i]) - snd_hda_codec_write(codec, dst, 0, - AC_VERB_SET_CONNECT_SEL, idx); - if (!force && (dst == spec->aa_mix_nid)) - continue; - if (have_mute(codec, dst, HDA_INPUT)) - activate_output_mix(codec, path, dst, idx, enable); - if (!force && (src == path->vol_ctl || src == path->mute_ctl)) - continue; - if (have_mute(codec, src, HDA_OUTPUT)) { - int val = enable ? AMP_OUT_UNMUTE : AMP_OUT_MUTE; - snd_hda_codec_write(codec, src, 0, - AC_VERB_SET_AMP_GAIN_MUTE, val); - } - } -} - -/* set the given pin as output */ -static void init_output_pin(struct hda_codec *codec, hda_nid_t pin, - int pin_type) -{ - if (!pin) - return; - snd_hda_set_pin_ctl(codec, pin, pin_type); - if (snd_hda_query_pin_caps(codec, pin) & AC_PINCAP_EAPD) - snd_hda_codec_write(codec, pin, 0, - AC_VERB_SET_EAPD_BTLENABLE, 0x02); -} - -static void via_auto_init_output(struct hda_codec *codec, - struct nid_path *path, int pin_type) -{ - unsigned int caps; - hda_nid_t pin; - - if (!path->depth) - return; - pin = path->path[path->depth - 1]; - - init_output_pin(codec, pin, pin_type); - if (get_wcaps(codec, pin) & AC_WCAP_OUT_AMP) - caps = query_amp_caps(codec, pin, HDA_OUTPUT); - else - caps = 0; - if (caps & AC_AMPCAP_MUTE) { - unsigned int val; - val = (caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT; - snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_AMP_GAIN_MUTE, - AMP_OUT_MUTE | val); - } - activate_output_path(codec, path, true, true); /* force on */ -} - -static void via_auto_init_multi_out(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - struct nid_path *path; - int i; - - for (i = 0; i < spec->autocfg.line_outs + spec->smart51_nums; i++) { - path = &spec->out_path[i]; - if (!i && spec->aamix_mode && spec->out_mix_path.depth) - path = &spec->out_mix_path; - via_auto_init_output(codec, path, PIN_OUT); - } -} - -/* deactivate the inactive headphone-paths */ -static void deactivate_hp_paths(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - int shared = spec->hp_indep_shared; - - if (spec->hp_independent_mode) { - activate_output_path(codec, &spec->hp_path, false, false); - activate_output_path(codec, &spec->hp_mix_path, false, false); - if (shared) - activate_output_path(codec, &spec->out_path[shared], - false, false); - } else if (spec->aamix_mode || !spec->hp_path.depth) { - activate_output_path(codec, &spec->hp_indep_path, false, false); - activate_output_path(codec, &spec->hp_path, false, false); - } else { - activate_output_path(codec, &spec->hp_indep_path, false, false); - activate_output_path(codec, &spec->hp_mix_path, false, false); - } -} - -static void via_auto_init_hp_out(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - - if (!spec->hp_path.depth) { - via_auto_init_output(codec, &spec->hp_mix_path, PIN_HP); - return; - } - deactivate_hp_paths(codec); - if (spec->hp_independent_mode) - via_auto_init_output(codec, &spec->hp_indep_path, PIN_HP); - else if (spec->aamix_mode) - via_auto_init_output(codec, &spec->hp_mix_path, PIN_HP); - else - via_auto_init_output(codec, &spec->hp_path, PIN_HP); -} - -static void via_auto_init_speaker_out(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - - if (!spec->autocfg.speaker_outs) - return; - if (!spec->speaker_path.depth) { - via_auto_init_output(codec, &spec->speaker_mix_path, PIN_OUT); - return; - } - if (!spec->aamix_mode) { - activate_output_path(codec, &spec->speaker_mix_path, - false, false); - via_auto_init_output(codec, &spec->speaker_path, PIN_OUT); - } else { - activate_output_path(codec, &spec->speaker_path, false, false); - via_auto_init_output(codec, &spec->speaker_mix_path, PIN_OUT); - } -} - -static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin); -static void via_hp_automute(struct hda_codec *codec); - -static void via_auto_init_analog_input(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - const struct auto_pin_cfg *cfg = &spec->autocfg; - hda_nid_t conn[HDA_MAX_CONNECTIONS]; - unsigned int ctl; - int i, num_conns; - - /* init ADCs */ - for (i = 0; i < spec->num_adc_nids; i++) { - hda_nid_t nid = spec->adc_nids[i]; - if (!(get_wcaps(codec, nid) & AC_WCAP_IN_AMP) || - !(query_amp_caps(codec, nid, HDA_INPUT) & AC_AMPCAP_MUTE)) - continue; - snd_hda_codec_write(codec, spec->adc_nids[i], 0, - AC_VERB_SET_AMP_GAIN_MUTE, - AMP_IN_UNMUTE(0)); - } - - /* init pins */ - for (i = 0; i < cfg->num_inputs; i++) { - hda_nid_t nid = cfg->inputs[i].pin; - if (spec->smart51_enabled && is_smart51_pins(codec, nid)) - ctl = PIN_OUT; - else { - ctl = PIN_IN; - if (cfg->inputs[i].type == AUTO_PIN_MIC) - ctl |= snd_hda_get_default_vref(codec, nid); - } - snd_hda_set_pin_ctl(codec, nid, ctl); - } - - /* init input-src */ - for (i = 0; i < spec->num_adc_nids; i++) { - int adc_idx = spec->inputs[spec->cur_mux[i]].adc_idx; - /* secondary ADCs must have the unique MUX */ - if (i > 0 && !spec->mux_nids[i]) - break; - if (spec->mux_nids[adc_idx]) { - int mux_idx = spec->inputs[spec->cur_mux[i]].mux_idx; - snd_hda_codec_write(codec, spec->mux_nids[adc_idx], 0, - AC_VERB_SET_CONNECT_SEL, - mux_idx); - } - if (spec->dyn_adc_switch) - break; /* only one input-src */ - } - - /* init aa-mixer */ - if (!spec->aa_mix_nid) - return; - num_conns = snd_hda_get_connections(codec, spec->aa_mix_nid, conn, - ARRAY_SIZE(conn)); - for (i = 0; i < num_conns; i++) { - unsigned int caps = get_wcaps(codec, conn[i]); - if (get_wcaps_type(caps) == AC_WID_PIN) - snd_hda_codec_write(codec, spec->aa_mix_nid, 0, - AC_VERB_SET_AMP_GAIN_MUTE, - AMP_IN_MUTE(i)); - } -} - static void update_power_state(struct hda_codec *codec, hda_nid_t nid, unsigned int parm) { @@ -737,6 +267,23 @@ static void update_conv_power_state(struct hda_codec *codec, hda_nid_t nid, } } +static bool smart51_enabled(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + return spec->gen.ext_channel_count > 2; +} + +static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin) +{ + struct via_spec *spec = codec->spec; + int i; + + for (i = 0; i < spec->gen.multi_ios; i++) + if (spec->gen.multi_io[i].pin == pin) + return true; + return false; +} + static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid, unsigned int *affected_parm) { @@ -751,7 +298,7 @@ static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid, no_presence |= spec->no_pin_power_ctl; if (!no_presence) present = snd_hda_jack_detect(codec, nid); - if ((spec->smart51_enabled && is_smart51_pins(codec, nid)) + if ((smart51_enabled(codec) && is_smart51_pins(codec, nid)) || ((no_presence || present) && get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)) { *affected_parm = AC_PWRST_D0; /* if it's connected */ @@ -792,1801 +339,185 @@ static int via_pin_power_ctl_put(struct snd_kcontrol *kcontrol, return 1; } -static const struct snd_kcontrol_new via_pin_power_ctl_enum = { +static const struct snd_kcontrol_new via_pin_power_ctl_enum[] = { + { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Dynamic Power-Control", .info = via_pin_power_ctl_info, .get = via_pin_power_ctl_get, .put = via_pin_power_ctl_put, + }, + {} /* terminator */ }; -static int via_independent_hp_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - static const char * const texts[] = { "OFF", "ON" }; - - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 2; - if (uinfo->value.enumerated.item >= 2) - uinfo->value.enumerated.item = 1; - strcpy(uinfo->value.enumerated.name, - texts[uinfo->value.enumerated.item]); - return 0; -} - -static int via_independent_hp_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +/* check AA path's mute status */ +static bool is_aa_path_mute(struct hda_codec *codec) { - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct via_spec *spec = codec->spec; + const struct hda_amp_list *p; + int i, ch, v; - ucontrol->value.enumerated.item[0] = spec->hp_independent_mode; - return 0; -} - -/* adjust spec->multiout setup according to the current flags */ -static void setup_playback_multi_pcm(struct via_spec *spec) -{ - const struct auto_pin_cfg *cfg = &spec->autocfg; - spec->multiout.num_dacs = cfg->line_outs + spec->smart51_nums; - spec->multiout.hp_nid = 0; - if (!spec->hp_independent_mode) { - if (!spec->hp_indep_shared) - spec->multiout.hp_nid = spec->hp_dac_nid; - } else { - if (spec->hp_indep_shared) - spec->multiout.num_dacs = cfg->line_outs - 1; + for (i = 0; i < spec->gen.num_loopbacks; i++) { + p = &spec->gen.loopback_list[i]; + for (ch = 0; ch < 2; ch++) { + v = snd_hda_codec_amp_read(codec, p->nid, ch, p->dir, + p->idx); + if (!(v & HDA_AMP_MUTE) && v > 0) + return false; + } } + return true; } -/* update DAC setups according to indep-HP switch; - * this function is called only when indep-HP is modified - */ -static void switch_indep_hp_dacs(struct hda_codec *codec) +/* enter/exit analog low-current mode */ +static void __analog_low_current_mode(struct hda_codec *codec, bool force) { struct via_spec *spec = codec->spec; - int shared = spec->hp_indep_shared; - hda_nid_t shared_dac, hp_dac; + bool enable; + unsigned int verb, parm; - if (!spec->opened_streams) + if (spec->no_pin_power_ctl) + enable = false; + else + enable = is_aa_path_mute(codec) && !spec->gen.active_streams; + if (enable == spec->alc_mode && !force) return; + spec->alc_mode = enable; - shared_dac = shared ? spec->multiout.dac_nids[shared] : 0; - hp_dac = spec->hp_dac_nid; - if (spec->hp_independent_mode) { - /* switch to indep-HP mode */ - if (spec->active_streams & STREAM_MULTI_OUT) { - __snd_hda_codec_cleanup_stream(codec, hp_dac, 1); - __snd_hda_codec_cleanup_stream(codec, shared_dac, 1); - } - if (spec->active_streams & STREAM_INDEP_HP) - snd_hda_codec_setup_stream(codec, hp_dac, - spec->cur_hp_stream_tag, 0, - spec->cur_hp_format); - } else { - /* back to HP or shared-DAC */ - if (spec->active_streams & STREAM_INDEP_HP) - __snd_hda_codec_cleanup_stream(codec, hp_dac, 1); - if (spec->active_streams & STREAM_MULTI_OUT) { - hda_nid_t dac; - int ch; - if (shared_dac) { /* reset mutli-ch DAC */ - dac = shared_dac; - ch = shared * 2; - } else { /* reset HP DAC */ - dac = hp_dac; - ch = 0; - } - snd_hda_codec_setup_stream(codec, dac, - spec->cur_dac_stream_tag, ch, - spec->cur_dac_format); - } - } - setup_playback_multi_pcm(spec); -} - -static int via_independent_hp_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct via_spec *spec = codec->spec; - int cur, shared; - - mutex_lock(&spec->config_mutex); - cur = !!ucontrol->value.enumerated.item[0]; - if (spec->hp_independent_mode == cur) { - mutex_unlock(&spec->config_mutex); - return 0; - } - spec->hp_independent_mode = cur; - shared = spec->hp_indep_shared; - deactivate_hp_paths(codec); - if (cur) - activate_output_path(codec, &spec->hp_indep_path, true, false); - else { - if (shared) - activate_output_path(codec, &spec->out_path[shared], - true, false); - if (spec->aamix_mode || !spec->hp_path.depth) - activate_output_path(codec, &spec->hp_mix_path, - true, false); - else - activate_output_path(codec, &spec->hp_path, - true, false); - } - - switch_indep_hp_dacs(codec); - mutex_unlock(&spec->config_mutex); - - /* update jack power state */ - set_widgets_power_state(codec); - via_hp_automute(codec); - return 1; -} - -static const struct snd_kcontrol_new via_hp_mixer = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Independent HP", - .info = via_independent_hp_info, - .get = via_independent_hp_get, - .put = via_independent_hp_put, -}; - -static int via_hp_build(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - struct snd_kcontrol_new *knew; - hda_nid_t nid; - - nid = spec->autocfg.hp_pins[0]; - knew = via_clone_control(spec, &via_hp_mixer); - if (knew == NULL) - return -ENOMEM; - - knew->subdevice = HDA_SUBDEV_NID_FLAG | nid; - - return 0; -} - -static void notify_aa_path_ctls(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - int i; - - for (i = 0; i < spec->smart51_nums; i++) { - struct snd_kcontrol *ctl; - struct snd_ctl_elem_id id; - memset(&id, 0, sizeof(id)); - id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; - sprintf(id.name, "%s Playback Volume", spec->smart51_labels[i]); - ctl = snd_hda_find_mixer_ctl(codec, id.name); - if (ctl) - snd_ctl_notify(codec->bus->card, - SNDRV_CTL_EVENT_MASK_VALUE, - &ctl->id); - } -} - -static void mute_aa_path(struct hda_codec *codec, int mute) -{ - struct via_spec *spec = codec->spec; - int val = mute ? HDA_AMP_MUTE : HDA_AMP_UNMUTE; - int i; - - /* check AA path's mute status */ - for (i = 0; i < spec->smart51_nums; i++) { - if (spec->smart51_idxs[i] < 0) - continue; - snd_hda_codec_amp_stereo(codec, spec->aa_mix_nid, - HDA_INPUT, spec->smart51_idxs[i], - HDA_AMP_MUTE, val); - } -} - -static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin) -{ - struct via_spec *spec = codec->spec; - int i; - - for (i = 0; i < spec->smart51_nums; i++) - if (spec->smart51_pins[i] == pin) - return true; - return false; -} - -static int via_smart51_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct via_spec *spec = codec->spec; - - *ucontrol->value.integer.value = spec->smart51_enabled; - return 0; -} - -static int via_smart51_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct via_spec *spec = codec->spec; - int out_in = *ucontrol->value.integer.value - ? AC_PINCTL_OUT_EN : AC_PINCTL_IN_EN; - int i; - - for (i = 0; i < spec->smart51_nums; i++) { - hda_nid_t nid = spec->smart51_pins[i]; - unsigned int parm; - - parm = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_PIN_WIDGET_CONTROL, 0); - parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN); - parm |= out_in; - snd_hda_set_pin_ctl(codec, nid, parm); - if (out_in == AC_PINCTL_OUT_EN) { - mute_aa_path(codec, 1); - notify_aa_path_ctls(codec); - } - } - spec->smart51_enabled = *ucontrol->value.integer.value; - set_widgets_power_state(codec); - return 1; -} - -static const struct snd_kcontrol_new via_smart51_mixer = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Smart 5.1", - .count = 1, - .info = snd_ctl_boolean_mono_info, - .get = via_smart51_get, - .put = via_smart51_put, -}; - -static int via_smart51_build(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - - if (!spec->smart51_nums) - return 0; - if (!via_clone_control(spec, &via_smart51_mixer)) - return -ENOMEM; - return 0; -} - -/* check AA path's mute status */ -static bool is_aa_path_mute(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - const struct hda_amp_list *p; - int i, ch, v; - - for (i = 0; i < spec->num_loopbacks; i++) { - p = &spec->loopback_list[i]; - for (ch = 0; ch < 2; ch++) { - v = snd_hda_codec_amp_read(codec, p->nid, ch, p->dir, - p->idx); - if (!(v & HDA_AMP_MUTE) && v > 0) - return false; - } - } - return true; -} - -/* enter/exit analog low-current mode */ -static void __analog_low_current_mode(struct hda_codec *codec, bool force) -{ - struct via_spec *spec = codec->spec; - bool enable; - unsigned int verb, parm; - - if (spec->no_pin_power_ctl) - enable = false; - else - enable = is_aa_path_mute(codec) && !spec->opened_streams; - if (enable == spec->alc_mode && !force) - return; - spec->alc_mode = enable; - - /* decide low current mode's verb & parameter */ - switch (spec->codec_type) { - case VT1708B_8CH: - case VT1708B_4CH: - verb = 0xf70; - parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */ - break; - case VT1708S: - case VT1718S: - case VT1716S: - verb = 0xf73; - parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */ - break; - case VT1702: - verb = 0xf73; - parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */ - break; - case VT2002P: - case VT1812: - case VT1802: - verb = 0xf93; - parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */ - break; - case VT1705CF: - case VT1808: - verb = 0xf82; - parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */ - break; - default: - return; /* other codecs are not supported */ + /* decide low current mode's verb & parameter */ + switch (spec->codec_type) { + case VT1708B_8CH: + case VT1708B_4CH: + verb = 0xf70; + parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */ + break; + case VT1708S: + case VT1718S: + case VT1716S: + verb = 0xf73; + parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */ + break; + case VT1702: + verb = 0xf73; + parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */ + break; + case VT2002P: + case VT1812: + case VT1802: + verb = 0xf93; + parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */ + break; + case VT1705CF: + case VT1808: + verb = 0xf82; + parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */ + break; + default: + return; /* other codecs are not supported */ } /* send verb */ snd_hda_codec_write(codec, codec->afg, 0, verb, parm); } -static void analog_low_current_mode(struct hda_codec *codec) -{ - return __analog_low_current_mode(codec, false); -} - -/* - * generic initialization of ADC, input mixers and output mixers - */ -static const struct hda_verb vt1708_init_verbs[] = { - /* power down jack detect function */ - {0x1, 0xf81, 0x1}, - { } -}; - -static void set_stream_open(struct hda_codec *codec, int bit, bool active) -{ - struct via_spec *spec = codec->spec; - - if (active) - spec->opened_streams |= bit; - else - spec->opened_streams &= ~bit; - analog_low_current_mode(codec); -} - -static int via_playback_multi_pcm_open(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct via_spec *spec = codec->spec; - const struct auto_pin_cfg *cfg = &spec->autocfg; - int err; - - spec->multiout.num_dacs = cfg->line_outs + spec->smart51_nums; - spec->multiout.max_channels = spec->multiout.num_dacs * 2; - set_stream_open(codec, STREAM_MULTI_OUT, true); - err = snd_hda_multi_out_analog_open(codec, &spec->multiout, substream, - hinfo); - if (err < 0) { - set_stream_open(codec, STREAM_MULTI_OUT, false); - return err; - } - return 0; -} - -static int via_playback_multi_pcm_close(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - set_stream_open(codec, STREAM_MULTI_OUT, false); - return 0; -} - -static int via_playback_hp_pcm_open(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct via_spec *spec = codec->spec; - - if (snd_BUG_ON(!spec->hp_dac_nid)) - return -EINVAL; - set_stream_open(codec, STREAM_INDEP_HP, true); - return 0; -} - -static int via_playback_hp_pcm_close(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - set_stream_open(codec, STREAM_INDEP_HP, false); - return 0; -} - -static int via_playback_multi_pcm_prepare(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - unsigned int stream_tag, - unsigned int format, - struct snd_pcm_substream *substream) -{ - struct via_spec *spec = codec->spec; - - mutex_lock(&spec->config_mutex); - setup_playback_multi_pcm(spec); - snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag, - format, substream); - /* remember for dynamic DAC switch with indep-HP */ - spec->active_streams |= STREAM_MULTI_OUT; - spec->cur_dac_stream_tag = stream_tag; - spec->cur_dac_format = format; - mutex_unlock(&spec->config_mutex); - vt1708_update_hp_work(spec); - return 0; -} - -static int via_playback_hp_pcm_prepare(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - unsigned int stream_tag, - unsigned int format, - struct snd_pcm_substream *substream) -{ - struct via_spec *spec = codec->spec; - - mutex_lock(&spec->config_mutex); - if (spec->hp_independent_mode) - snd_hda_codec_setup_stream(codec, spec->hp_dac_nid, - stream_tag, 0, format); - spec->active_streams |= STREAM_INDEP_HP; - spec->cur_hp_stream_tag = stream_tag; - spec->cur_hp_format = format; - mutex_unlock(&spec->config_mutex); - vt1708_update_hp_work(spec); - return 0; -} - -static int via_playback_multi_pcm_cleanup(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct via_spec *spec = codec->spec; - - mutex_lock(&spec->config_mutex); - snd_hda_multi_out_analog_cleanup(codec, &spec->multiout); - spec->active_streams &= ~STREAM_MULTI_OUT; - mutex_unlock(&spec->config_mutex); - vt1708_update_hp_work(spec); - return 0; -} - -static int via_playback_hp_pcm_cleanup(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct via_spec *spec = codec->spec; - - mutex_lock(&spec->config_mutex); - if (spec->hp_independent_mode) - snd_hda_codec_setup_stream(codec, spec->hp_dac_nid, 0, 0, 0); - spec->active_streams &= ~STREAM_INDEP_HP; - mutex_unlock(&spec->config_mutex); - vt1708_update_hp_work(spec); - return 0; -} - -/* - * Digital out - */ -static int via_dig_playback_pcm_open(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct via_spec *spec = codec->spec; - return snd_hda_multi_out_dig_open(codec, &spec->multiout); -} - -static int via_dig_playback_pcm_close(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct via_spec *spec = codec->spec; - return snd_hda_multi_out_dig_close(codec, &spec->multiout); -} - -static int via_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - unsigned int stream_tag, - unsigned int format, - struct snd_pcm_substream *substream) -{ - struct via_spec *spec = codec->spec; - return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, - stream_tag, format, substream); -} - -static int via_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct via_spec *spec = codec->spec; - snd_hda_multi_out_dig_cleanup(codec, &spec->multiout); - return 0; -} - -/* - * Analog capture - */ -static int via_capture_pcm_prepare(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - unsigned int stream_tag, - unsigned int format, - struct snd_pcm_substream *substream) -{ - struct via_spec *spec = codec->spec; - - snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number], - stream_tag, 0, format); - return 0; -} - -static int via_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct via_spec *spec = codec->spec; - snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]); - return 0; -} - -/* analog capture with dynamic ADC switching */ -static int via_dyn_adc_capture_pcm_prepare(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - unsigned int stream_tag, - unsigned int format, - struct snd_pcm_substream *substream) -{ - struct via_spec *spec = codec->spec; - int adc_idx = spec->inputs[spec->cur_mux[0]].adc_idx; - - mutex_lock(&spec->config_mutex); - spec->cur_adc = spec->adc_nids[adc_idx]; - spec->cur_adc_stream_tag = stream_tag; - spec->cur_adc_format = format; - snd_hda_codec_setup_stream(codec, spec->cur_adc, stream_tag, 0, format); - mutex_unlock(&spec->config_mutex); - return 0; -} - -static int via_dyn_adc_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct via_spec *spec = codec->spec; - - mutex_lock(&spec->config_mutex); - snd_hda_codec_cleanup_stream(codec, spec->cur_adc); - spec->cur_adc = 0; - mutex_unlock(&spec->config_mutex); - return 0; -} - -/* re-setup the stream if running; called from input-src put */ -static bool via_dyn_adc_pcm_resetup(struct hda_codec *codec, int cur) -{ - struct via_spec *spec = codec->spec; - int adc_idx = spec->inputs[cur].adc_idx; - hda_nid_t adc = spec->adc_nids[adc_idx]; - bool ret = false; - - mutex_lock(&spec->config_mutex); - if (spec->cur_adc && spec->cur_adc != adc) { - /* stream is running, let's swap the current ADC */ - __snd_hda_codec_cleanup_stream(codec, spec->cur_adc, 1); - spec->cur_adc = adc; - snd_hda_codec_setup_stream(codec, adc, - spec->cur_adc_stream_tag, 0, - spec->cur_adc_format); - ret = true; - } - mutex_unlock(&spec->config_mutex); - return ret; -} - -static const struct hda_pcm_stream via_pcm_analog_playback = { - .substreams = 1, - .channels_min = 2, - .channels_max = 8, - /* NID is set in via_build_pcms */ - .ops = { - .open = via_playback_multi_pcm_open, - .close = via_playback_multi_pcm_close, - .prepare = via_playback_multi_pcm_prepare, - .cleanup = via_playback_multi_pcm_cleanup - }, -}; - -static const struct hda_pcm_stream via_pcm_hp_playback = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, - /* NID is set in via_build_pcms */ - .ops = { - .open = via_playback_hp_pcm_open, - .close = via_playback_hp_pcm_close, - .prepare = via_playback_hp_pcm_prepare, - .cleanup = via_playback_hp_pcm_cleanup - }, -}; - -static const struct hda_pcm_stream vt1708_pcm_analog_s16_playback = { - .substreams = 1, - .channels_min = 2, - .channels_max = 8, - /* NID is set in via_build_pcms */ - /* We got noisy outputs on the right channel on VT1708 when - * 24bit samples are used. Until any workaround is found, - * disable the 24bit format, so far. - */ - .formats = SNDRV_PCM_FMTBIT_S16_LE, - .ops = { - .open = via_playback_multi_pcm_open, - .close = via_playback_multi_pcm_close, - .prepare = via_playback_multi_pcm_prepare, - .cleanup = via_playback_multi_pcm_cleanup - }, -}; - -static const struct hda_pcm_stream via_pcm_analog_capture = { - .substreams = 1, /* will be changed in via_build_pcms() */ - .channels_min = 2, - .channels_max = 2, - /* NID is set in via_build_pcms */ - .ops = { - .prepare = via_capture_pcm_prepare, - .cleanup = via_capture_pcm_cleanup - }, -}; - -static const struct hda_pcm_stream via_pcm_dyn_adc_analog_capture = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, - /* NID is set in via_build_pcms */ - .ops = { - .prepare = via_dyn_adc_capture_pcm_prepare, - .cleanup = via_dyn_adc_capture_pcm_cleanup, - }, -}; - -static const struct hda_pcm_stream via_pcm_digital_playback = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, - /* NID is set in via_build_pcms */ - .ops = { - .open = via_dig_playback_pcm_open, - .close = via_dig_playback_pcm_close, - .prepare = via_dig_playback_pcm_prepare, - .cleanup = via_dig_playback_pcm_cleanup - }, -}; - -static const struct hda_pcm_stream via_pcm_digital_capture = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, -}; - -/* - * slave controls for virtual master - */ -static const char * const via_slave_pfxs[] = { - "Front", "Surround", "Center", "LFE", "Side", - "Headphone", "Speaker", "Bass Speaker", - NULL, -}; - -static int via_build_controls(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - struct snd_kcontrol *kctl; - int err, i; - - spec->no_pin_power_ctl = 1; - if (spec->set_widgets_power_state) - if (!via_clone_control(spec, &via_pin_power_ctl_enum)) - return -ENOMEM; - - for (i = 0; i < spec->num_mixers; i++) { - err = snd_hda_add_new_ctls(codec, spec->mixers[i]); - if (err < 0) - return err; - } - - if (spec->multiout.dig_out_nid) { - err = snd_hda_create_spdif_out_ctls(codec, - spec->multiout.dig_out_nid, - spec->multiout.dig_out_nid); - if (err < 0) - return err; - err = snd_hda_create_spdif_share_sw(codec, - &spec->multiout); - if (err < 0) - return err; - spec->multiout.share_spdif = 1; - } - if (spec->dig_in_nid) { - err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid); - if (err < 0) - return err; - } - - /* if we have no master control, let's create it */ - if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) { - unsigned int vmaster_tlv[4]; - snd_hda_set_vmaster_tlv(codec, spec->multiout.dac_nids[0], - HDA_OUTPUT, vmaster_tlv); - err = snd_hda_add_vmaster(codec, "Master Playback Volume", - vmaster_tlv, via_slave_pfxs, - "Playback Volume"); - if (err < 0) - return err; - } - if (!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) { - err = snd_hda_add_vmaster(codec, "Master Playback Switch", - NULL, via_slave_pfxs, - "Playback Switch"); - if (err < 0) - return err; - } - - /* assign Capture Source enums to NID */ - kctl = snd_hda_find_mixer_ctl(codec, "Input Source"); - for (i = 0; kctl && i < kctl->count; i++) { - if (!spec->mux_nids[i]) - continue; - err = snd_hda_add_nid(codec, kctl, i, spec->mux_nids[i]); - if (err < 0) - return err; - } - - via_free_kctls(codec); /* no longer needed */ - - err = snd_hda_jack_add_kctls(codec, &spec->autocfg); - if (err < 0) - return err; - - return 0; -} - -static int via_build_pcms(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - struct hda_pcm *info = spec->pcm_rec; - - codec->num_pcms = 0; - codec->pcm_info = info; - - if (spec->multiout.num_dacs || spec->num_adc_nids) { - snprintf(spec->stream_name_analog, - sizeof(spec->stream_name_analog), - "%s Analog", codec->chip_name); - info->name = spec->stream_name_analog; - - if (spec->multiout.num_dacs) { - if (!spec->stream_analog_playback) - spec->stream_analog_playback = - &via_pcm_analog_playback; - info->stream[SNDRV_PCM_STREAM_PLAYBACK] = - *spec->stream_analog_playback; - info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = - spec->multiout.dac_nids[0]; - info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = - spec->multiout.max_channels; - if (spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT - && spec->autocfg.line_outs == 2) - info->stream[SNDRV_PCM_STREAM_PLAYBACK].chmap = - snd_pcm_2_1_chmaps; - } - - if (!spec->stream_analog_capture) { - if (spec->dyn_adc_switch) - spec->stream_analog_capture = - &via_pcm_dyn_adc_analog_capture; - else - spec->stream_analog_capture = - &via_pcm_analog_capture; - } - if (spec->num_adc_nids) { - info->stream[SNDRV_PCM_STREAM_CAPTURE] = - *spec->stream_analog_capture; - info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = - spec->adc_nids[0]; - if (!spec->dyn_adc_switch) - info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = - spec->num_adc_nids; - } - codec->num_pcms++; - info++; - } - - if (spec->multiout.dig_out_nid || spec->dig_in_nid) { - snprintf(spec->stream_name_digital, - sizeof(spec->stream_name_digital), - "%s Digital", codec->chip_name); - info->name = spec->stream_name_digital; - info->pcm_type = HDA_PCM_TYPE_SPDIF; - if (spec->multiout.dig_out_nid) { - if (!spec->stream_digital_playback) - spec->stream_digital_playback = - &via_pcm_digital_playback; - info->stream[SNDRV_PCM_STREAM_PLAYBACK] = - *spec->stream_digital_playback; - info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = - spec->multiout.dig_out_nid; - } - if (spec->dig_in_nid) { - if (!spec->stream_digital_capture) - spec->stream_digital_capture = - &via_pcm_digital_capture; - info->stream[SNDRV_PCM_STREAM_CAPTURE] = - *spec->stream_digital_capture; - info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = - spec->dig_in_nid; - } - codec->num_pcms++; - info++; - } - - if (spec->hp_dac_nid) { - snprintf(spec->stream_name_hp, sizeof(spec->stream_name_hp), - "%s HP", codec->chip_name); - info->name = spec->stream_name_hp; - info->stream[SNDRV_PCM_STREAM_PLAYBACK] = via_pcm_hp_playback; - info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = - spec->hp_dac_nid; - codec->num_pcms++; - info++; - } - return 0; -} - -static void via_free(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - - if (!spec) - return; - - via_free_kctls(codec); - vt1708_stop_hp_work(spec); - kfree(spec->bind_cap_vol); - kfree(spec->bind_cap_sw); - kfree(spec); -} - -/* mute/unmute outputs */ -static void toggle_output_mutes(struct hda_codec *codec, int num_pins, - hda_nid_t *pins, bool mute) -{ - int i; - for (i = 0; i < num_pins; i++) { - unsigned int parm = snd_hda_codec_read(codec, pins[i], 0, - AC_VERB_GET_PIN_WIDGET_CONTROL, 0); - if (parm & AC_PINCTL_IN_EN) - continue; - if (mute) - parm &= ~AC_PINCTL_OUT_EN; - else - parm |= AC_PINCTL_OUT_EN; - snd_hda_set_pin_ctl(codec, pins[i], parm); - } -} - -/* mute internal speaker if line-out is plugged */ -static void via_line_automute(struct hda_codec *codec, int present) -{ - struct via_spec *spec = codec->spec; - - if (!spec->autocfg.speaker_outs) - return; - if (!present) - present = snd_hda_jack_detect(codec, - spec->autocfg.line_out_pins[0]); - toggle_output_mutes(codec, spec->autocfg.speaker_outs, - spec->autocfg.speaker_pins, - present); -} - -/* mute internal speaker if HP is plugged */ -static void via_hp_automute(struct hda_codec *codec) -{ - int present = 0; - int nums; - struct via_spec *spec = codec->spec; - - if (!spec->hp_independent_mode && spec->autocfg.hp_pins[0] && - (spec->codec_type != VT1708 || spec->vt1708_jack_detect) && - is_jack_detectable(codec, spec->autocfg.hp_pins[0])) - present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]); - - if (spec->smart51_enabled) - nums = spec->autocfg.line_outs + spec->smart51_nums; - else - nums = spec->autocfg.line_outs; - toggle_output_mutes(codec, nums, spec->autocfg.line_out_pins, present); - - via_line_automute(codec, present); -} - -#ifdef CONFIG_PM -static int via_suspend(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - vt1708_stop_hp_work(spec); - - if (spec->codec_type == VT1802) { - /* Fix pop noise on headphones */ - int i; - for (i = 0; i < spec->autocfg.hp_outs; i++) - snd_hda_set_pin_ctl(codec, spec->autocfg.hp_pins[i], 0); - } - - return 0; -} -#endif - -#ifdef CONFIG_PM -static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid) -{ - struct via_spec *spec = codec->spec; - return snd_hda_check_amp_list_power(codec, &spec->loopback, nid); -} -#endif - -/* - */ - -static int via_init(struct hda_codec *codec); - -static const struct hda_codec_ops via_patch_ops = { - .build_controls = via_build_controls, - .build_pcms = via_build_pcms, - .init = via_init, - .free = via_free, - .unsol_event = snd_hda_jack_unsol_event, -#ifdef CONFIG_PM - .suspend = via_suspend, - .check_power_status = via_check_power_status, -#endif -}; - -static bool is_empty_dac(struct hda_codec *codec, hda_nid_t dac) -{ - struct via_spec *spec = codec->spec; - int i; - - for (i = 0; i < spec->multiout.num_dacs; i++) { - if (spec->multiout.dac_nids[i] == dac) - return false; - } - if (spec->hp_dac_nid == dac) - return false; - return true; -} - -static bool __parse_output_path(struct hda_codec *codec, hda_nid_t nid, - hda_nid_t target_dac, int with_aa_mix, - struct nid_path *path, int depth) -{ - struct via_spec *spec = codec->spec; - hda_nid_t conn[8]; - int i, nums; - - if (nid == spec->aa_mix_nid) { - if (!with_aa_mix) - return false; - with_aa_mix = 2; /* mark aa-mix is included */ - } - - nums = snd_hda_get_connections(codec, nid, conn, ARRAY_SIZE(conn)); - for (i = 0; i < nums; i++) { - if (get_wcaps_type(get_wcaps(codec, conn[i])) != AC_WID_AUD_OUT) - continue; - if (conn[i] == target_dac || is_empty_dac(codec, conn[i])) { - /* aa-mix is requested but not included? */ - if (!(spec->aa_mix_nid && with_aa_mix == 1)) - goto found; - } - } - if (depth >= MAX_NID_PATH_DEPTH) - return false; - for (i = 0; i < nums; i++) { - unsigned int type; - type = get_wcaps_type(get_wcaps(codec, conn[i])); - if (type == AC_WID_AUD_OUT) - continue; - if (__parse_output_path(codec, conn[i], target_dac, - with_aa_mix, path, depth + 1)) - goto found; - } - return false; - - found: - path->path[path->depth] = conn[i]; - path->idx[path->depth] = i; - if (nums > 1 && get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_AUD_MIX) - path->multi[path->depth] = 1; - path->depth++; - return true; -} - -static bool parse_output_path(struct hda_codec *codec, hda_nid_t nid, - hda_nid_t target_dac, int with_aa_mix, - struct nid_path *path) -{ - if (__parse_output_path(codec, nid, target_dac, with_aa_mix, path, 1)) { - path->path[path->depth] = nid; - path->depth++; - snd_printdd("output-path: depth=%d, %02x/%02x/%02x/%02x/%02x\n", - path->depth, path->path[0], path->path[1], - path->path[2], path->path[3], path->path[4]); - return true; - } - return false; -} - -static int via_auto_fill_dac_nids(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - const struct auto_pin_cfg *cfg = &spec->autocfg; - int i; - hda_nid_t nid; - - spec->multiout.num_dacs = 0; - spec->multiout.dac_nids = spec->private_dac_nids; - for (i = 0; i < cfg->line_outs; i++) { - hda_nid_t dac = 0; - nid = cfg->line_out_pins[i]; - if (!nid) - continue; - if (parse_output_path(codec, nid, 0, 0, &spec->out_path[i])) - dac = spec->out_path[i].path[0]; - if (!i && parse_output_path(codec, nid, dac, 1, - &spec->out_mix_path)) - dac = spec->out_mix_path.path[0]; - if (dac) - spec->private_dac_nids[spec->multiout.num_dacs++] = dac; - } - if (!spec->out_path[0].depth && spec->out_mix_path.depth) { - spec->out_path[0] = spec->out_mix_path; - spec->out_mix_path.depth = 0; - } - return 0; -} - -static int create_ch_ctls(struct hda_codec *codec, const char *pfx, - int chs, bool check_dac, struct nid_path *path) -{ - struct via_spec *spec = codec->spec; - char name[32]; - hda_nid_t dac, pin, sel, nid; - int err; - - dac = check_dac ? path->path[0] : 0; - pin = path->path[path->depth - 1]; - sel = path->depth > 1 ? path->path[1] : 0; - - if (dac && check_amp_caps(codec, dac, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS)) - nid = dac; - else if (check_amp_caps(codec, pin, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS)) - nid = pin; - else if (check_amp_caps(codec, sel, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS)) - nid = sel; - else - nid = 0; - if (nid) { - sprintf(name, "%s Playback Volume", pfx); - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, - HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT)); - if (err < 0) - return err; - path->vol_ctl = nid; - } - - if (dac && check_amp_caps(codec, dac, HDA_OUTPUT, AC_AMPCAP_MUTE)) - nid = dac; - else if (check_amp_caps(codec, pin, HDA_OUTPUT, AC_AMPCAP_MUTE)) - nid = pin; - else if (check_amp_caps(codec, sel, HDA_OUTPUT, AC_AMPCAP_MUTE)) - nid = sel; - else - nid = 0; - if (nid) { - sprintf(name, "%s Playback Switch", pfx); - err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, - HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT)); - if (err < 0) - return err; - path->mute_ctl = nid; - } - return 0; -} - -static void mangle_smart51(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - struct auto_pin_cfg_item *ins = cfg->inputs; - int i, j, nums, attr; - int pins[AUTO_CFG_MAX_INS]; - - for (attr = INPUT_PIN_ATTR_LAST; attr >= INPUT_PIN_ATTR_NORMAL; attr--) { - nums = 0; - for (i = 0; i < cfg->num_inputs; i++) { - unsigned int def; - if (ins[i].type > AUTO_PIN_LINE_IN) - continue; - def = snd_hda_codec_get_pincfg(codec, ins[i].pin); - if (snd_hda_get_input_pin_attr(def) != attr) - continue; - for (j = 0; j < nums; j++) - if (ins[pins[j]].type < ins[i].type) { - memmove(pins + j + 1, pins + j, - (nums - j) * sizeof(int)); - break; - } - pins[j] = i; - nums++; - } - if (cfg->line_outs + nums < 3) - continue; - for (i = 0; i < nums; i++) { - hda_nid_t pin = ins[pins[i]].pin; - spec->smart51_pins[spec->smart51_nums++] = pin; - cfg->line_out_pins[cfg->line_outs++] = pin; - if (cfg->line_outs == 3) - break; - } - return; - } -} - -static void copy_path_mixer_ctls(struct nid_path *dst, struct nid_path *src) -{ - dst->vol_ctl = src->vol_ctl; - dst->mute_ctl = src->mute_ctl; -} - -/* add playback controls from the parsed DAC table */ -static int via_auto_create_multi_out_ctls(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - struct nid_path *path; - static const char * const chname[4] = { - "Front", "Surround", NULL /* "CLFE" */, "Side" - }; - int i, idx, err; - int old_line_outs; - - /* check smart51 */ - old_line_outs = cfg->line_outs; - if (cfg->line_outs == 1) - mangle_smart51(codec); - - err = via_auto_fill_dac_nids(codec); - if (err < 0) - return err; - - if (spec->multiout.num_dacs < 3) { - spec->smart51_nums = 0; - cfg->line_outs = old_line_outs; - } - for (i = 0; i < cfg->line_outs; i++) { - hda_nid_t pin, dac; - pin = cfg->line_out_pins[i]; - dac = spec->multiout.dac_nids[i]; - if (!pin || !dac) - continue; - path = spec->out_path + i; - if (i == HDA_CLFE) { - err = create_ch_ctls(codec, "Center", 1, true, path); - if (err < 0) - return err; - err = create_ch_ctls(codec, "LFE", 2, true, path); - if (err < 0) - return err; - } else { - const char *pfx = chname[i]; - if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT && - cfg->line_outs <= 2) - pfx = i ? "Bass Speaker" : "Speaker"; - err = create_ch_ctls(codec, pfx, 3, true, path); - if (err < 0) - return err; - } - if (path != spec->out_path + i) - copy_path_mixer_ctls(&spec->out_path[i], path); - if (path == spec->out_path && spec->out_mix_path.depth) - copy_path_mixer_ctls(&spec->out_mix_path, path); - } - - idx = get_connection_index(codec, spec->aa_mix_nid, - spec->multiout.dac_nids[0]); - if (idx >= 0) { - /* add control to mixer */ - const char *name; - name = spec->out_mix_path.depth ? - "PCM Loopback Playback Volume" : "PCM Playback Volume"; - err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, - HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3, - idx, HDA_INPUT)); - if (err < 0) - return err; - name = spec->out_mix_path.depth ? - "PCM Loopback Playback Switch" : "PCM Playback Switch"; - err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, - HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3, - idx, HDA_INPUT)); - if (err < 0) - return err; - } - - cfg->line_outs = old_line_outs; - - return 0; -} - -static int via_auto_create_hp_ctls(struct hda_codec *codec, hda_nid_t pin) -{ - struct via_spec *spec = codec->spec; - struct nid_path *path; - bool check_dac; - int i, err; - - if (!pin) - return 0; - - if (!parse_output_path(codec, pin, 0, 0, &spec->hp_indep_path)) { - for (i = HDA_SIDE; i >= HDA_CLFE; i--) { - if (i < spec->multiout.num_dacs && - parse_output_path(codec, pin, - spec->multiout.dac_nids[i], 0, - &spec->hp_indep_path)) { - spec->hp_indep_shared = i; - break; - } - } - } - if (spec->hp_indep_path.depth) { - spec->hp_dac_nid = spec->hp_indep_path.path[0]; - if (!spec->hp_indep_shared) - spec->hp_path = spec->hp_indep_path; - } - /* optionally check front-path w/o AA-mix */ - if (!spec->hp_path.depth) - parse_output_path(codec, pin, - spec->multiout.dac_nids[HDA_FRONT], 0, - &spec->hp_path); - - if (!parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT], - 1, &spec->hp_mix_path) && !spec->hp_path.depth) - return 0; - - if (spec->hp_path.depth) { - path = &spec->hp_path; - check_dac = true; - } else { - path = &spec->hp_mix_path; - check_dac = false; - } - err = create_ch_ctls(codec, "Headphone", 3, check_dac, path); - if (err < 0) - return err; - if (check_dac) - copy_path_mixer_ctls(&spec->hp_mix_path, path); - else - copy_path_mixer_ctls(&spec->hp_path, path); - if (spec->hp_indep_path.depth) - copy_path_mixer_ctls(&spec->hp_indep_path, path); - return 0; -} - -static int via_auto_create_speaker_ctls(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - struct nid_path *path; - bool check_dac; - hda_nid_t pin, dac = 0; - int err; - - pin = spec->autocfg.speaker_pins[0]; - if (!spec->autocfg.speaker_outs || !pin) - return 0; - - if (parse_output_path(codec, pin, 0, 0, &spec->speaker_path)) - dac = spec->speaker_path.path[0]; - if (!dac) - parse_output_path(codec, pin, - spec->multiout.dac_nids[HDA_FRONT], 0, - &spec->speaker_path); - if (!parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT], - 1, &spec->speaker_mix_path) && !dac) - return 0; - - /* no AA-path for front? */ - if (!spec->out_mix_path.depth && spec->speaker_mix_path.depth) - dac = 0; - - spec->speaker_dac_nid = dac; - spec->multiout.extra_out_nid[0] = dac; - if (dac) { - path = &spec->speaker_path; - check_dac = true; - } else { - path = &spec->speaker_mix_path; - check_dac = false; - } - err = create_ch_ctls(codec, "Speaker", 3, check_dac, path); - if (err < 0) - return err; - if (check_dac) - copy_path_mixer_ctls(&spec->speaker_mix_path, path); - else - copy_path_mixer_ctls(&spec->speaker_path, path); - return 0; -} - -#define via_aamix_ctl_info via_pin_power_ctl_info - -static int via_aamix_ctl_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct via_spec *spec = codec->spec; - ucontrol->value.enumerated.item[0] = spec->aamix_mode; - return 0; -} - -static void update_aamix_paths(struct hda_codec *codec, int do_mix, - struct nid_path *nomix, struct nid_path *mix) -{ - if (do_mix) { - activate_output_path(codec, nomix, false, false); - activate_output_path(codec, mix, true, false); - } else { - activate_output_path(codec, mix, false, false); - activate_output_path(codec, nomix, true, false); - } -} - -static int via_aamix_ctl_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct via_spec *spec = codec->spec; - unsigned int val = ucontrol->value.enumerated.item[0]; - - if (val == spec->aamix_mode) - return 0; - spec->aamix_mode = val; - /* update front path */ - update_aamix_paths(codec, val, &spec->out_path[0], &spec->out_mix_path); - /* update HP path */ - if (!spec->hp_independent_mode) { - update_aamix_paths(codec, val, &spec->hp_path, - &spec->hp_mix_path); - } - /* update speaker path */ - update_aamix_paths(codec, val, &spec->speaker_path, - &spec->speaker_mix_path); - return 1; -} - -static const struct snd_kcontrol_new via_aamix_ctl_enum = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Loopback Mixing", - .info = via_aamix_ctl_info, - .get = via_aamix_ctl_get, - .put = via_aamix_ctl_put, -}; - -static int via_auto_create_loopback_switch(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - - if (!spec->aa_mix_nid) - return 0; /* no loopback switching available */ - if (!(spec->out_mix_path.depth || spec->hp_mix_path.depth || - spec->speaker_path.depth)) - return 0; /* no loopback switching available */ - if (!via_clone_control(spec, &via_aamix_ctl_enum)) - return -ENOMEM; - return 0; -} - -/* look for ADCs */ -static int via_fill_adcs(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - hda_nid_t nid = codec->start_nid; - int i; - - for (i = 0; i < codec->num_nodes; i++, nid++) { - unsigned int wcaps = get_wcaps(codec, nid); - if (get_wcaps_type(wcaps) != AC_WID_AUD_IN) - continue; - if (wcaps & AC_WCAP_DIGITAL) - continue; - if (!(wcaps & AC_WCAP_CONN_LIST)) - continue; - if (spec->num_adc_nids >= ARRAY_SIZE(spec->adc_nids)) - return -ENOMEM; - spec->adc_nids[spec->num_adc_nids++] = nid; - } - return 0; -} - -/* input-src control */ -static int via_mux_enum_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct via_spec *spec = codec->spec; - - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = spec->num_inputs; - if (uinfo->value.enumerated.item >= spec->num_inputs) - uinfo->value.enumerated.item = spec->num_inputs - 1; - strcpy(uinfo->value.enumerated.name, - spec->inputs[uinfo->value.enumerated.item].label); - return 0; -} - -static int via_mux_enum_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct via_spec *spec = codec->spec; - unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); - - ucontrol->value.enumerated.item[0] = spec->cur_mux[idx]; - return 0; -} - -static int via_mux_enum_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct via_spec *spec = codec->spec; - unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); - hda_nid_t mux; - int cur; - - cur = ucontrol->value.enumerated.item[0]; - if (cur < 0 || cur >= spec->num_inputs) - return -EINVAL; - if (spec->cur_mux[idx] == cur) - return 0; - spec->cur_mux[idx] = cur; - if (spec->dyn_adc_switch) { - int adc_idx = spec->inputs[cur].adc_idx; - mux = spec->mux_nids[adc_idx]; - via_dyn_adc_pcm_resetup(codec, cur); - } else { - mux = spec->mux_nids[idx]; - if (snd_BUG_ON(!mux)) - return -EINVAL; - } - - if (mux) { - /* switch to D0 beofre change index */ - update_power_state(codec, mux, AC_PWRST_D0); - snd_hda_codec_write(codec, mux, 0, - AC_VERB_SET_CONNECT_SEL, - spec->inputs[cur].mux_idx); - } - - /* update jack power state */ - set_widgets_power_state(codec); - return 0; -} - -static const struct snd_kcontrol_new via_input_src_ctl = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - /* The multiple "Capture Source" controls confuse alsamixer - * So call somewhat different.. - */ - /* .name = "Capture Source", */ - .name = "Input Source", - .info = via_mux_enum_info, - .get = via_mux_enum_get, - .put = via_mux_enum_put, -}; - -static int create_input_src_ctls(struct hda_codec *codec, int count) -{ - struct via_spec *spec = codec->spec; - struct snd_kcontrol_new *knew; - - if (spec->num_inputs <= 1 || !count) - return 0; /* no need for single src */ - - knew = via_clone_control(spec, &via_input_src_ctl); - if (!knew) - return -ENOMEM; - knew->count = count; - return 0; -} - -/* add the powersave loopback-list entry */ -static void add_loopback_list(struct via_spec *spec, hda_nid_t mix, int idx) -{ - struct hda_amp_list *list; - - if (spec->num_loopbacks >= ARRAY_SIZE(spec->loopback_list) - 1) - return; - list = spec->loopback_list + spec->num_loopbacks; - list->nid = mix; - list->dir = HDA_INPUT; - list->idx = idx; - spec->num_loopbacks++; - spec->loopback.amplist = spec->loopback_list; -} - -static bool is_reachable_nid(struct hda_codec *codec, hda_nid_t src, - hda_nid_t dst) -{ - return snd_hda_get_conn_index(codec, src, dst, 1) >= 0; -} - -/* add the input-route to the given pin */ -static bool add_input_route(struct hda_codec *codec, hda_nid_t pin) -{ - struct via_spec *spec = codec->spec; - int c, idx; - - spec->inputs[spec->num_inputs].adc_idx = -1; - spec->inputs[spec->num_inputs].pin = pin; - for (c = 0; c < spec->num_adc_nids; c++) { - if (spec->mux_nids[c]) { - idx = get_connection_index(codec, spec->mux_nids[c], - pin); - if (idx < 0) - continue; - spec->inputs[spec->num_inputs].mux_idx = idx; - } else { - if (!is_reachable_nid(codec, spec->adc_nids[c], pin)) - continue; - } - spec->inputs[spec->num_inputs].adc_idx = c; - /* Can primary ADC satisfy all inputs? */ - if (!spec->dyn_adc_switch && - spec->num_inputs > 0 && spec->inputs[0].adc_idx != c) { - snd_printd(KERN_INFO - "via: dynamic ADC switching enabled\n"); - spec->dyn_adc_switch = 1; - } - return true; - } - return false; -} - -static int get_mux_nids(struct hda_codec *codec); +static void analog_low_current_mode(struct hda_codec *codec) +{ + return __analog_low_current_mode(codec, false); +} -/* parse input-routes; fill ADCs, MUXs and input-src entries */ -static int parse_analog_inputs(struct hda_codec *codec) +static int via_build_controls(struct hda_codec *codec) { struct via_spec *spec = codec->spec; - const struct auto_pin_cfg *cfg = &spec->autocfg; - int i, err; + int err, i; - err = via_fill_adcs(codec); - if (err < 0) - return err; - err = get_mux_nids(codec); + err = snd_hda_gen_build_controls(codec); if (err < 0) return err; - /* fill all input-routes */ - for (i = 0; i < cfg->num_inputs; i++) { - if (add_input_route(codec, cfg->inputs[i].pin)) - spec->inputs[spec->num_inputs++].label = - hda_get_autocfg_input_label(codec, cfg, i); - } + if (spec->set_widgets_power_state) + spec->mixers[spec->num_mixers++] = via_pin_power_ctl_enum; - /* check for internal loopback recording */ - if (spec->aa_mix_nid && - add_input_route(codec, spec->aa_mix_nid)) - spec->inputs[spec->num_inputs++].label = "Stereo Mixer"; + for (i = 0; i < spec->num_mixers; i++) { + err = snd_hda_add_new_ctls(codec, spec->mixers[i]); + if (err < 0) + return err; + } return 0; } -/* create analog-loopback volume/switch controls */ -static int create_loopback_ctls(struct hda_codec *codec) +static void via_playback_pcm_hook(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream, + int action) { - struct via_spec *spec = codec->spec; - const struct auto_pin_cfg *cfg = &spec->autocfg; - const char *prev_label = NULL; - int type_idx = 0; - int i, j, err, idx; - - if (!spec->aa_mix_nid) - return 0; - - for (i = 0; i < cfg->num_inputs; i++) { - hda_nid_t pin = cfg->inputs[i].pin; - const char *label = hda_get_autocfg_input_label(codec, cfg, i); - - if (prev_label && !strcmp(label, prev_label)) - type_idx++; - else - type_idx = 0; - prev_label = label; - idx = get_connection_index(codec, spec->aa_mix_nid, pin); - if (idx >= 0) { - err = via_new_analog_input(spec, label, type_idx, - idx, spec->aa_mix_nid); - if (err < 0) - return err; - add_loopback_list(spec, spec->aa_mix_nid, idx); - } - - /* remember the label for smart51 control */ - for (j = 0; j < spec->smart51_nums; j++) { - if (spec->smart51_pins[j] == pin) { - spec->smart51_idxs[j] = idx; - spec->smart51_labels[j] = label; - break; - } - } - } - return 0; + analog_low_current_mode(codec); + vt1708_update_hp_work(codec); } -/* create mic-boost controls (if present) */ -static int create_mic_boost_ctls(struct hda_codec *codec) +static void via_free(struct hda_codec *codec) { struct via_spec *spec = codec->spec; - const struct auto_pin_cfg *cfg = &spec->autocfg; - const char *prev_label = NULL; - int type_idx = 0; - int i, err; - for (i = 0; i < cfg->num_inputs; i++) { - hda_nid_t pin = cfg->inputs[i].pin; - unsigned int caps; - const char *label; - char name[32]; + if (!spec) + return; - if (cfg->inputs[i].type != AUTO_PIN_MIC) - continue; - caps = query_amp_caps(codec, pin, HDA_INPUT); - if (caps == -1 || !(caps & AC_AMPCAP_NUM_STEPS)) - continue; - label = hda_get_autocfg_input_label(codec, cfg, i); - if (prev_label && !strcmp(label, prev_label)) - type_idx++; - else - type_idx = 0; - prev_label = label; - snprintf(name, sizeof(name), "%s Boost Volume", label); - err = __via_add_control(spec, VIA_CTL_WIDGET_VOL, name, type_idx, - HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_INPUT)); - if (err < 0) - return err; - } - return 0; + vt1708_stop_hp_work(codec); + snd_hda_gen_spec_free(&spec->gen); + kfree(spec); } -/* create capture and input-src controls for multiple streams */ -static int create_multi_adc_ctls(struct hda_codec *codec) +#ifdef CONFIG_PM +static int via_suspend(struct hda_codec *codec) { struct via_spec *spec = codec->spec; - int i, err; + vt1708_stop_hp_work(codec); - /* create capture mixer elements */ - for (i = 0; i < spec->num_adc_nids; i++) { - hda_nid_t adc = spec->adc_nids[i]; - err = __via_add_control(spec, VIA_CTL_WIDGET_VOL, - "Capture Volume", i, - HDA_COMPOSE_AMP_VAL(adc, 3, 0, - HDA_INPUT)); - if (err < 0) - return err; - err = __via_add_control(spec, VIA_CTL_WIDGET_MUTE, - "Capture Switch", i, - HDA_COMPOSE_AMP_VAL(adc, 3, 0, - HDA_INPUT)); - if (err < 0) - return err; + if (spec->codec_type == VT1802) { + /* Fix pop noise on headphones */ + int i; + for (i = 0; i < spec->gen.autocfg.hp_outs; i++) + snd_hda_set_pin_ctl(codec, spec->gen.autocfg.hp_pins[i], 0); } - /* input-source control */ - for (i = 0; i < spec->num_adc_nids; i++) - if (!spec->mux_nids[i]) - break; - err = create_input_src_ctls(codec, i); - if (err < 0) - return err; - return 0; -} - -/* bind capture volume/switch */ -static struct snd_kcontrol_new via_bind_cap_vol_ctl = - HDA_BIND_VOL("Capture Volume", 0); -static struct snd_kcontrol_new via_bind_cap_sw_ctl = - HDA_BIND_SW("Capture Switch", 0); - -static int init_bind_ctl(struct via_spec *spec, struct hda_bind_ctls **ctl_ret, - struct hda_ctl_ops *ops) -{ - struct hda_bind_ctls *ctl; - int i; - - ctl = kzalloc(sizeof(*ctl) + sizeof(long) * 4, GFP_KERNEL); - if (!ctl) - return -ENOMEM; - ctl->ops = ops; - for (i = 0; i < spec->num_adc_nids; i++) - ctl->values[i] = - HDA_COMPOSE_AMP_VAL(spec->adc_nids[i], 3, 0, HDA_INPUT); - *ctl_ret = ctl; return 0; } +#endif -/* create capture and input-src controls for dynamic ADC-switch case */ -static int create_dyn_adc_ctls(struct hda_codec *codec) +#ifdef CONFIG_PM +static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid) { struct via_spec *spec = codec->spec; - struct snd_kcontrol_new *knew; - int err; - - /* set up the bind capture ctls */ - err = init_bind_ctl(spec, &spec->bind_cap_vol, &snd_hda_bind_vol); - if (err < 0) - return err; - err = init_bind_ctl(spec, &spec->bind_cap_sw, &snd_hda_bind_sw); - if (err < 0) - return err; - - /* create capture mixer elements */ - knew = via_clone_control(spec, &via_bind_cap_vol_ctl); - if (!knew) - return -ENOMEM; - knew->private_value = (long)spec->bind_cap_vol; + set_widgets_power_state(codec); + analog_low_current_mode(codec); + vt1708_update_hp_work(codec); + return snd_hda_check_amp_list_power(codec, &spec->gen.loopback, nid); +} +#endif - knew = via_clone_control(spec, &via_bind_cap_sw_ctl); - if (!knew) - return -ENOMEM; - knew->private_value = (long)spec->bind_cap_sw; +/* + */ - /* input-source control */ - err = create_input_src_ctls(codec, 1); - if (err < 0) - return err; - return 0; -} +static int via_init(struct hda_codec *codec); -/* parse and create capture-related stuff */ -static int via_auto_create_analog_input_ctls(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - int err; +static const struct hda_codec_ops via_patch_ops = { + .build_controls = via_build_controls, + .build_pcms = snd_hda_gen_build_pcms, + .init = via_init, + .free = via_free, + .unsol_event = snd_hda_jack_unsol_event, +#ifdef CONFIG_PM + .suspend = via_suspend, + .check_power_status = via_check_power_status, +#endif +}; - err = parse_analog_inputs(codec); - if (err < 0) - return err; - if (spec->dyn_adc_switch) - err = create_dyn_adc_ctls(codec); - else - err = create_multi_adc_ctls(codec); - if (err < 0) - return err; - err = create_loopback_ctls(codec); - if (err < 0) - return err; - err = create_mic_boost_ctls(codec); - if (err < 0) - return err; - return 0; -} +static const struct hda_verb vt1708_init_verbs[] = { + /* power down jack detect function */ + {0x1, 0xf81, 0x1}, + { } +}; static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid) { unsigned int def_conf; @@ -2629,102 +560,32 @@ static int vt1708_jack_detect_put(struct snd_kcontrol *kcontrol, if (spec->vt1708_jack_detect == val) return 0; spec->vt1708_jack_detect = val; - if (spec->vt1708_jack_detect && - snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") != 1) { - mute_aa_path(codec, 1); - notify_aa_path_ctls(codec); - } - via_hp_automute(codec); - vt1708_update_hp_work(spec); + vt1708_update_hp_work(codec); return 1; } -static const struct snd_kcontrol_new vt1708_jack_detect_ctl = { +static const struct snd_kcontrol_new vt1708_jack_detect_ctl[] = { + { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Jack Detect", .count = 1, .info = snd_ctl_boolean_mono_info, .get = vt1708_jack_detect_get, .put = vt1708_jack_detect_put, + }, + {} /* terminator */ }; -static void fill_dig_outs(struct hda_codec *codec); -static void fill_dig_in(struct hda_codec *codec); - -static int via_parse_auto_config(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - int err; - - err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); - if (err < 0) - return err; - if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) - return -EINVAL; - - err = via_auto_create_multi_out_ctls(codec); - if (err < 0) - return err; - err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]); - if (err < 0) - return err; - err = via_auto_create_speaker_ctls(codec); - if (err < 0) - return err; - err = via_auto_create_loopback_switch(codec); - if (err < 0) - return err; - err = via_auto_create_analog_input_ctls(codec); - if (err < 0) - return err; - - spec->multiout.max_channels = spec->multiout.num_dacs * 2; - - fill_dig_outs(codec); - fill_dig_in(codec); - - if (spec->kctls.list) - spec->mixers[spec->num_mixers++] = spec->kctls.list; - - - if (spec->hp_dac_nid && spec->hp_mix_path.depth) { - err = via_hp_build(codec); - if (err < 0) - return err; - } - - err = via_smart51_build(codec); - if (err < 0) - return err; - - /* assign slave outs */ - if (spec->slave_dig_outs[0]) - codec->slave_dig_outs = spec->slave_dig_outs; - - return 1; -} - -static void via_auto_init_dig_outs(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - if (spec->multiout.dig_out_nid) - init_output_pin(codec, spec->autocfg.dig_out_pins[0], PIN_OUT); - if (spec->slave_dig_outs[0]) - init_output_pin(codec, spec->autocfg.dig_out_pins[1], PIN_OUT); -} - -static void via_auto_init_dig_in(struct hda_codec *codec) +static void via_hp_automute(struct hda_codec *codec, struct hda_jack_tbl *tbl) { - struct via_spec *spec = codec->spec; - if (!spec->dig_in_nid) - return; - snd_hda_set_pin_ctl(codec, spec->autocfg.dig_in_pin, PIN_IN); + set_widgets_power_state(codec); + snd_hda_gen_hp_automute(codec, tbl); } -static void via_jack_output_event(struct hda_codec *codec, struct hda_jack_tbl *tbl) +static void via_line_automute(struct hda_codec *codec, struct hda_jack_tbl *tbl) { set_widgets_power_state(codec); - via_hp_automute(codec); + snd_hda_gen_line_automute(codec, tbl); } static void via_jack_powerstate_event(struct hda_codec *codec, struct hda_jack_tbl *tbl) @@ -2732,41 +593,55 @@ static void via_jack_powerstate_event(struct hda_codec *codec, struct hda_jack_t set_widgets_power_state(codec); } -/* initialize the unsolicited events */ -static void via_auto_init_unsol_event(struct hda_codec *codec) +#define VIA_JACK_EVENT (HDA_GEN_LAST_EVENT + 1) + +static void via_set_jack_unsol_events(struct hda_codec *codec) { struct via_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - unsigned int ev; + struct auto_pin_cfg *cfg = &spec->gen.autocfg; + hda_nid_t pin; int i; - hda_jack_callback cb; - - if (cfg->hp_pins[0] && is_jack_detectable(codec, cfg->hp_pins[0])) - snd_hda_jack_detect_enable_callback(codec, cfg->hp_pins[0], - VIA_HP_EVENT | VIA_JACK_EVENT, - via_jack_output_event); + spec->gen.hp_automute_hook = via_hp_automute; if (cfg->speaker_pins[0]) - ev = VIA_LINE_EVENT; - else - ev = 0; - cb = ev ? via_jack_output_event : via_jack_powerstate_event; + spec->gen.line_automute_hook = via_line_automute; for (i = 0; i < cfg->line_outs; i++) { - if (cfg->line_out_pins[i] && - is_jack_detectable(codec, cfg->line_out_pins[i])) - snd_hda_jack_detect_enable_callback(codec, cfg->line_out_pins[i], - ev | VIA_JACK_EVENT, cb); + pin = cfg->line_out_pins[i]; + if (pin && !snd_hda_jack_tbl_get(codec, pin) && + is_jack_detectable(codec, pin)) + snd_hda_jack_detect_enable_callback(codec, pin, + VIA_JACK_EVENT, + via_jack_powerstate_event); } for (i = 0; i < cfg->num_inputs; i++) { - if (is_jack_detectable(codec, cfg->inputs[i].pin)) - snd_hda_jack_detect_enable_callback(codec, cfg->inputs[i].pin, + pin = cfg->line_out_pins[i]; + if (pin && !snd_hda_jack_tbl_get(codec, pin) && + is_jack_detectable(codec, pin)) + snd_hda_jack_detect_enable_callback(codec, pin, VIA_JACK_EVENT, via_jack_powerstate_event); } } +static int via_parse_auto_config(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + int err; + + err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL, 0); + if (err < 0) + return err; + + err = snd_hda_gen_parse_auto_config(codec, &spec->gen.autocfg); + if (err < 0) + return err; + + via_set_jack_unsol_events(codec); + return 0; +} + static int via_init(struct hda_codec *codec) { struct via_spec *spec = codec->spec; @@ -2779,63 +654,35 @@ static int via_init(struct hda_codec *codec) set_widgets_power_state(codec); __analog_low_current_mode(codec, true); - via_auto_init_multi_out(codec); - via_auto_init_hp_out(codec); - via_auto_init_speaker_out(codec); - via_auto_init_analog_input(codec); - via_auto_init_dig_outs(codec); - via_auto_init_dig_in(codec); - - via_auto_init_unsol_event(codec); + snd_hda_gen_init(codec); - via_hp_automute(codec); - vt1708_update_hp_work(spec); + vt1708_update_hp_work(codec); return 0; } -static void vt1708_update_hp_jack_state(struct work_struct *work) -{ - struct via_spec *spec = container_of(work, struct via_spec, - vt1708_hp_work.work); - if (spec->codec_type != VT1708) - return; - snd_hda_jack_set_dirty_all(spec->codec); - /* if jack state toggled */ - if (spec->vt1708_hp_present - != snd_hda_jack_detect(spec->codec, spec->autocfg.hp_pins[0])) { - spec->vt1708_hp_present ^= 1; - via_hp_automute(spec->codec); - } - if (spec->vt1708_jack_detect) - schedule_delayed_work(&spec->vt1708_hp_work, - msecs_to_jiffies(100)); -} - -static int get_mux_nids(struct hda_codec *codec) +static int vt1708_build_pcms(struct hda_codec *codec) { struct via_spec *spec = codec->spec; - hda_nid_t nid, conn[8]; - unsigned int type; - int i, n; - - for (i = 0; i < spec->num_adc_nids; i++) { - nid = spec->adc_nids[i]; - while (nid) { - type = get_wcaps_type(get_wcaps(codec, nid)); - if (type == AC_WID_PIN) - break; - n = snd_hda_get_connections(codec, nid, conn, - ARRAY_SIZE(conn)); - if (n <= 0) - break; - if (n > 1) { - spec->mux_nids[i] = nid; - break; - } - nid = conn[0]; - } + int i, err; + + err = snd_hda_gen_build_pcms(codec); + if (err < 0 || codec->vendor_id != 0x11061708) + return err; + + /* We got noisy outputs on the right channel on VT1708 when + * 24bit samples are used. Until any workaround is found, + * disable the 24bit format, so far. + */ + for (i = 0; i < codec->num_pcms; i++) { + struct hda_pcm *info = &spec->gen.pcm_rec[i]; + if (!info->stream[SNDRV_PCM_STREAM_PLAYBACK].substreams || + info->pcm_type != HDA_PCM_TYPE_AUDIO) + continue; + info->stream[SNDRV_PCM_STREAM_PLAYBACK].formats = + SNDRV_PCM_FMTBIT_S16_LE; } + return 0; } @@ -2849,7 +696,15 @@ static int patch_vt1708(struct hda_codec *codec) if (spec == NULL) return -ENOMEM; - spec->aa_mix_nid = 0x17; + spec->gen.mixer_nid = 0x17; + + /* set jackpoll_interval while parsing the codec */ + codec->jackpoll_interval = msecs_to_jiffies(100); + spec->vt1708_jack_detect = 1; + + /* don't support the input jack switching due to lack of unsol event */ + /* (it may work with polling, though, but it needs testing) */ + spec->gen.suppress_auto_mic = 1; /* Add HP and CD pin config connect bit re-config action */ vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID); @@ -2863,18 +718,16 @@ static int patch_vt1708(struct hda_codec *codec) } /* add jack detect on/off control */ - if (!via_clone_control(spec, &vt1708_jack_detect_ctl)) - return -ENOMEM; - - /* disable 32bit format on VT1708 */ - if (codec->vendor_id == 0x11061708) - spec->stream_analog_playback = &vt1708_pcm_analog_s16_playback; + spec->mixers[spec->num_mixers++] = vt1708_jack_detect_ctl; spec->init_verbs[spec->num_iverbs++] = vt1708_init_verbs; codec->patch_ops = via_patch_ops; + codec->patch_ops.build_pcms = vt1708_build_pcms; + + /* clear jackpoll_interval again; it's set dynamically */ + codec->jackpoll_interval = 0; - INIT_DELAYED_WORK(&spec->vt1708_hp_work, vt1708_update_hp_jack_state); return 0; } @@ -2888,7 +741,7 @@ static int patch_vt1709(struct hda_codec *codec) if (spec == NULL) return -ENOMEM; - spec->aa_mix_nid = 0x18; + spec->gen.mixer_nid = 0x18; err = via_parse_auto_config(codec); if (err < 0) { @@ -2932,7 +785,7 @@ static void set_widgets_power_state_vt1708B(struct hda_codec *codec) /* PW0 (19h), SW1 (18h), AOW1 (11h) */ parm = AC_PWRST_D3; set_pin_power_state(codec, 0x19, &parm); - if (spec->smart51_enabled) + if (smart51_enabled(codec)) set_pin_power_state(codec, 0x1b, &parm); update_power_state(codec, 0x18, parm); update_power_state(codec, 0x11, parm); @@ -2941,7 +794,7 @@ static void set_widgets_power_state_vt1708B(struct hda_codec *codec) if (is_8ch) { parm = AC_PWRST_D3; set_pin_power_state(codec, 0x22, &parm); - if (spec->smart51_enabled) + if (smart51_enabled(codec)) set_pin_power_state(codec, 0x1a, &parm); update_power_state(codec, 0x26, parm); update_power_state(codec, 0x24, parm); @@ -2949,7 +802,7 @@ static void set_widgets_power_state_vt1708B(struct hda_codec *codec) /* PW7(23h), SW2(27h), AOW2(25h) */ parm = AC_PWRST_D3; set_pin_power_state(codec, 0x23, &parm); - if (spec->smart51_enabled) + if (smart51_enabled(codec)) set_pin_power_state(codec, 0x1a, &parm); update_power_state(codec, 0x27, parm); update_power_state(codec, 0x25, parm); @@ -2969,7 +822,7 @@ static void set_widgets_power_state_vt1708B(struct hda_codec *codec) if (is_8ch) { update_power_state(codec, 0x25, parm); update_power_state(codec, 0x27, parm); - } else if (codec->vendor_id == 0x11064397 && spec->hp_independent_mode) + } else if (codec->vendor_id == 0x11064397 && spec->gen.indep_hp_enabled) update_power_state(codec, 0x25, parm); } @@ -2987,7 +840,7 @@ static int patch_vt1708B(struct hda_codec *codec) if (spec == NULL) return -ENOMEM; - spec->aa_mix_nid = 0x16; + spec->gen.mixer_nid = 0x16; /* automatic parse from the BIOS config */ err = via_parse_auto_config(codec); @@ -3012,58 +865,6 @@ static const struct hda_verb vt1708S_init_verbs[] = { { } }; -/* fill out digital output widgets; one for master and one for slave outputs */ -static void fill_dig_outs(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - int i; - - for (i = 0; i < spec->autocfg.dig_outs; i++) { - hda_nid_t nid; - int conn; - - nid = spec->autocfg.dig_out_pins[i]; - if (!nid) - continue; - conn = snd_hda_get_connections(codec, nid, &nid, 1); - if (conn < 1) - continue; - if (!spec->multiout.dig_out_nid) - spec->multiout.dig_out_nid = nid; - else { - spec->slave_dig_outs[0] = nid; - break; /* at most two dig outs */ - } - } -} - -static void fill_dig_in(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - hda_nid_t dig_nid; - int i, err; - - if (!spec->autocfg.dig_in_pin) - return; - - dig_nid = codec->start_nid; - for (i = 0; i < codec->num_nodes; i++, dig_nid++) { - unsigned int wcaps = get_wcaps(codec, dig_nid); - if (get_wcaps_type(wcaps) != AC_WID_AUD_IN) - continue; - if (!(wcaps & AC_WCAP_DIGITAL)) - continue; - if (!(wcaps & AC_WCAP_CONN_LIST)) - continue; - err = get_connection_index(codec, dig_nid, - spec->autocfg.dig_in_pin); - if (err >= 0) { - spec->dig_in_nid = dig_nid; - break; - } - } -} - static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin, int offset, int num_steps, int step_size) { @@ -3084,21 +885,10 @@ static int patch_vt1708S(struct hda_codec *codec) if (spec == NULL) return -ENOMEM; - spec->aa_mix_nid = 0x16; + spec->gen.mixer_nid = 0x16; override_mic_boost(codec, 0x1a, 0, 3, 40); override_mic_boost(codec, 0x1e, 0, 3, 40); - /* automatic parse from the BIOS config */ - err = via_parse_auto_config(codec); - if (err < 0) { - via_free(codec); - return err; - } - - spec->init_verbs[spec->num_iverbs++] = vt1708S_init_verbs; - - codec->patch_ops = via_patch_ops; - /* correct names for VT1708BCE */ if (get_codec_type(codec) == VT1708BCE) { kfree(codec->chip_name); @@ -3115,6 +905,18 @@ static int patch_vt1708S(struct hda_codec *codec) sizeof(codec->bus->card->mixername), "%s %s", codec->vendor_name, codec->chip_name); } + + /* automatic parse from the BIOS config */ + err = via_parse_auto_config(codec); + if (err < 0) { + via_free(codec); + return err; + } + + spec->init_verbs[spec->num_iverbs++] = vt1708S_init_verbs; + + codec->patch_ops = via_patch_ops; + spec->set_widgets_power_state = set_widgets_power_state_vt1708B; return 0; } @@ -3169,7 +971,7 @@ static int patch_vt1702(struct hda_codec *codec) if (spec == NULL) return -ENOMEM; - spec->aa_mix_nid = 0x1a; + spec->gen.mixer_nid = 0x1a; /* limit AA path volume to 0 dB */ snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT, @@ -3236,17 +1038,17 @@ static void set_widgets_power_state_vt1718S(struct hda_codec *codec) /* PW2 (26h), AOW2 (ah) */ parm = AC_PWRST_D3; set_pin_power_state(codec, 0x26, &parm); - if (spec->smart51_enabled) + if (smart51_enabled(codec)) set_pin_power_state(codec, 0x2b, &parm); update_power_state(codec, 0xa, parm); /* PW0 (24h), AOW0 (8h) */ parm = AC_PWRST_D3; set_pin_power_state(codec, 0x24, &parm); - if (!spec->hp_independent_mode) /* check for redirected HP */ + if (!spec->gen.indep_hp_enabled) /* check for redirected HP */ set_pin_power_state(codec, 0x28, &parm); update_power_state(codec, 0x8, parm); - if (!spec->hp_independent_mode && parm2 != AC_PWRST_D3) + if (!spec->gen.indep_hp_enabled && parm2 != AC_PWRST_D3) parm = parm2; update_power_state(codec, 0xb, parm); /* MW9 (21h), Mw2 (1ah), AOW0 (8h) */ @@ -3255,11 +1057,11 @@ static void set_widgets_power_state_vt1718S(struct hda_codec *codec) /* PW1 (25h), AOW1 (9h) */ parm = AC_PWRST_D3; set_pin_power_state(codec, 0x25, &parm); - if (spec->smart51_enabled) + if (smart51_enabled(codec)) set_pin_power_state(codec, 0x2a, &parm); update_power_state(codec, 0x9, parm); - if (spec->hp_independent_mode) { + if (spec->gen.indep_hp_enabled) { /* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) */ parm = AC_PWRST_D3; set_pin_power_state(codec, 0x28, &parm); @@ -3279,9 +1081,9 @@ static int add_secret_dac_path(struct hda_codec *codec) hda_nid_t conn[8]; hda_nid_t nid; - if (!spec->aa_mix_nid) + if (!spec->gen.mixer_nid) return 0; - nums = snd_hda_get_connections(codec, spec->aa_mix_nid, conn, + nums = snd_hda_get_connections(codec, spec->gen.mixer_nid, conn, ARRAY_SIZE(conn) - 1); for (i = 0; i < nums; i++) { if (get_wcaps_type(get_wcaps(codec, conn[i])) == AC_WID_AUD_OUT) @@ -3296,7 +1098,7 @@ static int add_secret_dac_path(struct hda_codec *codec) !(caps & AC_WCAP_DIGITAL)) { conn[nums++] = nid; return snd_hda_override_conn_list(codec, - spec->aa_mix_nid, + spec->gen.mixer_nid, nums, conn); } } @@ -3314,7 +1116,7 @@ static int patch_vt1718S(struct hda_codec *codec) if (spec == NULL) return -ENOMEM; - spec->aa_mix_nid = 0x21; + spec->gen.mixer_nid = 0x21; override_mic_boost(codec, 0x2b, 0, 3, 40); override_mic_boost(codec, 0x29, 0, 3, 40); add_secret_dac_path(codec); @@ -3445,7 +1247,7 @@ static void set_widgets_power_state_vt1716S(struct hda_codec *codec) parm = AC_PWRST_D3; set_pin_power_state(codec, 0x19, &parm); /* Smart 5.1 PW2(1bh) */ - if (spec->smart51_enabled) + if (smart51_enabled(codec)) set_pin_power_state(codec, 0x1b, &parm); update_power_state(codec, 0x18, parm); update_power_state(codec, 0x11, parm); @@ -3454,12 +1256,12 @@ static void set_widgets_power_state_vt1716S(struct hda_codec *codec) parm = AC_PWRST_D3; set_pin_power_state(codec, 0x23, &parm); /* Smart 5.1 PW1(1ah) */ - if (spec->smart51_enabled) + if (smart51_enabled(codec)) set_pin_power_state(codec, 0x1a, &parm); update_power_state(codec, 0x27, parm); /* Smart 5.1 PW5(1eh) */ - if (spec->smart51_enabled) + if (smart51_enabled(codec)) set_pin_power_state(codec, 0x1e, &parm); update_power_state(codec, 0x25, parm); @@ -3471,7 +1273,7 @@ static void set_widgets_power_state_vt1716S(struct hda_codec *codec) mono_out = 0; else { present = snd_hda_jack_detect(codec, 0x1d); - if (!spec->hp_independent_mode && present) + if (!spec->gen.indep_hp_enabled && present) mono_out = 0; else mono_out = 1; @@ -3486,7 +1288,7 @@ static void set_widgets_power_state_vt1716S(struct hda_codec *codec) set_pin_power_state(codec, 0x1c, &parm); set_pin_power_state(codec, 0x1d, &parm); /* HP Independent Mode, power on AOW3 */ - if (spec->hp_independent_mode) + if (spec->gen.indep_hp_enabled) update_power_state(codec, 0x25, parm); /* force to D0 for internal Speaker */ @@ -3505,7 +1307,7 @@ static int patch_vt1716S(struct hda_codec *codec) if (spec == NULL) return -ENOMEM; - spec->aa_mix_nid = 0x16; + spec->gen.mixer_nid = 0x16; override_mic_boost(codec, 0x1a, 0, 3, 40); override_mic_boost(codec, 0x1e, 0, 3, 40); @@ -3518,9 +1320,7 @@ static int patch_vt1716S(struct hda_codec *codec) spec->init_verbs[spec->num_iverbs++] = vt1716S_init_verbs; - spec->mixers[spec->num_mixers] = vt1716s_dmic_mixer; - spec->num_mixers++; - + spec->mixers[spec->num_mixers++] = vt1716s_dmic_mixer; spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer; codec->patch_ops = via_patch_ops; @@ -3605,7 +1405,7 @@ static void set_widgets_power_state_vt2002P(struct hda_codec *codec) update_power_state(codec, 0x35, parm); } - if (spec->hp_independent_mode) + if (spec->gen.indep_hp_enabled) update_power_state(codec, 0x9, AC_PWRST_D0); /* Class-D */ @@ -3703,7 +1503,7 @@ static int patch_vt2002P(struct hda_codec *codec) if (spec == NULL) return -ENOMEM; - spec->aa_mix_nid = 0x21; + spec->gen.mixer_nid = 0x21; override_mic_boost(codec, 0x2b, 0, 3, 40); override_mic_boost(codec, 0x29, 0, 3, 40); if (spec->codec_type == VT1802) @@ -3774,7 +1574,7 @@ static void set_widgets_power_state_vt1812(struct hda_codec *codec) set_pin_power_state(codec, 0x25, &parm); update_power_state(codec, 0x15, parm); update_power_state(codec, 0x35, parm); - if (spec->hp_independent_mode) + if (spec->gen.indep_hp_enabled) update_power_state(codec, 0x9, AC_PWRST_D0); /* Internal Speaker */ @@ -3827,7 +1627,7 @@ static int patch_vt1812(struct hda_codec *codec) if (spec == NULL) return -ENOMEM; - spec->aa_mix_nid = 0x21; + spec->gen.mixer_nid = 0x21; override_mic_boost(codec, 0x2b, 0, 3, 40); override_mic_boost(codec, 0x29, 0, 3, 40); add_secret_dac_path(codec); @@ -3897,7 +1697,7 @@ static void set_widgets_power_state_vt3476(struct hda_codec *codec) parm = AC_PWRST_D3; set_pin_power_state(codec, 0x26, &parm); update_power_state(codec, 0x36, parm); - if (spec->smart51_enabled) { + if (smart51_enabled(codec)) { /* PW7(2bh), MW7(3bh), MUX7(1Bh) */ set_pin_power_state(codec, 0x2b, &parm); update_power_state(codec, 0x3b, parm); @@ -3909,7 +1709,7 @@ static void set_widgets_power_state_vt3476(struct hda_codec *codec) parm = AC_PWRST_D3; set_pin_power_state(codec, 0x25, &parm); update_power_state(codec, 0x35, parm); - if (spec->smart51_enabled) { + if (smart51_enabled(codec)) { /* PW6(2ah), MW6(3ah), MUX6(1ah) */ set_pin_power_state(codec, 0x2a, &parm); update_power_state(codec, 0x3a, parm); @@ -3922,7 +1722,7 @@ static void set_widgets_power_state_vt3476(struct hda_codec *codec) set_pin_power_state(codec, 0x28, &parm); update_power_state(codec, 0x38, parm); update_power_state(codec, 0x18, parm); - if (spec->hp_independent_mode) + if (spec->gen.indep_hp_enabled) update_conv_power_state(codec, 0xb, parm, 3); parm2 = parm; /* for pin 0x0b */ @@ -3930,7 +1730,7 @@ static void set_widgets_power_state_vt3476(struct hda_codec *codec) parm = AC_PWRST_D3; set_pin_power_state(codec, 0x24, &parm); update_power_state(codec, 0x34, parm); - if (!spec->hp_independent_mode && parm2 != AC_PWRST_D3) + if (!spec->gen.indep_hp_enabled && parm2 != AC_PWRST_D3) parm = parm2; update_conv_power_state(codec, 0x8, parm, 0); /* MW9 (21h), Mw2 (1ah), AOW0 (8h) */ @@ -3947,7 +1747,7 @@ static int patch_vt3476(struct hda_codec *codec) if (spec == NULL) return -ENOMEM; - spec->aa_mix_nid = 0x3f; + spec->gen.mixer_nid = 0x3f; add_secret_dac_path(codec); /* automatic parse from the BIOS config */ -- cgit v1.2.3 From ae177c3fd0667df21b60bc8e031607de257e58e4 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 14 Jan 2013 12:13:06 +0100 Subject: ALSA: hda - Add capture_switch_hook to generic parser Add a hook for the capture mixer switch. This will be used by IDT codecs for controlling the mic-mute LED. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 16 +++++++++++++++- sound/pci/hda/hda_generic.h | 3 +++ 2 files changed, 18 insertions(+), 1 deletion(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 4bc4cd933866..932e6a133f3d 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -2517,9 +2517,23 @@ static const struct snd_kcontrol_new cap_vol_temp = { static int cap_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - return cap_put_caller(kcontrol, ucontrol, + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct hda_gen_spec *spec = codec->spec; + int ret; + + ret = cap_put_caller(kcontrol, ucontrol, snd_hda_mixer_amp_switch_put, NID_PATH_MUTE_CTL); + if (ret < 0) + return ret; + + if (spec->capture_switch_hook) { + bool enable = (ucontrol->value.integer.value[0] || + ucontrol->value.integer.value[1]); + spec->capture_switch_hook(codec, enable); + } + + return ret; } static const struct snd_kcontrol_new cap_sw_temp = { diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h index bfa2d973268b..1ceaacd1b775 100644 --- a/sound/pci/hda/hda_generic.h +++ b/sound/pci/hda/hda_generic.h @@ -227,6 +227,9 @@ struct hda_gen_spec { struct hda_jack_tbl *tbl); void (*mic_autoswitch_hook)(struct hda_codec *codec, struct hda_jack_tbl *tbl); + + /* capture switch hook (for mic-mute LED) */ + void (*capture_switch_hook)(struct hda_codec *codec, bool enable); }; int snd_hda_gen_spec_init(struct hda_gen_spec *spec); -- cgit v1.2.3 From d39a3ae8215ad90e68ca5fbbced507b07052018b Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 14 Jan 2013 14:06:26 +0100 Subject: ALSA: hda - Use standard fixup table for STAC9200 Convert patch_stac9200() to use the standard fixup table instead of manual switch-case with board_config. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_sigmatel.c | 369 +++++++++++++++++++++++++++++------------ 1 file changed, 265 insertions(+), 104 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 9cc4cb9b4bd2..e5dffd25d83a 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -50,7 +50,6 @@ enum { }; enum { - STAC_AUTO, STAC_REF, STAC_9200_OQO, STAC_9200_DELL_D21, @@ -66,6 +65,7 @@ enum { STAC_9200_M4, STAC_9200_M4_2, STAC_9200_PANASONIC, + STAC_9200_EAPD_INIT, STAC_9200_MODELS }; @@ -1197,18 +1197,40 @@ static int stac92xx_build_controls(struct hda_codec *codec) return 0; } -static const unsigned int ref9200_pin_configs[8] = { - 0x01c47010, 0x01447010, 0x0221401f, 0x01114010, - 0x02a19020, 0x01a19021, 0x90100140, 0x01813122, +static const struct hda_pintbl ref9200_pin_configs[] = { + { 0x08, 0x01c47010 }, + { 0x09, 0x01447010 }, + { 0x0d, 0x0221401f }, + { 0x0e, 0x01114010 }, + { 0x0f, 0x02a19020 }, + { 0x10, 0x01a19021 }, + { 0x11, 0x90100140 }, + { 0x12, 0x01813122 }, + {} }; -static const unsigned int gateway9200_m4_pin_configs[8] = { - 0x400000fe, 0x404500f4, 0x400100f0, 0x90110010, - 0x400100f1, 0x02a1902e, 0x500000f2, 0x500000f3, +static const struct hda_pintbl gateway9200_m4_pin_configs[] = { + { 0x08, 0x400000fe }, + { 0x09, 0x404500f4 }, + { 0x0d, 0x400100f0 }, + { 0x0e, 0x90110010 }, + { 0x0f, 0x400100f1 }, + { 0x10, 0x02a1902e }, + { 0x11, 0x500000f2 }, + { 0x12, 0x500000f3 }, + {} }; -static const unsigned int gateway9200_m4_2_pin_configs[8] = { - 0x400000fe, 0x404500f4, 0x400100f0, 0x90110010, - 0x400100f1, 0x02a1902e, 0x500000f2, 0x500000f3, + +static const struct hda_pintbl gateway9200_m4_2_pin_configs[] = { + { 0x08, 0x400000fe }, + { 0x09, 0x404500f4 }, + { 0x0d, 0x400100f0 }, + { 0x0e, 0x90110010 }, + { 0x0f, 0x400100f1 }, + { 0x10, 0x02a1902e }, + { 0x11, 0x500000f2 }, + { 0x12, 0x500000f3 }, + {} }; /* @@ -1217,9 +1239,16 @@ static const unsigned int gateway9200_m4_2_pin_configs[8] = { 102801DE 102801E8 */ -static const unsigned int dell9200_d21_pin_configs[8] = { - 0x400001f0, 0x400001f1, 0x02214030, 0x01014010, - 0x02a19020, 0x01a19021, 0x90100140, 0x01813122, +static const struct hda_pintbl dell9200_d21_pin_configs[] = { + { 0x08, 0x400001f0 }, + { 0x09, 0x400001f1 }, + { 0x0d, 0x02214030 }, + { 0x0e, 0x01014010 }, + { 0x0f, 0x02a19020 }, + { 0x10, 0x01a19021 }, + { 0x11, 0x90100140 }, + { 0x12, 0x01813122 }, + {} }; /* @@ -1227,9 +1256,16 @@ static const unsigned int dell9200_d21_pin_configs[8] = { 102801C0 102801C1 */ -static const unsigned int dell9200_d22_pin_configs[8] = { - 0x400001f0, 0x400001f1, 0x0221401f, 0x01014010, - 0x01813020, 0x02a19021, 0x90100140, 0x400001f2, +static const struct hda_pintbl dell9200_d22_pin_configs[] = { + { 0x08, 0x400001f0 }, + { 0x09, 0x400001f1 }, + { 0x0d, 0x0221401f }, + { 0x0e, 0x01014010 }, + { 0x0f, 0x01813020 }, + { 0x10, 0x02a19021 }, + { 0x11, 0x90100140 }, + { 0x12, 0x400001f2 }, + {} }; /* @@ -1241,9 +1277,16 @@ static const unsigned int dell9200_d22_pin_configs[8] = { 102801DA 102801E3 */ -static const unsigned int dell9200_d23_pin_configs[8] = { - 0x400001f0, 0x400001f1, 0x0221401f, 0x01014010, - 0x01813020, 0x01a19021, 0x90100140, 0x400001f2, +static const struct hda_pintbl dell9200_d23_pin_configs[] = { + { 0x08, 0x400001f0 }, + { 0x09, 0x400001f1 }, + { 0x0d, 0x0221401f }, + { 0x0e, 0x01014010 }, + { 0x0f, 0x01813020 }, + { 0x10, 0x01a19021 }, + { 0x11, 0x90100140 }, + { 0x12, 0x400001f2 }, + {} }; @@ -1252,9 +1295,16 @@ static const unsigned int dell9200_d23_pin_configs[8] = { 102801B5 (Dell Inspiron 630m) 102801D8 (Dell Inspiron 640m) */ -static const unsigned int dell9200_m21_pin_configs[8] = { - 0x40c003fa, 0x03441340, 0x0321121f, 0x90170310, - 0x408003fb, 0x03a11020, 0x401003fc, 0x403003fd, +static const struct hda_pintbl dell9200_m21_pin_configs[] = { + { 0x08, 0x40c003fa }, + { 0x09, 0x03441340 }, + { 0x0d, 0x0321121f }, + { 0x0e, 0x90170310 }, + { 0x0f, 0x408003fb }, + { 0x10, 0x03a11020 }, + { 0x11, 0x401003fc }, + { 0x12, 0x403003fd }, + {} }; /* @@ -1265,9 +1315,16 @@ static const unsigned int dell9200_m21_pin_configs[8] = { 102801D4 102801D6 */ -static const unsigned int dell9200_m22_pin_configs[8] = { - 0x40c003fa, 0x0144131f, 0x0321121f, 0x90170310, - 0x90a70321, 0x03a11020, 0x401003fb, 0x40f000fc, +static const struct hda_pintbl dell9200_m22_pin_configs[] = { + { 0x08, 0x40c003fa }, + { 0x09, 0x0144131f }, + { 0x0d, 0x0321121f }, + { 0x0e, 0x90170310 }, + { 0x0f, 0x90a70321 }, + { 0x10, 0x03a11020 }, + { 0x11, 0x401003fb }, + { 0x12, 0x40f000fc }, + {} }; /* @@ -1275,9 +1332,16 @@ static const unsigned int dell9200_m22_pin_configs[8] = { 102801CE (Dell XPS M1710) 102801CF (Dell Precision M90) */ -static const unsigned int dell9200_m23_pin_configs[8] = { - 0x40c003fa, 0x01441340, 0x0421421f, 0x90170310, - 0x408003fb, 0x04a1102e, 0x90170311, 0x403003fc, +static const struct hda_pintbl dell9200_m23_pin_configs[] = { + { 0x08, 0x40c003fa }, + { 0x09, 0x01441340 }, + { 0x0d, 0x0421421f }, + { 0x0e, 0x90170310 }, + { 0x0f, 0x408003fb }, + { 0x10, 0x04a1102e }, + { 0x11, 0x90170311 }, + { 0x12, 0x403003fc }, + {} }; /* @@ -1287,9 +1351,16 @@ static const unsigned int dell9200_m23_pin_configs[8] = { 102801CB (Dell Latitude 120L) 102801D3 */ -static const unsigned int dell9200_m24_pin_configs[8] = { - 0x40c003fa, 0x404003fb, 0x0321121f, 0x90170310, - 0x408003fc, 0x03a11020, 0x401003fd, 0x403003fe, +static const struct hda_pintbl dell9200_m24_pin_configs[] = { + { 0x08, 0x40c003fa }, + { 0x09, 0x404003fb }, + { 0x0d, 0x0321121f }, + { 0x0e, 0x90170310 }, + { 0x0f, 0x408003fc }, + { 0x10, 0x03a11020 }, + { 0x11, 0x401003fd }, + { 0x12, 0x403003fe }, + {} }; /* @@ -1298,9 +1369,16 @@ static const unsigned int dell9200_m24_pin_configs[8] = { 102801EE 102801EF */ -static const unsigned int dell9200_m25_pin_configs[8] = { - 0x40c003fa, 0x01441340, 0x0421121f, 0x90170310, - 0x408003fb, 0x04a11020, 0x401003fc, 0x403003fd, +static const struct hda_pintbl dell9200_m25_pin_configs[] = { + { 0x08, 0x40c003fa }, + { 0x09, 0x01441340 }, + { 0x0d, 0x0421121f }, + { 0x0e, 0x90170310 }, + { 0x0f, 0x408003fb }, + { 0x10, 0x04a11020 }, + { 0x11, 0x401003fc }, + { 0x12, 0x403003fd }, + {} }; /* @@ -1308,64 +1386,163 @@ static const unsigned int dell9200_m25_pin_configs[8] = { 102801F5 (Dell Inspiron 1501) 102801F6 */ -static const unsigned int dell9200_m26_pin_configs[8] = { - 0x40c003fa, 0x404003fb, 0x0421121f, 0x90170310, - 0x408003fc, 0x04a11020, 0x401003fd, 0x403003fe, +static const struct hda_pintbl dell9200_m26_pin_configs[] = { + { 0x08, 0x40c003fa }, + { 0x09, 0x404003fb }, + { 0x0d, 0x0421121f }, + { 0x0e, 0x90170310 }, + { 0x0f, 0x408003fc }, + { 0x10, 0x04a11020 }, + { 0x11, 0x401003fd }, + { 0x12, 0x403003fe }, + {} }; /* STAC 9200-32 102801CD (Dell Inspiron E1705/9400) */ -static const unsigned int dell9200_m27_pin_configs[8] = { - 0x40c003fa, 0x01441340, 0x0421121f, 0x90170310, - 0x90170310, 0x04a11020, 0x90170310, 0x40f003fc, +static const struct hda_pintbl dell9200_m27_pin_configs[] = { + { 0x08, 0x40c003fa }, + { 0x09, 0x01441340 }, + { 0x0d, 0x0421121f }, + { 0x0e, 0x90170310 }, + { 0x0f, 0x90170310 }, + { 0x10, 0x04a11020 }, + { 0x11, 0x90170310 }, + { 0x12, 0x40f003fc }, + {} }; -static const unsigned int oqo9200_pin_configs[8] = { - 0x40c000f0, 0x404000f1, 0x0221121f, 0x02211210, - 0x90170111, 0x90a70120, 0x400000f2, 0x400000f3, +static const struct hda_pintbl oqo9200_pin_configs[] = { + { 0x08, 0x40c000f0 }, + { 0x09, 0x404000f1 }, + { 0x0d, 0x0221121f }, + { 0x0e, 0x02211210 }, + { 0x0f, 0x90170111 }, + { 0x10, 0x90a70120 }, + { 0x11, 0x400000f2 }, + { 0x12, 0x400000f3 }, + {} }; -static const unsigned int *stac9200_brd_tbl[STAC_9200_MODELS] = { - [STAC_REF] = ref9200_pin_configs, - [STAC_9200_OQO] = oqo9200_pin_configs, - [STAC_9200_DELL_D21] = dell9200_d21_pin_configs, - [STAC_9200_DELL_D22] = dell9200_d22_pin_configs, - [STAC_9200_DELL_D23] = dell9200_d23_pin_configs, - [STAC_9200_DELL_M21] = dell9200_m21_pin_configs, - [STAC_9200_DELL_M22] = dell9200_m22_pin_configs, - [STAC_9200_DELL_M23] = dell9200_m23_pin_configs, - [STAC_9200_DELL_M24] = dell9200_m24_pin_configs, - [STAC_9200_DELL_M25] = dell9200_m25_pin_configs, - [STAC_9200_DELL_M26] = dell9200_m26_pin_configs, - [STAC_9200_DELL_M27] = dell9200_m27_pin_configs, - [STAC_9200_M4] = gateway9200_m4_pin_configs, - [STAC_9200_M4_2] = gateway9200_m4_2_pin_configs, - [STAC_9200_PANASONIC] = ref9200_pin_configs, +static void stac9200_fixup_panasonic(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct sigmatel_spec *spec = codec->spec; + + switch (action) { + case HDA_FIXUP_ACT_PRE_PROBE: + spec->gpio_mask = spec->gpio_dir = 0x09; + spec->gpio_data = 0x00; + break; + case HDA_FIXUP_ACT_PROBE: + /* CF-74 has no headphone detection, and the driver should *NOT* + * do detection and HP/speaker toggle because the hardware does it. + */ + spec->hp_detect = 0; + break; + } +} + + +static const struct hda_fixup stac9200_fixups[] = { + [STAC_REF] = { + .type = HDA_FIXUP_PINS, + .v.pins = ref9200_pin_configs, + }, + [STAC_9200_OQO] = { + .type = HDA_FIXUP_PINS, + .v.pins = oqo9200_pin_configs, + .chained = true, + .chain_id = STAC_9200_EAPD_INIT, + }, + [STAC_9200_DELL_D21] = { + .type = HDA_FIXUP_PINS, + .v.pins = dell9200_d21_pin_configs, + }, + [STAC_9200_DELL_D22] = { + .type = HDA_FIXUP_PINS, + .v.pins = dell9200_d22_pin_configs, + }, + [STAC_9200_DELL_D23] = { + .type = HDA_FIXUP_PINS, + .v.pins = dell9200_d23_pin_configs, + }, + [STAC_9200_DELL_M21] = { + .type = HDA_FIXUP_PINS, + .v.pins = dell9200_m21_pin_configs, + }, + [STAC_9200_DELL_M22] = { + .type = HDA_FIXUP_PINS, + .v.pins = dell9200_m22_pin_configs, + }, + [STAC_9200_DELL_M23] = { + .type = HDA_FIXUP_PINS, + .v.pins = dell9200_m23_pin_configs, + }, + [STAC_9200_DELL_M24] = { + .type = HDA_FIXUP_PINS, + .v.pins = dell9200_m24_pin_configs, + }, + [STAC_9200_DELL_M25] = { + .type = HDA_FIXUP_PINS, + .v.pins = dell9200_m25_pin_configs, + }, + [STAC_9200_DELL_M26] = { + .type = HDA_FIXUP_PINS, + .v.pins = dell9200_m26_pin_configs, + }, + [STAC_9200_DELL_M27] = { + .type = HDA_FIXUP_PINS, + .v.pins = dell9200_m27_pin_configs, + }, + [STAC_9200_M4] = { + .type = HDA_FIXUP_PINS, + .v.pins = gateway9200_m4_pin_configs, + .chained = true, + .chain_id = STAC_9200_EAPD_INIT, + }, + [STAC_9200_M4_2] = { + .type = HDA_FIXUP_PINS, + .v.pins = gateway9200_m4_2_pin_configs, + .chained = true, + .chain_id = STAC_9200_EAPD_INIT, + }, + [STAC_9200_PANASONIC] = { + .type = HDA_FIXUP_FUNC, + .v.func = stac9200_fixup_panasonic, + }, + [STAC_9200_EAPD_INIT] = { + .type = HDA_FIXUP_VERBS, + .v.verbs = (const struct hda_verb[]) { + {0x08, AC_VERB_SET_EAPD_BTLENABLE, 0x02}, + {} + }, + }, }; -static const char * const stac9200_models[STAC_9200_MODELS] = { - [STAC_AUTO] = "auto", - [STAC_REF] = "ref", - [STAC_9200_OQO] = "oqo", - [STAC_9200_DELL_D21] = "dell-d21", - [STAC_9200_DELL_D22] = "dell-d22", - [STAC_9200_DELL_D23] = "dell-d23", - [STAC_9200_DELL_M21] = "dell-m21", - [STAC_9200_DELL_M22] = "dell-m22", - [STAC_9200_DELL_M23] = "dell-m23", - [STAC_9200_DELL_M24] = "dell-m24", - [STAC_9200_DELL_M25] = "dell-m25", - [STAC_9200_DELL_M26] = "dell-m26", - [STAC_9200_DELL_M27] = "dell-m27", - [STAC_9200_M4] = "gateway-m4", - [STAC_9200_M4_2] = "gateway-m4-2", - [STAC_9200_PANASONIC] = "panasonic", -}; - -static const struct snd_pci_quirk stac9200_cfg_tbl[] = { +static const struct hda_model_fixup stac9200_models[] = { + { .id = STAC_REF, .name = "ref" }, + { .id = STAC_9200_OQO, .name = "oqo" }, + { .id = STAC_9200_DELL_D21, .name = "dell-d21" }, + { .id = STAC_9200_DELL_D22, .name = "dell-d22" }, + { .id = STAC_9200_DELL_D23, .name = "dell-d23" }, + { .id = STAC_9200_DELL_M21, .name = "dell-m21" }, + { .id = STAC_9200_DELL_M22, .name = "dell-m22" }, + { .id = STAC_9200_DELL_M23, .name = "dell-m23" }, + { .id = STAC_9200_DELL_M24, .name = "dell-m24" }, + { .id = STAC_9200_DELL_M25, .name = "dell-m25" }, + { .id = STAC_9200_DELL_M26, .name = "dell-m26" }, + { .id = STAC_9200_DELL_M27, .name = "dell-m27" }, + { .id = STAC_9200_M4, .name = "gateway-m4" }, + { .id = STAC_9200_M4_2, .name = "gateway-m4-2" }, + { .id = STAC_9200_PANASONIC, .name = "panasonic" }, + {} +}; + +static const struct snd_pci_quirk stac9200_fixup_tbl[] = { /* SigmaTel reference board */ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668, "DFI LanParty", STAC_REF), @@ -4358,6 +4535,8 @@ static int stac92xx_init(struct hda_codec *codec) if (spec->init) snd_hda_sequence_write(codec, spec->init); + snd_hda_apply_verbs(codec); + /* power down adcs initially */ if (spec->powerdown_adcs) for (i = 0; i < spec->num_adcs; i++) @@ -5177,15 +5356,9 @@ static int patch_stac9200(struct hda_codec *codec) spec = codec->spec; spec->linear_tone_beep = 1; - spec->board_config = snd_hda_check_board_config(codec, STAC_9200_MODELS, - stac9200_models, - stac9200_cfg_tbl); - if (spec->board_config < 0) - snd_printdd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", - codec->chip_name); - else - stac92xx_set_config_regs(codec, - stac9200_brd_tbl[spec->board_config]); + + snd_hda_pick_fixup(codec, stac9200_models, stac9200_fixup_tbl, + stac9200_fixups); spec->multiout.max_channels = 2; spec->multiout.num_dacs = 1; @@ -5196,19 +5369,11 @@ static int patch_stac9200(struct hda_codec *codec) spec->num_dmics = 0; spec->num_adcs = 1; spec->num_pwrs = 0; + snd_hda_add_verbs(codec, stac9200_eapd_init); - if (spec->board_config == STAC_9200_M4 || - spec->board_config == STAC_9200_M4_2 || - spec->board_config == STAC_9200_OQO) - spec->init = stac9200_eapd_init; - else - spec->init = stac9200_core_init; spec->mixer = stac9200_mixer; - if (spec->board_config == STAC_9200_PANASONIC) { - spec->gpio_mask = spec->gpio_dir = 0x09; - spec->gpio_data = 0x00; - } + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); err = stac9200_parse_auto_config(codec); if (err < 0) { @@ -5216,14 +5381,10 @@ static int patch_stac9200(struct hda_codec *codec) return err; } - /* CF-74 has no headphone detection, and the driver should *NOT* - * do detection and HP/speaker toggle because the hardware does it. - */ - if (spec->board_config == STAC_9200_PANASONIC) - spec->hp_detect = 0; - codec->patch_ops = stac92xx_patch_ops; + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); + return 0; } -- cgit v1.2.3 From d2077d40cbfc8c08cacd153f5b02f9b177f10da0 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 14 Jan 2013 14:20:16 +0100 Subject: ALSA: hda - Use standard fixup table for STAC925x Similar like the previous commit, convert patch_stac925x() to use the standard fixup table. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_sigmatel.c | 226 +++++++++++++++++++++++++---------------- 1 file changed, 141 insertions(+), 85 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index e5dffd25d83a..4c98b3009e51 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -123,7 +123,6 @@ enum { }; enum { - STAC_925x_AUTO, STAC_925x_REF, STAC_M1, STAC_M1_2, @@ -1618,70 +1617,159 @@ static const struct snd_pci_quirk stac9200_fixup_tbl[] = { {} /* terminator */ }; -static const unsigned int ref925x_pin_configs[8] = { - 0x40c003f0, 0x424503f2, 0x01813022, 0x02a19021, - 0x90a70320, 0x02214210, 0x01019020, 0x9033032e, +static const struct hda_pintbl ref925x_pin_configs[] = { + { 0x07, 0x40c003f0 }, + { 0x08, 0x424503f2 }, + { 0x0a, 0x01813022 }, + { 0x0b, 0x02a19021 }, + { 0x0c, 0x90a70320 }, + { 0x0d, 0x02214210 }, + { 0x10, 0x01019020 }, + { 0x11, 0x9033032e }, + {} }; -static const unsigned int stac925xM1_pin_configs[8] = { - 0x40c003f4, 0x424503f2, 0x400000f3, 0x02a19020, - 0x40a000f0, 0x90100210, 0x400003f1, 0x9033032e, +static const struct hda_pintbl stac925xM1_pin_configs[] = { + { 0x07, 0x40c003f4 }, + { 0x08, 0x424503f2 }, + { 0x0a, 0x400000f3 }, + { 0x0b, 0x02a19020 }, + { 0x0c, 0x40a000f0 }, + { 0x0d, 0x90100210 }, + { 0x10, 0x400003f1 }, + { 0x11, 0x9033032e }, + {} }; -static const unsigned int stac925xM1_2_pin_configs[8] = { - 0x40c003f4, 0x424503f2, 0x400000f3, 0x02a19020, - 0x40a000f0, 0x90100210, 0x400003f1, 0x9033032e, +static const struct hda_pintbl stac925xM1_2_pin_configs[] = { + { 0x07, 0x40c003f4 }, + { 0x08, 0x424503f2 }, + { 0x0a, 0x400000f3 }, + { 0x0b, 0x02a19020 }, + { 0x0c, 0x40a000f0 }, + { 0x0d, 0x90100210 }, + { 0x10, 0x400003f1 }, + { 0x11, 0x9033032e }, + {} }; -static const unsigned int stac925xM2_pin_configs[8] = { - 0x40c003f4, 0x424503f2, 0x400000f3, 0x02a19020, - 0x40a000f0, 0x90100210, 0x400003f1, 0x9033032e, +static const struct hda_pintbl stac925xM2_pin_configs[] = { + { 0x07, 0x40c003f4 }, + { 0x08, 0x424503f2 }, + { 0x0a, 0x400000f3 }, + { 0x0b, 0x02a19020 }, + { 0x0c, 0x40a000f0 }, + { 0x0d, 0x90100210 }, + { 0x10, 0x400003f1 }, + { 0x11, 0x9033032e }, + {} }; -static const unsigned int stac925xM2_2_pin_configs[8] = { - 0x40c003f4, 0x424503f2, 0x400000f3, 0x02a19020, - 0x40a000f0, 0x90100210, 0x400003f1, 0x9033032e, +static const struct hda_pintbl stac925xM2_2_pin_configs[] = { + { 0x07, 0x40c003f4 }, + { 0x08, 0x424503f2 }, + { 0x0a, 0x400000f3 }, + { 0x0b, 0x02a19020 }, + { 0x0c, 0x40a000f0 }, + { 0x0d, 0x90100210 }, + { 0x10, 0x400003f1 }, + { 0x11, 0x9033032e }, + {} }; -static const unsigned int stac925xM3_pin_configs[8] = { - 0x40c003f4, 0x424503f2, 0x400000f3, 0x02a19020, - 0x40a000f0, 0x90100210, 0x400003f1, 0x503303f3, +static const struct hda_pintbl stac925xM3_pin_configs[] = { + { 0x07, 0x40c003f4 }, + { 0x08, 0x424503f2 }, + { 0x0a, 0x400000f3 }, + { 0x0b, 0x02a19020 }, + { 0x0c, 0x40a000f0 }, + { 0x0d, 0x90100210 }, + { 0x10, 0x400003f1 }, + { 0x11, 0x503303f3 }, + {} }; -static const unsigned int stac925xM5_pin_configs[8] = { - 0x40c003f4, 0x424503f2, 0x400000f3, 0x02a19020, - 0x40a000f0, 0x90100210, 0x400003f1, 0x9033032e, +static const struct hda_pintbl stac925xM5_pin_configs[] = { + { 0x07, 0x40c003f4 }, + { 0x08, 0x424503f2 }, + { 0x0a, 0x400000f3 }, + { 0x0b, 0x02a19020 }, + { 0x0c, 0x40a000f0 }, + { 0x0d, 0x90100210 }, + { 0x10, 0x400003f1 }, + { 0x11, 0x9033032e }, + {} }; -static const unsigned int stac925xM6_pin_configs[8] = { - 0x40c003f4, 0x424503f2, 0x400000f3, 0x02a19020, - 0x40a000f0, 0x90100210, 0x400003f1, 0x90330320, +static const struct hda_pintbl stac925xM6_pin_configs[] = { + { 0x07, 0x40c003f4 }, + { 0x08, 0x424503f2 }, + { 0x0a, 0x400000f3 }, + { 0x0b, 0x02a19020 }, + { 0x0c, 0x40a000f0 }, + { 0x0d, 0x90100210 }, + { 0x10, 0x400003f1 }, + { 0x11, 0x90330320 }, + {} }; -static const unsigned int *stac925x_brd_tbl[STAC_925x_MODELS] = { - [STAC_REF] = ref925x_pin_configs, - [STAC_M1] = stac925xM1_pin_configs, - [STAC_M1_2] = stac925xM1_2_pin_configs, - [STAC_M2] = stac925xM2_pin_configs, - [STAC_M2_2] = stac925xM2_2_pin_configs, - [STAC_M3] = stac925xM3_pin_configs, - [STAC_M5] = stac925xM5_pin_configs, - [STAC_M6] = stac925xM6_pin_configs, +static const struct hda_fixup stac925x_fixups[] = { + [STAC_REF] = { + .type = HDA_FIXUP_PINS, + .v.pins = ref925x_pin_configs, + }, + [STAC_M1] = { + .type = HDA_FIXUP_PINS, + .v.pins = stac925xM1_pin_configs, + }, + [STAC_M1_2] = { + .type = HDA_FIXUP_PINS, + .v.pins = stac925xM1_2_pin_configs, + }, + [STAC_M2] = { + .type = HDA_FIXUP_PINS, + .v.pins = stac925xM2_pin_configs, + }, + [STAC_M2_2] = { + .type = HDA_FIXUP_PINS, + .v.pins = stac925xM2_2_pin_configs, + }, + [STAC_M3] = { + .type = HDA_FIXUP_PINS, + .v.pins = stac925xM3_pin_configs, + }, + [STAC_M5] = { + .type = HDA_FIXUP_PINS, + .v.pins = stac925xM5_pin_configs, + }, + [STAC_M6] = { + .type = HDA_FIXUP_PINS, + .v.pins = stac925xM6_pin_configs, + }, }; -static const char * const stac925x_models[STAC_925x_MODELS] = { - [STAC_925x_AUTO] = "auto", - [STAC_REF] = "ref", - [STAC_M1] = "m1", - [STAC_M1_2] = "m1-2", - [STAC_M2] = "m2", - [STAC_M2_2] = "m2-2", - [STAC_M3] = "m3", - [STAC_M5] = "m5", - [STAC_M6] = "m6", +static const struct hda_model_fixup stac925x_models[] = { + { .id = STAC_REF, .name = "ref" }, + { .id = STAC_M1, .name = "m1" }, + { .id = STAC_M1_2, .name = "m1-2" }, + { .id = STAC_M2, .name = "m2" }, + { .id = STAC_M2_2, .name = "m2-2" }, + { .id = STAC_M3, .name = "m3" }, + { .id = STAC_M5, .name = "m5" }, + { .id = STAC_M6, .name = "m6" }, + {} }; -static const struct snd_pci_quirk stac925x_codec_id_cfg_tbl[] = { +static const struct snd_pci_quirk stac925x_fixup_tbl[] = { + /* SigmaTel reference board */ + SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668, "DFI LanParty", STAC_REF), + SND_PCI_QUIRK(PCI_VENDOR_ID_DFI, 0x3101, "DFI LanParty", STAC_REF), + SND_PCI_QUIRK(0x8384, 0x7632, "Stac9202 Reference Board", STAC_REF), + + /* Default table for unknown ID */ + SND_PCI_QUIRK(0x1002, 0x437b, "Gateway mobile", STAC_M2_2), + + /* gateway machines are checked via codec ssid */ SND_PCI_QUIRK(0x107b, 0x0316, "Gateway M255", STAC_M2), SND_PCI_QUIRK(0x107b, 0x0366, "Gateway MP6954", STAC_M5), SND_PCI_QUIRK(0x107b, 0x0461, "Gateway NX560XL", STAC_M1), @@ -1695,18 +1783,6 @@ static const struct snd_pci_quirk stac925x_codec_id_cfg_tbl[] = { {} /* terminator */ }; -static const struct snd_pci_quirk stac925x_cfg_tbl[] = { - /* SigmaTel reference board */ - SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668, "DFI LanParty", STAC_REF), - SND_PCI_QUIRK(PCI_VENDOR_ID_DFI, 0x3101, "DFI LanParty", STAC_REF), - SND_PCI_QUIRK(0x8384, 0x7632, "Stac9202 Reference Board", STAC_REF), - - /* Default table for unknown ID */ - SND_PCI_QUIRK(0x1002, 0x437b, "Gateway mobile", STAC_M2_2), - - {} /* terminator */ -}; - static const unsigned int ref92hd73xx_pin_configs[13] = { 0x02214030, 0x02a19040, 0x01a19020, 0x02214030, 0x0181302e, 0x01014010, 0x01014020, 0x01014030, @@ -5401,25 +5477,8 @@ static int patch_stac925x(struct hda_codec *codec) spec = codec->spec; spec->linear_tone_beep = 1; - /* Check first for codec ID */ - spec->board_config = snd_hda_check_board_codec_sid_config(codec, - STAC_925x_MODELS, - stac925x_models, - stac925x_codec_id_cfg_tbl); - - /* Now checks for PCI ID, if codec ID is not found */ - if (spec->board_config < 0) - spec->board_config = snd_hda_check_board_config(codec, - STAC_925x_MODELS, - stac925x_models, - stac925x_cfg_tbl); - again: - if (spec->board_config < 0) - snd_printdd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", - codec->chip_name); - else - stac92xx_set_config_regs(codec, - stac925x_brd_tbl[spec->board_config]); + snd_hda_pick_fixup(codec, stac925x_models, stac925x_fixup_tbl, + stac925x_fixups); spec->multiout.max_channels = 2; spec->multiout.num_dacs = 1; @@ -5444,22 +5503,17 @@ static int patch_stac925x(struct hda_codec *codec) break; } - spec->init = stac925x_core_init; + snd_hda_add_verbs(codec, stac925x_core_init); spec->mixer = stac925x_mixer; spec->num_caps = 1; spec->capvols = stac925x_capvols; spec->capsws = stac925x_capsws; + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); + err = stac92xx_parse_auto_config(codec); - if (!err) { - if (spec->board_config < 0) { - printk(KERN_WARNING "hda_codec: No auto-config is " - "available, default to model=ref\n"); - spec->board_config = STAC_925x_REF; - goto again; - } + if (!err) err = -EINVAL; - } if (err < 0) { stac92xx_free(codec); return err; @@ -5467,6 +5521,8 @@ static int patch_stac925x(struct hda_codec *codec) codec->patch_ops = stac92xx_patch_ops; + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); + return 0; } -- cgit v1.2.3 From fc268c10cadb4fb171381cd1fe04849877412e40 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 14 Jan 2013 14:29:36 +0100 Subject: ALSA: hda - Use standard fixup table for STAC9872 Now for STAC9872. It has a small fixup table, fortunately. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_sigmatel.c | 49 ++++++++++++++++++++++++------------------ 1 file changed, 28 insertions(+), 21 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 4c98b3009e51..533afb698fb9 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -176,7 +176,6 @@ enum { }; enum { - STAC_9872_AUTO, STAC_9872_VAIO, STAC_9872_MODELS }; @@ -6606,22 +6605,32 @@ static const unsigned long stac9872_capvols[] = { }; #define stac9872_capsws stac9872_capvols -static const unsigned int stac9872_vaio_pin_configs[9] = { - 0x03211020, 0x411111f0, 0x411111f0, 0x03a15030, - 0x411111f0, 0x90170110, 0x411111f0, 0x411111f0, - 0x90a7013e +static const struct hda_pintbl stac9872_vaio_pin_configs[] = { + { 0x0a, 0x03211020 }, + { 0x0b, 0x411111f0 }, + { 0x0c, 0x411111f0 }, + { 0x0d, 0x03a15030 }, + { 0x0e, 0x411111f0 }, + { 0x0f, 0x90170110 }, + { 0x11, 0x411111f0 }, + { 0x13, 0x411111f0 }, + { 0x14, 0x90a7013e }, + {} }; -static const char * const stac9872_models[STAC_9872_MODELS] = { - [STAC_9872_AUTO] = "auto", - [STAC_9872_VAIO] = "vaio", +static const struct hda_model_fixup stac9872_models[] = { + { .id = STAC_9872_VAIO, .name = "vaio" }, + {} }; -static const unsigned int *stac9872_brd_tbl[STAC_9872_MODELS] = { - [STAC_9872_VAIO] = stac9872_vaio_pin_configs, +static const struct hda_fixup stac9872_fixups[] = { + [STAC_9872_VAIO] = { + .type = HDA_FIXUP_PINS, + .v.pins = stac9872_vaio_pin_configs, + }, }; -static const struct snd_pci_quirk stac9872_cfg_tbl[] = { +static const struct snd_pci_quirk stac9872_fixup_tbl[] = { SND_PCI_QUIRK_MASK(0x104d, 0xfff0, 0x81e0, "Sony VAIO F/S", STAC_9872_VAIO), {} /* terminator */ @@ -6640,25 +6649,20 @@ static int patch_stac9872(struct hda_codec *codec) spec = codec->spec; spec->linear_tone_beep = 1; - spec->board_config = snd_hda_check_board_config(codec, STAC_9872_MODELS, - stac9872_models, - stac9872_cfg_tbl); - if (spec->board_config < 0) - snd_printdd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", - codec->chip_name); - else - stac92xx_set_config_regs(codec, - stac9872_brd_tbl[spec->board_config]); + snd_hda_pick_fixup(codec, stac9872_models, stac9872_fixup_tbl, + stac9872_fixups); spec->multiout.dac_nids = spec->dac_nids; spec->num_adcs = ARRAY_SIZE(stac9872_adc_nids); spec->adc_nids = stac9872_adc_nids; spec->num_muxes = ARRAY_SIZE(stac9872_mux_nids); spec->mux_nids = stac9872_mux_nids; - spec->init = stac9872_core_init; spec->num_caps = 1; spec->capvols = stac9872_capvols; spec->capsws = stac9872_capsws; + snd_hda_add_verbs(codec, stac9872_core_init); + + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); err = stac92xx_parse_auto_config(codec); if (err < 0) { @@ -6667,6 +6671,9 @@ static int patch_stac9872(struct hda_codec *codec) } spec->input_mux = &spec->private_imux; codec->patch_ops = stac92xx_patch_ops; + + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); + return 0; } -- cgit v1.2.3 From fe6322ca66bbfa46e2810eed280fb35ef0c86fd6 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 14 Jan 2013 14:46:51 +0100 Subject: ALSA: hda - Use standard fixup table for STAC9205 Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_sigmatel.c | 237 ++++++++++++++++++++++++++--------------- 1 file changed, 154 insertions(+), 83 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 533afb698fb9..f66c7d19a7df 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -70,7 +70,6 @@ enum { }; enum { - STAC_9205_AUTO, STAC_9205_REF, STAC_9205_DELL_M42, STAC_9205_DELL_M43, @@ -2472,10 +2471,20 @@ static const struct snd_pci_quirk stac927x_cfg_tbl[] = { {} /* terminator */ }; -static const unsigned int ref9205_pin_configs[12] = { - 0x40000100, 0x40000100, 0x01016011, 0x01014010, - 0x01813122, 0x01a19021, 0x01019020, 0x40000100, - 0x90a000f0, 0x90a000f0, 0x01441030, 0x01c41030 +static const struct hda_pintbl ref9205_pin_configs[] = { + { 0x0a, 0x40000100 }, + { 0x0b, 0x40000100 }, + { 0x0c, 0x01016011 }, + { 0x0d, 0x01014010 }, + { 0x0e, 0x01813122 }, + { 0x0f, 0x01a19021 }, + { 0x14, 0x01019020 }, + { 0x16, 0x40000100 }, + { 0x17, 0x90a000f0 }, + { 0x18, 0x90a000f0 }, + { 0x21, 0x01441030 }, + { 0x22, 0x01c41030 }, + {} }; /* @@ -2489,10 +2498,20 @@ static const unsigned int ref9205_pin_configs[12] = { 10280228 (Dell Vostro 1500) 10280229 (Dell Vostro 1700) */ -static const unsigned int dell_9205_m42_pin_configs[12] = { - 0x0321101F, 0x03A11020, 0x400003FA, 0x90170310, - 0x400003FB, 0x400003FC, 0x400003FD, 0x40F000F9, - 0x90A60330, 0x400003FF, 0x0144131F, 0x40C003FE, +static const struct hda_pintbl dell_9205_m42_pin_configs[] = { + { 0x0a, 0x0321101F }, + { 0x0b, 0x03A11020 }, + { 0x0c, 0x400003FA }, + { 0x0d, 0x90170310 }, + { 0x0e, 0x400003FB }, + { 0x0f, 0x400003FC }, + { 0x14, 0x400003FD }, + { 0x16, 0x40F000F9 }, + { 0x17, 0x90A60330 }, + { 0x18, 0x400003FF }, + { 0x21, 0x0144131F }, + { 0x22, 0x40C003FE }, + {} }; /* @@ -2505,36 +2524,127 @@ static const unsigned int dell_9205_m42_pin_configs[12] = { 10280200 10280201 */ -static const unsigned int dell_9205_m43_pin_configs[12] = { - 0x0321101f, 0x03a11020, 0x90a70330, 0x90170310, - 0x400000fe, 0x400000ff, 0x400000fd, 0x40f000f9, - 0x400000fa, 0x400000fc, 0x0144131f, 0x40c003f8, +static const struct hda_pintbl dell_9205_m43_pin_configs[] = { + { 0x0a, 0x0321101f }, + { 0x0b, 0x03a11020 }, + { 0x0c, 0x90a70330 }, + { 0x0d, 0x90170310 }, + { 0x0e, 0x400000fe }, + { 0x0f, 0x400000ff }, + { 0x14, 0x400000fd }, + { 0x16, 0x40f000f9 }, + { 0x17, 0x400000fa }, + { 0x18, 0x400000fc }, + { 0x21, 0x0144131f }, + { 0x22, 0x40c003f8 }, + /* Enable SPDIF in/out */ + { 0x1f, 0x01441030 }, + { 0x20, 0x1c410030 }, + {} }; -static const unsigned int dell_9205_m44_pin_configs[12] = { - 0x0421101f, 0x04a11020, 0x400003fa, 0x90170310, - 0x400003fb, 0x400003fc, 0x400003fd, 0x400003f9, - 0x90a60330, 0x400003ff, 0x01441340, 0x40c003fe, +static const struct hda_pintbl dell_9205_m44_pin_configs[] = { + { 0x0a, 0x0421101f }, + { 0x0b, 0x04a11020 }, + { 0x0c, 0x400003fa }, + { 0x0d, 0x90170310 }, + { 0x0e, 0x400003fb }, + { 0x0f, 0x400003fc }, + { 0x14, 0x400003fd }, + { 0x16, 0x400003f9 }, + { 0x17, 0x90a60330 }, + { 0x18, 0x400003ff }, + { 0x21, 0x01441340 }, + { 0x22, 0x40c003fe }, + {} }; -static const unsigned int *stac9205_brd_tbl[STAC_9205_MODELS] = { - [STAC_9205_REF] = ref9205_pin_configs, - [STAC_9205_DELL_M42] = dell_9205_m42_pin_configs, - [STAC_9205_DELL_M43] = dell_9205_m43_pin_configs, - [STAC_9205_DELL_M44] = dell_9205_m44_pin_configs, - [STAC_9205_EAPD] = NULL, +static void stac9205_fixup_ref(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct sigmatel_spec *spec = codec->spec; + + if (action == HDA_FIXUP_ACT_PRE_PROBE) { + snd_hda_apply_pincfgs(codec, ref9205_pin_configs); + /* SPDIF-In enabled */ + spec->eapd_mask = spec->gpio_mask = spec->gpio_dir = 0; + } +} + +static int stac_add_event(struct hda_codec *codec, hda_nid_t nid, + unsigned char type, int data); + +static void stac9205_fixup_dell_m43(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct sigmatel_spec *spec = codec->spec; + int err; + + if (action == HDA_FIXUP_ACT_PRE_PROBE) { + snd_hda_apply_pincfgs(codec, dell_9205_m43_pin_configs); + + /* Enable unsol response for GPIO4/Dock HP connection */ + err = stac_add_event(codec, codec->afg, STAC_VREF_EVENT, 0x01); + if (err < 0) + return; + snd_hda_codec_write_cache(codec, codec->afg, 0, + AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK, 0x10); + snd_hda_jack_detect_enable(codec, codec->afg, 0); + + spec->gpio_dir = 0x0b; + spec->eapd_mask = 0x01; + spec->gpio_mask = 0x1b; + spec->gpio_mute = 0x10; + /* GPIO0 High = EAPD, GPIO1 Low = Headphone Mute, + * GPIO3 Low = DRM + */ + spec->gpio_data = 0x01; + } +} + +static void stac9205_fixup_eapd(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct sigmatel_spec *spec = codec->spec; + + if (action == HDA_FIXUP_ACT_PRE_PROBE) + spec->eapd_switch = 0; +} + +static const struct hda_fixup stac9205_fixups[] = { + [STAC_9205_REF] = { + .type = HDA_FIXUP_FUNC, + .v.func = stac9205_fixup_ref, + }, + [STAC_9205_DELL_M42] = { + .type = HDA_FIXUP_PINS, + .v.pins = dell_9205_m42_pin_configs, + }, + [STAC_9205_DELL_M43] = { + .type = HDA_FIXUP_FUNC, + .v.func = stac9205_fixup_dell_m43, + }, + [STAC_9205_DELL_M44] = { + .type = HDA_FIXUP_PINS, + .v.pins = dell_9205_m44_pin_configs, + }, + [STAC_9205_EAPD] = { + .type = HDA_FIXUP_FUNC, + .v.func = stac9205_fixup_eapd, + }, + {} }; -static const char * const stac9205_models[STAC_9205_MODELS] = { - [STAC_9205_AUTO] = "auto", - [STAC_9205_REF] = "ref", - [STAC_9205_DELL_M42] = "dell-m42", - [STAC_9205_DELL_M43] = "dell-m43", - [STAC_9205_DELL_M44] = "dell-m44", - [STAC_9205_EAPD] = "eapd", +static const struct hda_model_fixup stac9205_models[] = { + { .id = STAC_9205_REF, .name = "ref" }, + { .id = STAC_9205_DELL_M42, .name = "dell-m42" }, + { .id = STAC_9205_DELL_M43, .name = "dell-m43" }, + { .id = STAC_9205_DELL_M44, .name = "dell-m44" }, + { .id = STAC_9205_EAPD, .name = "eapd" }, + {} }; -static const struct snd_pci_quirk stac9205_cfg_tbl[] = { +static const struct snd_pci_quirk stac9205_fixup_tbl[] = { /* SigmaTel reference board */ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668, "DFI LanParty", STAC_9205_REF), @@ -6484,16 +6594,9 @@ static int patch_stac9205(struct hda_codec *codec) spec = codec->spec; spec->linear_tone_beep = 1; - spec->board_config = snd_hda_check_board_config(codec, STAC_9205_MODELS, - stac9205_models, - stac9205_cfg_tbl); - again: - if (spec->board_config < 0) - snd_printdd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", - codec->chip_name); - else - stac92xx_set_config_regs(codec, - stac9205_brd_tbl[spec->board_config]); + + snd_hda_pick_fixup(codec, stac9205_models, stac9205_fixup_tbl, + stac9205_fixups); spec->digbeep_nid = 0x23; spec->adc_nids = stac9205_adc_nids; @@ -6508,7 +6611,7 @@ static int patch_stac9205(struct hda_codec *codec) spec->num_dmuxes = ARRAY_SIZE(stac9205_dmux_nids); spec->num_pwrs = 0; - spec->init = stac9205_core_init; + snd_hda_add_verbs(codec, stac9205_core_init); spec->aloopback_ctl = stac9205_loopback; spec->num_caps = STAC9205_NUM_CAPS; @@ -6517,54 +6620,20 @@ static int patch_stac9205(struct hda_codec *codec) spec->aloopback_mask = 0x40; spec->aloopback_shift = 0; - /* Turn on/off EAPD per HP plugging */ - if (spec->board_config != STAC_9205_EAPD) - spec->eapd_switch = 1; spec->multiout.dac_nids = spec->dac_nids; - switch (spec->board_config){ - case STAC_9205_DELL_M43: - /* Enable SPDIF in/out */ - snd_hda_codec_set_pincfg(codec, 0x1f, 0x01441030); - snd_hda_codec_set_pincfg(codec, 0x20, 0x1c410030); + /* GPIO0 High = EAPD */ + spec->eapd_mask = spec->gpio_mask = spec->gpio_dir = 0x1; + spec->gpio_data = 0x01; - /* Enable unsol response for GPIO4/Dock HP connection */ - err = stac_add_event(codec, codec->afg, STAC_VREF_EVENT, 0x01); - if (err < 0) - return err; - snd_hda_codec_write_cache(codec, codec->afg, 0, - AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK, 0x10); - snd_hda_jack_detect_enable(codec, codec->afg, 0); + /* Turn on/off EAPD per HP plugging */ + spec->eapd_switch = 1; - spec->gpio_dir = 0x0b; - spec->eapd_mask = 0x01; - spec->gpio_mask = 0x1b; - spec->gpio_mute = 0x10; - /* GPIO0 High = EAPD, GPIO1 Low = Headphone Mute, - * GPIO3 Low = DRM - */ - spec->gpio_data = 0x01; - break; - case STAC_9205_REF: - /* SPDIF-In enabled */ - break; - default: - /* GPIO0 High = EAPD */ - spec->eapd_mask = spec->gpio_mask = spec->gpio_dir = 0x1; - spec->gpio_data = 0x01; - break; - } + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); err = stac92xx_parse_auto_config(codec); - if (!err) { - if (spec->board_config < 0) { - printk(KERN_WARNING "hda_codec: No auto-config is " - "available, default to model=ref\n"); - spec->board_config = STAC_9205_REF; - goto again; - } + if (!err) err = -EINVAL; - } if (err < 0) { stac92xx_free(codec); return err; @@ -6574,6 +6643,8 @@ static int patch_stac9205(struct hda_codec *codec) codec->proc_widget_hook = stac9205_proc_hook; + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); + return 0; } -- cgit v1.2.3 From 0a4278464eba4bf98c0d6304c62a1116553125d3 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 14 Jan 2013 15:20:13 +0100 Subject: ALSA: hda - Use standard fixup table for STAC922x Rather straightforward conversion, except for ones for Intel Mac. As Intel Mac have only unique codec SSIDs, we need to remap the fixup again for the codec SSID and call the new fixup there. Also, we can reduce model enums like STAC_MACMINI, which are model aliases for backward compatibility, since they can be pointed directly via hda_model_fixup table. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_sigmatel.c | 483 ++++++++++++++++++++++++++--------------- 1 file changed, 312 insertions(+), 171 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index f66c7d19a7df..60d43dd3e061 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -134,7 +134,6 @@ enum { }; enum { - STAC_922X_AUTO, STAC_D945_REF, STAC_D945GTP3, STAC_D945GTP5, @@ -143,21 +142,13 @@ enum { STAC_INTEL_MAC_V3, STAC_INTEL_MAC_V4, STAC_INTEL_MAC_V5, - STAC_INTEL_MAC_AUTO, /* This model is selected if no module parameter - * is given, one of the above models will be - * chosen according to the subsystem id. */ - /* for backward compatibility */ - STAC_MACMINI, - STAC_MACBOOK, - STAC_MACBOOK_PRO_V1, - STAC_MACBOOK_PRO_V2, - STAC_IMAC_INTEL, - STAC_IMAC_INTEL_20, + STAC_INTEL_MAC_AUTO, STAC_ECS_202, STAC_922X_DELL_D81, STAC_922X_DELL_D82, STAC_922X_DELL_M81, STAC_922X_DELL_M82, + STAC_922X_INTEL_MAC_GPIO, STAC_922X_MODELS }; @@ -2136,10 +2127,18 @@ static const struct snd_pci_quirk stac92hd71bxx_cfg_tbl[] = { {} /* terminator */ }; -static const unsigned int ref922x_pin_configs[10] = { - 0x01014010, 0x01016011, 0x01012012, 0x0221401f, - 0x01813122, 0x01011014, 0x01441030, 0x01c41030, - 0x40000100, 0x40000100, +static const struct hda_pintbl ref922x_pin_configs[] = { + { 0x0a, 0x01014010 }, + { 0x0b, 0x01016011 }, + { 0x0c, 0x01012012 }, + { 0x0d, 0x0221401f }, + { 0x0e, 0x01813122 }, + { 0x0f, 0x01011014 }, + { 0x10, 0x01441030 }, + { 0x11, 0x01c41030 }, + { 0x15, 0x40000100 }, + { 0x1b, 0x40000100 }, + {} }; /* @@ -2150,10 +2149,18 @@ static const unsigned int ref922x_pin_configs[10] = { 102801D1 102801D2 */ -static const unsigned int dell_922x_d81_pin_configs[10] = { - 0x02214030, 0x01a19021, 0x01111012, 0x01114010, - 0x02a19020, 0x01117011, 0x400001f0, 0x400001f1, - 0x01813122, 0x400001f2, +static const struct hda_pintbl dell_922x_d81_pin_configs[] = { + { 0x0a, 0x02214030 }, + { 0x0b, 0x01a19021 }, + { 0x0c, 0x01111012 }, + { 0x0d, 0x01114010 }, + { 0x0e, 0x02a19020 }, + { 0x0f, 0x01117011 }, + { 0x10, 0x400001f0 }, + { 0x11, 0x400001f1 }, + { 0x15, 0x01813122 }, + { 0x1b, 0x400001f2 }, + {} }; /* @@ -2161,130 +2168,311 @@ static const unsigned int dell_922x_d81_pin_configs[10] = { 102801AC 102801D0 */ -static const unsigned int dell_922x_d82_pin_configs[10] = { - 0x02214030, 0x01a19021, 0x01111012, 0x01114010, - 0x02a19020, 0x01117011, 0x01451140, 0x400001f0, - 0x01813122, 0x400001f1, +static const struct hda_pintbl dell_922x_d82_pin_configs[] = { + { 0x0a, 0x02214030 }, + { 0x0b, 0x01a19021 }, + { 0x0c, 0x01111012 }, + { 0x0d, 0x01114010 }, + { 0x0e, 0x02a19020 }, + { 0x0f, 0x01117011 }, + { 0x10, 0x01451140 }, + { 0x11, 0x400001f0 }, + { 0x15, 0x01813122 }, + { 0x1b, 0x400001f1 }, + {} }; /* STAC 922X pin configs for 102801BF */ -static const unsigned int dell_922x_m81_pin_configs[10] = { - 0x0321101f, 0x01112024, 0x01111222, 0x91174220, - 0x03a11050, 0x01116221, 0x90a70330, 0x01452340, - 0x40C003f1, 0x405003f0, +static const struct hda_pintbl dell_922x_m81_pin_configs[] = { + { 0x0a, 0x0321101f }, + { 0x0b, 0x01112024 }, + { 0x0c, 0x01111222 }, + { 0x0d, 0x91174220 }, + { 0x0e, 0x03a11050 }, + { 0x0f, 0x01116221 }, + { 0x10, 0x90a70330 }, + { 0x11, 0x01452340 }, + { 0x15, 0x40C003f1 }, + { 0x1b, 0x405003f0 }, + {} }; /* STAC 9221 A1 pin configs for 102801D7 (Dell XPS M1210) */ -static const unsigned int dell_922x_m82_pin_configs[10] = { - 0x02211211, 0x408103ff, 0x02a1123e, 0x90100310, - 0x408003f1, 0x0221121f, 0x03451340, 0x40c003f2, - 0x508003f3, 0x405003f4, +static const struct hda_pintbl dell_922x_m82_pin_configs[] = { + { 0x0a, 0x02211211 }, + { 0x0b, 0x408103ff }, + { 0x0c, 0x02a1123e }, + { 0x0d, 0x90100310 }, + { 0x0e, 0x408003f1 }, + { 0x0f, 0x0221121f }, + { 0x10, 0x03451340 }, + { 0x11, 0x40c003f2 }, + { 0x15, 0x508003f3 }, + { 0x1b, 0x405003f4 }, + {} }; -static const unsigned int d945gtp3_pin_configs[10] = { - 0x0221401f, 0x01a19022, 0x01813021, 0x01014010, - 0x40000100, 0x40000100, 0x40000100, 0x40000100, - 0x02a19120, 0x40000100, +static const struct hda_pintbl d945gtp3_pin_configs[] = { + { 0x0a, 0x0221401f }, + { 0x0b, 0x01a19022 }, + { 0x0c, 0x01813021 }, + { 0x0d, 0x01014010 }, + { 0x0e, 0x40000100 }, + { 0x0f, 0x40000100 }, + { 0x10, 0x40000100 }, + { 0x11, 0x40000100 }, + { 0x15, 0x02a19120 }, + { 0x1b, 0x40000100 }, + {} +}; + +static const struct hda_pintbl d945gtp5_pin_configs[] = { + { 0x0a, 0x0221401f }, + { 0x0b, 0x01011012 }, + { 0x0c, 0x01813024 }, + { 0x0d, 0x01014010 }, + { 0x0e, 0x01a19021 }, + { 0x0f, 0x01016011 }, + { 0x10, 0x01452130 }, + { 0x11, 0x40000100 }, + { 0x15, 0x02a19320 }, + { 0x1b, 0x40000100 }, + {} }; -static const unsigned int d945gtp5_pin_configs[10] = { - 0x0221401f, 0x01011012, 0x01813024, 0x01014010, - 0x01a19021, 0x01016011, 0x01452130, 0x40000100, - 0x02a19320, 0x40000100, +static const struct hda_pintbl intel_mac_v1_pin_configs[] = { + { 0x0a, 0x0121e21f }, + { 0x0b, 0x400000ff }, + { 0x0c, 0x9017e110 }, + { 0x0d, 0x400000fd }, + { 0x0e, 0x400000fe }, + { 0x0f, 0x0181e020 }, + { 0x10, 0x1145e030 }, + { 0x11, 0x11c5e240 }, + { 0x15, 0x400000fc }, + { 0x1b, 0x400000fb }, + {} }; -static const unsigned int intel_mac_v1_pin_configs[10] = { - 0x0121e21f, 0x400000ff, 0x9017e110, 0x400000fd, - 0x400000fe, 0x0181e020, 0x1145e030, 0x11c5e240, - 0x400000fc, 0x400000fb, +static const struct hda_pintbl intel_mac_v2_pin_configs[] = { + { 0x0a, 0x0121e21f }, + { 0x0b, 0x90a7012e }, + { 0x0c, 0x9017e110 }, + { 0x0d, 0x400000fd }, + { 0x0e, 0x400000fe }, + { 0x0f, 0x0181e020 }, + { 0x10, 0x1145e230 }, + { 0x11, 0x500000fa }, + { 0x15, 0x400000fc }, + { 0x1b, 0x400000fb }, + {} }; -static const unsigned int intel_mac_v2_pin_configs[10] = { - 0x0121e21f, 0x90a7012e, 0x9017e110, 0x400000fd, - 0x400000fe, 0x0181e020, 0x1145e230, 0x500000fa, - 0x400000fc, 0x400000fb, +static const struct hda_pintbl intel_mac_v3_pin_configs[] = { + { 0x0a, 0x0121e21f }, + { 0x0b, 0x90a7012e }, + { 0x0c, 0x9017e110 }, + { 0x0d, 0x400000fd }, + { 0x0e, 0x400000fe }, + { 0x0f, 0x0181e020 }, + { 0x10, 0x1145e230 }, + { 0x11, 0x11c5e240 }, + { 0x15, 0x400000fc }, + { 0x1b, 0x400000fb }, + {} }; -static const unsigned int intel_mac_v3_pin_configs[10] = { - 0x0121e21f, 0x90a7012e, 0x9017e110, 0x400000fd, - 0x400000fe, 0x0181e020, 0x1145e230, 0x11c5e240, - 0x400000fc, 0x400000fb, +static const struct hda_pintbl intel_mac_v4_pin_configs[] = { + { 0x0a, 0x0321e21f }, + { 0x0b, 0x03a1e02e }, + { 0x0c, 0x9017e110 }, + { 0x0d, 0x9017e11f }, + { 0x0e, 0x400000fe }, + { 0x0f, 0x0381e020 }, + { 0x10, 0x1345e230 }, + { 0x11, 0x13c5e240 }, + { 0x15, 0x400000fc }, + { 0x1b, 0x400000fb }, + {} }; -static const unsigned int intel_mac_v4_pin_configs[10] = { - 0x0321e21f, 0x03a1e02e, 0x9017e110, 0x9017e11f, - 0x400000fe, 0x0381e020, 0x1345e230, 0x13c5e240, - 0x400000fc, 0x400000fb, +static const struct hda_pintbl intel_mac_v5_pin_configs[] = { + { 0x0a, 0x0321e21f }, + { 0x0b, 0x03a1e02e }, + { 0x0c, 0x9017e110 }, + { 0x0d, 0x9017e11f }, + { 0x0e, 0x400000fe }, + { 0x0f, 0x0381e020 }, + { 0x10, 0x1345e230 }, + { 0x11, 0x13c5e240 }, + { 0x15, 0x400000fc }, + { 0x1b, 0x400000fb }, + {} }; -static const unsigned int intel_mac_v5_pin_configs[10] = { - 0x0321e21f, 0x03a1e02e, 0x9017e110, 0x9017e11f, - 0x400000fe, 0x0381e020, 0x1345e230, 0x13c5e240, - 0x400000fc, 0x400000fb, +static const struct hda_pintbl ecs202_pin_configs[] = { + { 0x0a, 0x0221401f }, + { 0x0b, 0x02a19020 }, + { 0x0c, 0x01a19020 }, + { 0x0d, 0x01114010 }, + { 0x0e, 0x408000f0 }, + { 0x0f, 0x01813022 }, + { 0x10, 0x074510a0 }, + { 0x11, 0x40c400f1 }, + { 0x15, 0x9037012e }, + { 0x1b, 0x40e000f2 }, + {} }; -static const unsigned int ecs202_pin_configs[10] = { - 0x0221401f, 0x02a19020, 0x01a19020, 0x01114010, - 0x408000f0, 0x01813022, 0x074510a0, 0x40c400f1, - 0x9037012e, 0x40e000f2, +/* codec SSIDs for Intel Mac sharing the same PCI SSID 8384:7680 */ +static const struct snd_pci_quirk stac922x_intel_mac_fixup_tbl[] = { + SND_PCI_QUIRK(0x106b, 0x0800, "Mac", STAC_INTEL_MAC_V1), + SND_PCI_QUIRK(0x106b, 0x0600, "Mac", STAC_INTEL_MAC_V2), + SND_PCI_QUIRK(0x106b, 0x0700, "Mac", STAC_INTEL_MAC_V2), + SND_PCI_QUIRK(0x106b, 0x0e00, "Mac", STAC_INTEL_MAC_V3), + SND_PCI_QUIRK(0x106b, 0x0f00, "Mac", STAC_INTEL_MAC_V3), + SND_PCI_QUIRK(0x106b, 0x1600, "Mac", STAC_INTEL_MAC_V3), + SND_PCI_QUIRK(0x106b, 0x1700, "Mac", STAC_INTEL_MAC_V3), + SND_PCI_QUIRK(0x106b, 0x0200, "Mac", STAC_INTEL_MAC_V3), + SND_PCI_QUIRK(0x106b, 0x1e00, "Mac", STAC_INTEL_MAC_V3), + SND_PCI_QUIRK(0x106b, 0x1a00, "Mac", STAC_INTEL_MAC_V4), + SND_PCI_QUIRK(0x106b, 0x0a00, "Mac", STAC_INTEL_MAC_V5), + SND_PCI_QUIRK(0x106b, 0x2200, "Mac", STAC_INTEL_MAC_V5), + {} }; -static const unsigned int *stac922x_brd_tbl[STAC_922X_MODELS] = { - [STAC_D945_REF] = ref922x_pin_configs, - [STAC_D945GTP3] = d945gtp3_pin_configs, - [STAC_D945GTP5] = d945gtp5_pin_configs, - [STAC_INTEL_MAC_V1] = intel_mac_v1_pin_configs, - [STAC_INTEL_MAC_V2] = intel_mac_v2_pin_configs, - [STAC_INTEL_MAC_V3] = intel_mac_v3_pin_configs, - [STAC_INTEL_MAC_V4] = intel_mac_v4_pin_configs, - [STAC_INTEL_MAC_V5] = intel_mac_v5_pin_configs, - [STAC_INTEL_MAC_AUTO] = intel_mac_v3_pin_configs, - /* for backward compatibility */ - [STAC_MACMINI] = intel_mac_v3_pin_configs, - [STAC_MACBOOK] = intel_mac_v5_pin_configs, - [STAC_MACBOOK_PRO_V1] = intel_mac_v3_pin_configs, - [STAC_MACBOOK_PRO_V2] = intel_mac_v3_pin_configs, - [STAC_IMAC_INTEL] = intel_mac_v2_pin_configs, - [STAC_IMAC_INTEL_20] = intel_mac_v3_pin_configs, - [STAC_ECS_202] = ecs202_pin_configs, - [STAC_922X_DELL_D81] = dell_922x_d81_pin_configs, - [STAC_922X_DELL_D82] = dell_922x_d82_pin_configs, - [STAC_922X_DELL_M81] = dell_922x_m81_pin_configs, - [STAC_922X_DELL_M82] = dell_922x_m82_pin_configs, -}; - -static const char * const stac922x_models[STAC_922X_MODELS] = { - [STAC_922X_AUTO] = "auto", - [STAC_D945_REF] = "ref", - [STAC_D945GTP5] = "5stack", - [STAC_D945GTP3] = "3stack", - [STAC_INTEL_MAC_V1] = "intel-mac-v1", - [STAC_INTEL_MAC_V2] = "intel-mac-v2", - [STAC_INTEL_MAC_V3] = "intel-mac-v3", - [STAC_INTEL_MAC_V4] = "intel-mac-v4", - [STAC_INTEL_MAC_V5] = "intel-mac-v5", - [STAC_INTEL_MAC_AUTO] = "intel-mac-auto", +static const struct hda_fixup stac922x_fixups[]; + +/* remap the fixup from codec SSID and apply it */ +static void stac922x_fixup_intel_mac_auto(struct hda_codec *codec, + const struct hda_fixup *fix, + int action) +{ + if (action != HDA_FIXUP_ACT_PRE_PROBE) + return; + snd_hda_pick_fixup(codec, NULL, stac922x_intel_mac_fixup_tbl, + stac922x_fixups); + if (codec->fixup_id != STAC_INTEL_MAC_AUTO) + snd_hda_apply_fixup(codec, action); +} + +static void stac922x_fixup_intel_mac_gpio(struct hda_codec *codec, + const struct hda_fixup *fix, + int action) +{ + struct sigmatel_spec *spec = codec->spec; + + if (action == HDA_FIXUP_ACT_PRE_PROBE) { + spec->gpio_mask = spec->gpio_dir = 0x03; + spec->gpio_data = 0x03; + } +} + +static const struct hda_fixup stac922x_fixups[] = { + [STAC_D945_REF] = { + .type = HDA_FIXUP_PINS, + .v.pins = ref922x_pin_configs, + }, + [STAC_D945GTP3] = { + .type = HDA_FIXUP_PINS, + .v.pins = d945gtp3_pin_configs, + }, + [STAC_D945GTP5] = { + .type = HDA_FIXUP_PINS, + .v.pins = d945gtp5_pin_configs, + }, + [STAC_INTEL_MAC_AUTO] = { + .type = HDA_FIXUP_FUNC, + .v.func = stac922x_fixup_intel_mac_auto, + }, + [STAC_INTEL_MAC_V1] = { + .type = HDA_FIXUP_PINS, + .v.pins = intel_mac_v1_pin_configs, + .chained = true, + .chain_id = STAC_922X_INTEL_MAC_GPIO, + }, + [STAC_INTEL_MAC_V2] = { + .type = HDA_FIXUP_PINS, + .v.pins = intel_mac_v2_pin_configs, + .chained = true, + .chain_id = STAC_922X_INTEL_MAC_GPIO, + }, + [STAC_INTEL_MAC_V3] = { + .type = HDA_FIXUP_PINS, + .v.pins = intel_mac_v3_pin_configs, + .chained = true, + .chain_id = STAC_922X_INTEL_MAC_GPIO, + }, + [STAC_INTEL_MAC_V4] = { + .type = HDA_FIXUP_PINS, + .v.pins = intel_mac_v4_pin_configs, + .chained = true, + .chain_id = STAC_922X_INTEL_MAC_GPIO, + }, + [STAC_INTEL_MAC_V5] = { + .type = HDA_FIXUP_PINS, + .v.pins = intel_mac_v5_pin_configs, + .chained = true, + .chain_id = STAC_922X_INTEL_MAC_GPIO, + }, + [STAC_922X_INTEL_MAC_GPIO] = { + .type = HDA_FIXUP_FUNC, + .v.func = stac922x_fixup_intel_mac_gpio, + }, + [STAC_ECS_202] = { + .type = HDA_FIXUP_PINS, + .v.pins = ecs202_pin_configs, + }, + [STAC_922X_DELL_D81] = { + .type = HDA_FIXUP_PINS, + .v.pins = dell_922x_d81_pin_configs, + }, + [STAC_922X_DELL_D82] = { + .type = HDA_FIXUP_PINS, + .v.pins = dell_922x_d82_pin_configs, + }, + [STAC_922X_DELL_M81] = { + .type = HDA_FIXUP_PINS, + .v.pins = dell_922x_m81_pin_configs, + }, + [STAC_922X_DELL_M82] = { + .type = HDA_FIXUP_PINS, + .v.pins = dell_922x_m82_pin_configs, + }, +}; + +static const struct hda_model_fixup stac922x_models[] = { + { .id = STAC_D945_REF, .name = "ref" }, + { .id = STAC_D945GTP5, .name = "5stack" }, + { .id = STAC_D945GTP3, .name = "3stack" }, + { .id = STAC_INTEL_MAC_V1, .name = "intel-mac-v1" }, + { .id = STAC_INTEL_MAC_V2, .name = "intel-mac-v2" }, + { .id = STAC_INTEL_MAC_V3, .name = "intel-mac-v3" }, + { .id = STAC_INTEL_MAC_V4, .name = "intel-mac-v4" }, + { .id = STAC_INTEL_MAC_V5, .name = "intel-mac-v5" }, + { .id = STAC_INTEL_MAC_AUTO, .name = "intel-mac-auto" }, + { .id = STAC_ECS_202, .name = "ecs202" }, + { .id = STAC_922X_DELL_D81, .name = "dell-d81" }, + { .id = STAC_922X_DELL_D82, .name = "dell-d82" }, + { .id = STAC_922X_DELL_M81, .name = "dell-m81" }, + { .id = STAC_922X_DELL_M82, .name = "dell-m82" }, /* for backward compatibility */ - [STAC_MACMINI] = "macmini", - [STAC_MACBOOK] = "macbook", - [STAC_MACBOOK_PRO_V1] = "macbook-pro-v1", - [STAC_MACBOOK_PRO_V2] = "macbook-pro", - [STAC_IMAC_INTEL] = "imac-intel", - [STAC_IMAC_INTEL_20] = "imac-intel-20", - [STAC_ECS_202] = "ecs202", - [STAC_922X_DELL_D81] = "dell-d81", - [STAC_922X_DELL_D82] = "dell-d82", - [STAC_922X_DELL_M81] = "dell-m81", - [STAC_922X_DELL_M82] = "dell-m82", -}; - -static const struct snd_pci_quirk stac922x_cfg_tbl[] = { + { .id = STAC_INTEL_MAC_V3, .name = "macmini" }, + { .id = STAC_INTEL_MAC_V5, .name = "macbook" }, + { .id = STAC_INTEL_MAC_V3, .name = "macbook-pro-v1" }, + { .id = STAC_INTEL_MAC_V3, .name = "macbook-pro" }, + { .id = STAC_INTEL_MAC_V2, .name = "imac-intel" }, + { .id = STAC_INTEL_MAC_V3, .name = "imac-intel-20" }, + {} +}; + +static const struct snd_pci_quirk stac922x_fixup_tbl[] = { /* SigmaTel reference board */ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668, "DFI LanParty", STAC_D945_REF), @@ -2347,9 +2535,10 @@ static const struct snd_pci_quirk stac922x_cfg_tbl[] = { SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0204, "Intel D945", STAC_D945_REF), /* other systems */ + /* Apple Intel Mac (Mac Mini, MacBook, MacBook Pro...) */ - SND_PCI_QUIRK(0x8384, 0x7680, - "Mac", STAC_INTEL_MAC_AUTO), + SND_PCI_QUIRK(0x8384, 0x7680, "Mac", STAC_INTEL_MAC_AUTO), + /* Dell systems */ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01a7, "unknown Dell", STAC_922X_DELL_D81), @@ -6357,54 +6546,9 @@ static int patch_stac922x(struct hda_codec *codec) spec = codec->spec; spec->linear_tone_beep = 1; - spec->board_config = snd_hda_check_board_config(codec, STAC_922X_MODELS, - stac922x_models, - stac922x_cfg_tbl); - if (spec->board_config == STAC_INTEL_MAC_AUTO) { - spec->gpio_mask = spec->gpio_dir = 0x03; - spec->gpio_data = 0x03; - /* Intel Macs have all same PCI SSID, so we need to check - * codec SSID to distinguish the exact models - */ - printk(KERN_INFO "hda_codec: STAC922x, Apple subsys_id=%x\n", codec->subsystem_id); - switch (codec->subsystem_id) { - case 0x106b0800: - spec->board_config = STAC_INTEL_MAC_V1; - break; - case 0x106b0600: - case 0x106b0700: - spec->board_config = STAC_INTEL_MAC_V2; - break; - case 0x106b0e00: - case 0x106b0f00: - case 0x106b1600: - case 0x106b1700: - case 0x106b0200: - case 0x106b1e00: - spec->board_config = STAC_INTEL_MAC_V3; - break; - case 0x106b1a00: - case 0x00000100: - spec->board_config = STAC_INTEL_MAC_V4; - break; - case 0x106b0a00: - case 0x106b2200: - spec->board_config = STAC_INTEL_MAC_V5; - break; - default: - spec->board_config = STAC_INTEL_MAC_V3; - break; - } - } - - again: - if (spec->board_config < 0) - snd_printdd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", - codec->chip_name); - else - stac92xx_set_config_regs(codec, - stac922x_brd_tbl[spec->board_config]); + snd_hda_pick_fixup(codec, stac922x_models, stac922x_fixup_tbl, + stac922x_fixups); spec->adc_nids = stac922x_adc_nids; spec->mux_nids = stac922x_mux_nids; @@ -6413,24 +6557,19 @@ static int patch_stac922x(struct hda_codec *codec) spec->num_dmics = 0; spec->num_pwrs = 0; - spec->init = stac922x_core_init; - spec->num_caps = STAC922X_NUM_CAPS; spec->capvols = stac922x_capvols; spec->capsws = stac922x_capsws; spec->multiout.dac_nids = spec->dac_nids; + snd_hda_add_verbs(codec, stac922x_core_init); + + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); + err = stac92xx_parse_auto_config(codec); - if (!err) { - if (spec->board_config < 0) { - printk(KERN_WARNING "hda_codec: No auto-config is " - "available, default to model=ref\n"); - spec->board_config = STAC_D945_REF; - goto again; - } + if (!err) err = -EINVAL; - } if (err < 0) { stac92xx_free(codec); return err; @@ -6445,6 +6584,8 @@ static int patch_stac922x(struct hda_codec *codec) (0x27 << AC_AMPCAP_STEP_SIZE_SHIFT) | (0 << AC_AMPCAP_MUTE_SHIFT)); + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); + return 0; } -- cgit v1.2.3 From 29ac83635fcf244a7178f9fc8f918cd7ddf799d5 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 14 Jan 2013 16:03:38 +0100 Subject: ALSA: hda - Use standard fixup table for STAC927x This conversion is a bit tricky. Since STAC927x may take two different volume-knob initialization values depending on the model, a new flag, spec->volknob_init, is introduced to indicate whether it's the standard volume-knob initialization or not. Also, Dell BIOS model is now directly mapped onto the fixup table instead of parsing in the function. This resulted in a new model ref, STAC_927X_DELL_BIOS_SPDIF, which is a chained entry. Also, for reducing the fixups, virtual entries like STAC_927X_DELL_DMIC and STAC_D965_VERBS are introduced. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_sigmatel.c | 355 +++++++++++++++++++++++++++-------------- 1 file changed, 235 insertions(+), 120 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 60d43dd3e061..796dfd1ca235 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -153,14 +153,16 @@ enum { }; enum { - STAC_927X_AUTO, STAC_D965_REF_NO_JD, /* no jack-detection */ STAC_D965_REF, STAC_D965_3ST, STAC_D965_5ST, STAC_D965_5ST_NO_FP, + STAC_D965_VERBS, STAC_DELL_3ST, STAC_DELL_BIOS, + STAC_DELL_BIOS_SPDIF, + STAC_927X_DELL_DMIC, STAC_927X_VOLKNOB, STAC_927X_MODELS }; @@ -194,6 +196,7 @@ struct sigmatel_spec { unsigned int auto_mic:1; unsigned int linear_tone_beep:1; unsigned int headset_jack:1; /* 4-pin headset jack (hp + mono mic) */ + unsigned int volknob_init:1; /* special volume-knob initialization */ /* gpio lines */ unsigned int eapd_mask; @@ -930,8 +933,6 @@ static const struct hda_verb stac922x_core_init[] = { }; static const struct hda_verb d965_core_init[] = { - /* set master volume and direct control */ - { 0x24, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff}, /* unmute node 0x1b */ { 0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000}, /* select node 0x03 as DAC */ @@ -2564,65 +2565,235 @@ static const struct snd_pci_quirk stac922x_fixup_tbl[] = { {} /* terminator */ }; -static const unsigned int ref927x_pin_configs[14] = { - 0x02214020, 0x02a19080, 0x0181304e, 0x01014010, - 0x01a19040, 0x01011012, 0x01016011, 0x0101201f, - 0x183301f0, 0x18a001f0, 0x18a001f0, 0x01442070, - 0x01c42190, 0x40000100, +static const struct hda_pintbl ref927x_pin_configs[] = { + { 0x0a, 0x02214020 }, + { 0x0b, 0x02a19080 }, + { 0x0c, 0x0181304e }, + { 0x0d, 0x01014010 }, + { 0x0e, 0x01a19040 }, + { 0x0f, 0x01011012 }, + { 0x10, 0x01016011 }, + { 0x11, 0x0101201f }, + { 0x12, 0x183301f0 }, + { 0x13, 0x18a001f0 }, + { 0x14, 0x18a001f0 }, + { 0x21, 0x01442070 }, + { 0x22, 0x01c42190 }, + { 0x23, 0x40000100 }, + {} }; -static const unsigned int d965_3st_pin_configs[14] = { - 0x0221401f, 0x02a19120, 0x40000100, 0x01014011, - 0x01a19021, 0x01813024, 0x40000100, 0x40000100, - 0x40000100, 0x40000100, 0x40000100, 0x40000100, - 0x40000100, 0x40000100 +static const struct hda_pintbl d965_3st_pin_configs[] = { + { 0x0a, 0x0221401f }, + { 0x0b, 0x02a19120 }, + { 0x0c, 0x40000100 }, + { 0x0d, 0x01014011 }, + { 0x0e, 0x01a19021 }, + { 0x0f, 0x01813024 }, + { 0x10, 0x40000100 }, + { 0x11, 0x40000100 }, + { 0x12, 0x40000100 }, + { 0x13, 0x40000100 }, + { 0x14, 0x40000100 }, + { 0x21, 0x40000100 }, + { 0x22, 0x40000100 }, + { 0x23, 0x40000100 }, + {} }; -static const unsigned int d965_5st_pin_configs[14] = { - 0x02214020, 0x02a19080, 0x0181304e, 0x01014010, - 0x01a19040, 0x01011012, 0x01016011, 0x40000100, - 0x40000100, 0x40000100, 0x40000100, 0x01442070, - 0x40000100, 0x40000100 +static const struct hda_pintbl d965_5st_pin_configs[] = { + { 0x0a, 0x02214020 }, + { 0x0b, 0x02a19080 }, + { 0x0c, 0x0181304e }, + { 0x0d, 0x01014010 }, + { 0x0e, 0x01a19040 }, + { 0x0f, 0x01011012 }, + { 0x10, 0x01016011 }, + { 0x11, 0x40000100 }, + { 0x12, 0x40000100 }, + { 0x13, 0x40000100 }, + { 0x14, 0x40000100 }, + { 0x21, 0x01442070 }, + { 0x22, 0x40000100 }, + { 0x23, 0x40000100 }, + {} }; -static const unsigned int d965_5st_no_fp_pin_configs[14] = { - 0x40000100, 0x40000100, 0x0181304e, 0x01014010, - 0x01a19040, 0x01011012, 0x01016011, 0x40000100, - 0x40000100, 0x40000100, 0x40000100, 0x01442070, - 0x40000100, 0x40000100 +static const struct hda_pintbl d965_5st_no_fp_pin_configs[] = { + { 0x0a, 0x40000100 }, + { 0x0b, 0x40000100 }, + { 0x0c, 0x0181304e }, + { 0x0d, 0x01014010 }, + { 0x0e, 0x01a19040 }, + { 0x0f, 0x01011012 }, + { 0x10, 0x01016011 }, + { 0x11, 0x40000100 }, + { 0x12, 0x40000100 }, + { 0x13, 0x40000100 }, + { 0x14, 0x40000100 }, + { 0x21, 0x01442070 }, + { 0x22, 0x40000100 }, + { 0x23, 0x40000100 }, + {} }; -static const unsigned int dell_3st_pin_configs[14] = { - 0x02211230, 0x02a11220, 0x01a19040, 0x01114210, - 0x01111212, 0x01116211, 0x01813050, 0x01112214, - 0x403003fa, 0x90a60040, 0x90a60040, 0x404003fb, - 0x40c003fc, 0x40000100 +static const struct hda_pintbl dell_3st_pin_configs[] = { + { 0x0a, 0x02211230 }, + { 0x0b, 0x02a11220 }, + { 0x0c, 0x01a19040 }, + { 0x0d, 0x01114210 }, + { 0x0e, 0x01111212 }, + { 0x0f, 0x01116211 }, + { 0x10, 0x01813050 }, + { 0x11, 0x01112214 }, + { 0x12, 0x403003fa }, + { 0x13, 0x90a60040 }, + { 0x14, 0x90a60040 }, + { 0x21, 0x404003fb }, + { 0x22, 0x40c003fc }, + { 0x23, 0x40000100 }, + {} }; -static const unsigned int *stac927x_brd_tbl[STAC_927X_MODELS] = { - [STAC_D965_REF_NO_JD] = ref927x_pin_configs, - [STAC_D965_REF] = ref927x_pin_configs, - [STAC_D965_3ST] = d965_3st_pin_configs, - [STAC_D965_5ST] = d965_5st_pin_configs, - [STAC_D965_5ST_NO_FP] = d965_5st_no_fp_pin_configs, - [STAC_DELL_3ST] = dell_3st_pin_configs, - [STAC_DELL_BIOS] = NULL, - [STAC_927X_VOLKNOB] = NULL, +static void stac927x_fixup_ref_no_jd(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct sigmatel_spec *spec = codec->spec; + + /* no jack detecion for ref-no-jd model */ + if (action == HDA_FIXUP_ACT_PROBE) + spec->hp_detect = 0; +} + +static void stac927x_fixup_ref(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct sigmatel_spec *spec = codec->spec; + + if (action == HDA_FIXUP_ACT_PRE_PROBE) { + snd_hda_apply_pincfgs(codec, ref927x_pin_configs); + spec->eapd_mask = spec->gpio_mask = 0; + spec->gpio_dir = spec->gpio_data = 0; + } +} + +static void stac927x_fixup_dell_dmic(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct sigmatel_spec *spec = codec->spec; + + if (action != HDA_FIXUP_ACT_PRE_PROBE) + return; + + if (codec->subsystem_id != 0x1028022f) { + /* GPIO2 High = Enable EAPD */ + spec->eapd_mask = spec->gpio_mask = 0x04; + spec->gpio_dir = spec->gpio_data = 0x04; + } + spec->dmic_nids = stac927x_dmic_nids; + spec->num_dmics = STAC927X_NUM_DMICS; + + snd_hda_add_verbs(codec, dell_3st_core_init); + spec->volknob_init = 1; + spec->dmux_nids = stac927x_dmux_nids; + spec->num_dmuxes = ARRAY_SIZE(stac927x_dmux_nids); +} + +static void stac927x_fixup_volknob(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct sigmatel_spec *spec = codec->spec; + + if (action == HDA_FIXUP_ACT_PRE_PROBE) { + snd_hda_add_verbs(codec, stac927x_volknob_core_init); + spec->volknob_init = 1; + } +} + +static const struct hda_fixup stac927x_fixups[] = { + [STAC_D965_REF_NO_JD] = { + .type = HDA_FIXUP_FUNC, + .v.func = stac927x_fixup_ref_no_jd, + .chained = true, + .chain_id = STAC_D965_REF, + }, + [STAC_D965_REF] = { + .type = HDA_FIXUP_FUNC, + .v.func = stac927x_fixup_ref, + }, + [STAC_D965_3ST] = { + .type = HDA_FIXUP_PINS, + .v.pins = d965_3st_pin_configs, + .chained = true, + .chain_id = STAC_D965_VERBS, + }, + [STAC_D965_5ST] = { + .type = HDA_FIXUP_PINS, + .v.pins = d965_5st_pin_configs, + .chained = true, + .chain_id = STAC_D965_VERBS, + }, + [STAC_D965_VERBS] = { + .type = HDA_FIXUP_VERBS, + .v.verbs = d965_core_init, + }, + [STAC_D965_5ST_NO_FP] = { + .type = HDA_FIXUP_PINS, + .v.pins = d965_5st_no_fp_pin_configs, + }, + [STAC_DELL_3ST] = { + .type = HDA_FIXUP_PINS, + .v.pins = dell_3st_pin_configs, + .chained = true, + .chain_id = STAC_927X_DELL_DMIC, + }, + [STAC_DELL_BIOS] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + /* configure the analog microphone on some laptops */ + { 0x0c, 0x90a79130 }, + /* correct the front output jack as a hp out */ + { 0x0f, 0x0227011f }, + /* correct the front input jack as a mic */ + { 0x0e, 0x02a79130 }, + {} + }, + .chained = true, + .chain_id = STAC_927X_DELL_DMIC, + }, + [STAC_DELL_BIOS_SPDIF] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + /* correct the device field to SPDIF out */ + { 0x21, 0x01442070 }, + {} + }, + .chained = true, + .chain_id = STAC_DELL_BIOS, + }, + [STAC_927X_DELL_DMIC] = { + .type = HDA_FIXUP_FUNC, + .v.func = stac927x_fixup_dell_dmic, + }, + [STAC_927X_VOLKNOB] = { + .type = HDA_FIXUP_FUNC, + .v.func = stac927x_fixup_volknob, + }, }; -static const char * const stac927x_models[STAC_927X_MODELS] = { - [STAC_927X_AUTO] = "auto", - [STAC_D965_REF_NO_JD] = "ref-no-jd", - [STAC_D965_REF] = "ref", - [STAC_D965_3ST] = "3stack", - [STAC_D965_5ST] = "5stack", - [STAC_D965_5ST_NO_FP] = "5stack-no-fp", - [STAC_DELL_3ST] = "dell-3stack", - [STAC_DELL_BIOS] = "dell-bios", - [STAC_927X_VOLKNOB] = "volknob", +static const struct hda_model_fixup stac927x_models[] = { + { .id = STAC_D965_REF_NO_JD, .name = "ref-no-jd" }, + { .id = STAC_D965_REF, .name = "ref" }, + { .id = STAC_D965_3ST, .name = "3stack" }, + { .id = STAC_D965_5ST, .name = "5stack" }, + { .id = STAC_D965_5ST_NO_FP, .name = "5stack-no-fp" }, + { .id = STAC_DELL_3ST, .name = "dell-3stack" }, + { .id = STAC_DELL_BIOS, .name = "dell-bios" }, + { .id = STAC_927X_VOLKNOB, .name = "volknob" }, + {} }; -static const struct snd_pci_quirk stac927x_cfg_tbl[] = { +static const struct snd_pci_quirk stac927x_fixup_tbl[] = { /* SigmaTel reference board */ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668, "DFI LanParty", STAC_D965_REF), @@ -2644,12 +2815,12 @@ static const struct snd_pci_quirk stac927x_cfg_tbl[] = { SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f3, "Dell Inspiron 1420", STAC_DELL_BIOS), SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f7, "Dell XPS M1730", STAC_DELL_BIOS), SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0227, "Dell Vostro 1400 ", STAC_DELL_BIOS), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x022e, "Dell ", STAC_DELL_BIOS), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x022e, "Dell ", STAC_DELL_BIOS_SPDIF), SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x022f, "Dell Inspiron 1525", STAC_DELL_BIOS), SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0242, "Dell ", STAC_DELL_BIOS), SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0243, "Dell ", STAC_DELL_BIOS), SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x02ff, "Dell ", STAC_DELL_BIOS), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0209, "Dell XPS 1330", STAC_DELL_BIOS), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0209, "Dell XPS 1330", STAC_DELL_BIOS_SPDIF), /* 965 based 5 stack systems */ SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_INTEL, 0xff00, 0x2300, "Intel D965", STAC_D965_5ST), @@ -6602,16 +6773,9 @@ static int patch_stac927x(struct hda_codec *codec) spec = codec->spec; spec->linear_tone_beep = 1; codec->slave_dig_outs = stac927x_slave_dig_outs; - spec->board_config = snd_hda_check_board_config(codec, STAC_927X_MODELS, - stac927x_models, - stac927x_cfg_tbl); - again: - if (spec->board_config < 0) - snd_printdd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", - codec->chip_name); - else - stac92xx_set_config_regs(codec, - stac927x_brd_tbl[spec->board_config]); + + snd_hda_pick_fixup(codec, stac927x_models, stac927x_fixup_tbl, + stac927x_fixups); spec->digbeep_nid = 0x23; spec->adc_nids = stac927x_adc_nids; @@ -6624,56 +6788,11 @@ static int patch_stac927x(struct hda_codec *codec) spec->dac_list = stac927x_dac_nids; spec->multiout.dac_nids = spec->dac_nids; - if (spec->board_config != STAC_D965_REF) { - /* GPIO0 High = Enable EAPD */ - spec->eapd_mask = spec->gpio_mask = 0x01; - spec->gpio_dir = spec->gpio_data = 0x01; - } + /* GPIO0 High = Enable EAPD */ + spec->eapd_mask = spec->gpio_mask = 0x01; + spec->gpio_dir = spec->gpio_data = 0x01; - switch (spec->board_config) { - case STAC_D965_3ST: - case STAC_D965_5ST: - /* GPIO0 High = Enable EAPD */ - spec->num_dmics = 0; - spec->init = d965_core_init; - break; - case STAC_DELL_BIOS: - switch (codec->subsystem_id) { - case 0x10280209: - case 0x1028022e: - /* correct the device field to SPDIF out */ - snd_hda_codec_set_pincfg(codec, 0x21, 0x01442070); - break; - } - /* configure the analog microphone on some laptops */ - snd_hda_codec_set_pincfg(codec, 0x0c, 0x90a79130); - /* correct the front output jack as a hp out */ - snd_hda_codec_set_pincfg(codec, 0x0f, 0x0227011f); - /* correct the front input jack as a mic */ - snd_hda_codec_set_pincfg(codec, 0x0e, 0x02a79130); - /* fallthru */ - case STAC_DELL_3ST: - if (codec->subsystem_id != 0x1028022f) { - /* GPIO2 High = Enable EAPD */ - spec->eapd_mask = spec->gpio_mask = 0x04; - spec->gpio_dir = spec->gpio_data = 0x04; - } - spec->dmic_nids = stac927x_dmic_nids; - spec->num_dmics = STAC927X_NUM_DMICS; - - spec->init = dell_3st_core_init; - spec->dmux_nids = stac927x_dmux_nids; - spec->num_dmuxes = ARRAY_SIZE(stac927x_dmux_nids); - break; - case STAC_927X_VOLKNOB: - spec->num_dmics = 0; - spec->init = stac927x_volknob_core_init; - break; - default: - spec->num_dmics = 0; - spec->init = stac927x_core_init; - break; - } + spec->num_dmics = 0; spec->num_caps = STAC927X_NUM_CAPS; spec->capvols = stac927x_capvols; @@ -6685,16 +6804,14 @@ static int patch_stac927x(struct hda_codec *codec) spec->aloopback_shift = 0; spec->eapd_switch = 1; + if (!spec->volknob_init) + snd_hda_add_verbs(codec, stac927x_core_init); + + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); + err = stac92xx_parse_auto_config(codec); - if (!err) { - if (spec->board_config < 0) { - printk(KERN_WARNING "hda_codec: No auto-config is " - "available, default to model=ref\n"); - spec->board_config = STAC_D965_REF; - goto again; - } + if (!err) err = -EINVAL; - } if (err < 0) { stac92xx_free(codec); return err; @@ -6716,9 +6833,7 @@ static int patch_stac927x(struct hda_codec *codec) */ codec->bus->needs_damn_long_delay = 1; - /* no jack detecion for ref-no-jd model */ - if (spec->board_config == STAC_D965_REF_NO_JD) - spec->hp_detect = 0; + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); return 0; } -- cgit v1.2.3 From 52fd5cbc9bef6a2e20bfbdae771498ef97c67b34 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 15 Jan 2013 08:45:33 +0100 Subject: ALSA: hda - Check pincap while parsing the configuration Sometimes (or rather often) BIOS sets the pin default configurations obviously wrongly. Looking through these failures, one common pattern is to enable some dead pins that are usually marked as speaker pins. In such a case, we can skip them if the pins don't have the output capability. In this patch, add a check for the valid pin cap bit for each parsed pin, and filter out when it's invalid. The fix was originally suggested by Raymond Yau. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_auto_parser.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_auto_parser.c b/sound/pci/hda/hda_auto_parser.c index 33b3ece224c6..a4810c7437bd 100644 --- a/sound/pci/hda/hda_auto_parser.c +++ b/sound/pci/hda/hda_auto_parser.c @@ -97,6 +97,28 @@ static void reorder_outputs(unsigned int nums, hda_nid_t *pins) } } +/* check whether the given pin has a proper pin I/O capability bit */ +static bool check_pincap_validity(struct hda_codec *codec, hda_nid_t pin, + unsigned int dev) +{ + unsigned int pincap = snd_hda_query_pin_caps(codec, pin); + + /* some old hardware don't return the proper pincaps */ + if (!pincap) + return true; + + switch (dev) { + case AC_JACK_LINE_OUT: + case AC_JACK_SPEAKER: + case AC_JACK_HP_OUT: + case AC_JACK_SPDIF_OUT: + case AC_JACK_DIG_OTHER_OUT: + return !!(pincap & AC_PINCAP_OUT); + default: + return !!(pincap & AC_PINCAP_IN); + } +} + /* * Parse all pin widgets and store the useful pin nids to cfg * @@ -164,6 +186,9 @@ int snd_hda_parse_pin_defcfg(struct hda_codec *codec, dev = AC_JACK_SPEAKER; } + if (!check_pincap_validity(codec, nid, dev)) + continue; + switch (dev) { case AC_JACK_LINE_OUT: seq = get_defcfg_sequence(def_conf); -- cgit v1.2.3 From 0f6fcb73c02759085dbf5cf5a0afb5f0e9f832e5 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 14 Jan 2013 17:05:25 +0100 Subject: ALSA: hda - Use standard fixup table for IDT92HD71Bxx This time, the only intrusive changes are for HP machines. As the mute LED fixup and the bass speaker switch are required only for HP machines, we can move these checks into the fixup entries; the former is applied generically to all HP machines while the latter for only certain models. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_sigmatel.c | 481 ++++++++++++++++++++++++++--------------- 1 file changed, 312 insertions(+), 169 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 796dfd1ca235..4a2594a7f02c 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -108,7 +108,6 @@ enum { }; enum { - STAC_92HD71BXX_AUTO, STAC_92HD71BXX_REF, STAC_DELL_M4_1, STAC_DELL_M4_2, @@ -118,6 +117,9 @@ enum { STAC_HP_DV5, STAC_HP_HDX, STAC_HP_DV4_1222NR, + STAC_92HD71BXX_HP, + STAC_92HD71BXX_NO_DMIC, + STAC_92HD71BXX_NO_SMUX, STAC_92HD71BXX_MODELS }; @@ -589,6 +591,14 @@ static const hda_nid_t stac9205_pin_nids[12] = { 0x21, 0x22, }; +static int stac_add_event(struct hda_codec *codec, hda_nid_t nid, + unsigned char type, int data); +static int stac_add_hp_bass_switch(struct hda_codec *codec); +static void stac92xx_auto_set_pinctl(struct hda_codec *codec, + hda_nid_t nid, int pin_type); +static int hp_blike_system(u32 subsystem_id); +static int find_mute_led_cfg(struct hda_codec *codec, int default_polarity); + static int stac92xx_dmux_enum_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { @@ -2018,60 +2028,305 @@ static const struct snd_pci_quirk stac92hd83xxx_codec_id_cfg_tbl[] = { {} /* terminator */ }; -static const unsigned int ref92hd71bxx_pin_configs[STAC92HD71BXX_NUM_PINS] = { - 0x02214030, 0x02a19040, 0x01a19020, 0x01014010, - 0x0181302e, 0x01014010, 0x01019020, 0x90a000f0, - 0x90a000f0, 0x01452050, 0x01452050, 0x00000000, - 0x00000000 +static const struct hda_pintbl ref92hd71bxx_pin_configs[] = { + { 0x0a, 0x02214030 }, + { 0x0b, 0x02a19040 }, + { 0x0c, 0x01a19020 }, + { 0x0d, 0x01014010 }, + { 0x0e, 0x0181302e }, + { 0x0f, 0x01014010 }, + { 0x14, 0x01019020 }, + { 0x18, 0x90a000f0 }, + { 0x19, 0x90a000f0 }, + { 0x1e, 0x01452050 }, + { 0x1f, 0x01452050 }, + {} }; -static const unsigned int dell_m4_1_pin_configs[STAC92HD71BXX_NUM_PINS] = { - 0x0421101f, 0x04a11221, 0x40f000f0, 0x90170110, - 0x23a1902e, 0x23014250, 0x40f000f0, 0x90a000f0, - 0x40f000f0, 0x4f0000f0, 0x4f0000f0, 0x00000000, - 0x00000000 +static const struct hda_pintbl dell_m4_1_pin_configs[] = { + { 0x0a, 0x0421101f }, + { 0x0b, 0x04a11221 }, + { 0x0c, 0x40f000f0 }, + { 0x0d, 0x90170110 }, + { 0x0e, 0x23a1902e }, + { 0x0f, 0x23014250 }, + { 0x14, 0x40f000f0 }, + { 0x18, 0x90a000f0 }, + { 0x19, 0x40f000f0 }, + { 0x1e, 0x4f0000f0 }, + { 0x1f, 0x4f0000f0 }, + {} }; -static const unsigned int dell_m4_2_pin_configs[STAC92HD71BXX_NUM_PINS] = { - 0x0421101f, 0x04a11221, 0x90a70330, 0x90170110, - 0x23a1902e, 0x23014250, 0x40f000f0, 0x40f000f0, - 0x40f000f0, 0x044413b0, 0x044413b0, 0x00000000, - 0x00000000 +static const struct hda_pintbl dell_m4_2_pin_configs[] = { + { 0x0a, 0x0421101f }, + { 0x0b, 0x04a11221 }, + { 0x0c, 0x90a70330 }, + { 0x0d, 0x90170110 }, + { 0x0e, 0x23a1902e }, + { 0x0f, 0x23014250 }, + { 0x14, 0x40f000f0 }, + { 0x18, 0x40f000f0 }, + { 0x19, 0x40f000f0 }, + { 0x1e, 0x044413b0 }, + { 0x1f, 0x044413b0 }, + {} }; -static const unsigned int dell_m4_3_pin_configs[STAC92HD71BXX_NUM_PINS] = { - 0x0421101f, 0x04a11221, 0x90a70330, 0x90170110, - 0x40f000f0, 0x40f000f0, 0x40f000f0, 0x90a000f0, - 0x40f000f0, 0x044413b0, 0x044413b0, 0x00000000, - 0x00000000 +static const struct hda_pintbl dell_m4_3_pin_configs[] = { + { 0x0a, 0x0421101f }, + { 0x0b, 0x04a11221 }, + { 0x0c, 0x90a70330 }, + { 0x0d, 0x90170110 }, + { 0x0e, 0x40f000f0 }, + { 0x0f, 0x40f000f0 }, + { 0x14, 0x40f000f0 }, + { 0x18, 0x90a000f0 }, + { 0x19, 0x40f000f0 }, + { 0x1e, 0x044413b0 }, + { 0x1f, 0x044413b0 }, + {} }; -static const unsigned int *stac92hd71bxx_brd_tbl[STAC_92HD71BXX_MODELS] = { - [STAC_92HD71BXX_REF] = ref92hd71bxx_pin_configs, - [STAC_DELL_M4_1] = dell_m4_1_pin_configs, - [STAC_DELL_M4_2] = dell_m4_2_pin_configs, - [STAC_DELL_M4_3] = dell_m4_3_pin_configs, - [STAC_HP_M4] = NULL, - [STAC_HP_DV4] = NULL, - [STAC_HP_DV5] = NULL, - [STAC_HP_HDX] = NULL, - [STAC_HP_DV4_1222NR] = NULL, +static void stac92hd71bxx_fixup_ref(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct sigmatel_spec *spec = codec->spec; + + if (action != HDA_FIXUP_ACT_PRE_PROBE) + return; + + snd_hda_apply_pincfgs(codec, ref92hd71bxx_pin_configs); + spec->gpio_mask = spec->gpio_dir = spec->gpio_data = 0; +} + +static void stac92hd71bxx_fixup_no_dmic(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct sigmatel_spec *spec = codec->spec; + + if (action != HDA_FIXUP_ACT_PRE_PROBE) + return; + spec->num_dmics = 0; + spec->num_smuxes = 0; + spec->num_dmuxes = 0; +} + +static void stac92hd71bxx_fixup_no_smux(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct sigmatel_spec *spec = codec->spec; + + if (action != HDA_FIXUP_ACT_PRE_PROBE) + return; + spec->num_dmics = 1; + spec->num_smuxes = 0; + spec->num_dmuxes = 1; +} + +static void stac92hd71bxx_fixup_hp_m4(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct sigmatel_spec *spec = codec->spec; + + if (action != HDA_FIXUP_ACT_PRE_PROBE) + return; + + /* Enable VREF power saving on GPIO1 detect */ + stac_add_event(codec, codec->afg, STAC_VREF_EVENT, 0x02); + snd_hda_codec_write_cache(codec, codec->afg, 0, + AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK, 0x02); + snd_hda_jack_detect_enable(codec, codec->afg, 0); + spec->gpio_mask |= 0x02; + + /* enable internal microphone */ + snd_hda_codec_set_pincfg(codec, 0x0e, 0x01813040); + stac92xx_auto_set_pinctl(codec, 0x0e, + AC_PINCTL_IN_EN | AC_PINCTL_VREF_80); + + stac92hd71bxx_fixup_no_dmic(codec, fix, action); +} + +static void stac92hd71bxx_fixup_hp_dv4_1222nr(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct sigmatel_spec *spec = codec->spec; + + if (action != HDA_FIXUP_ACT_PRE_PROBE) + return; + spec->num_dmics = 1; + /* I don't know if it needs 1 or 2 smuxes - will wait for + * bug reports to fix if needed + */ + spec->num_smuxes = 1; + spec->num_dmuxes = 1; +} + +static void stac92hd71bxx_fixup_hp_dv4(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct sigmatel_spec *spec = codec->spec; + + if (action != HDA_FIXUP_ACT_PRE_PROBE) + return; + spec->gpio_led = 0x01; +} + +static void stac92hd71bxx_fixup_hp_dv5(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct sigmatel_spec *spec = codec->spec; + unsigned int cap; + + switch (action) { + case HDA_FIXUP_ACT_PRE_PROBE: + snd_hda_codec_set_pincfg(codec, 0x0d, 0x90170010); + stac92xx_auto_set_pinctl(codec, 0x0d, AC_PINCTL_OUT_EN); + /* HP dv6 gives the headphone pin as a line-out. Thus we + * need to set hp_detect flag here to force to enable HP + * detection. + */ + spec->hp_detect = 1; + break; + + case HDA_FIXUP_ACT_PROBE: + /* enable bass on HP dv7 */ + cap = snd_hda_param_read(codec, 0x1, AC_PAR_GPIO_CAP); + cap &= AC_GPIO_IO_COUNT; + if (cap >= 6) + stac_add_hp_bass_switch(codec); + break; + } +} + +static void stac92hd71bxx_fixup_hp_hdx(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct sigmatel_spec *spec = codec->spec; + + if (action != HDA_FIXUP_ACT_PRE_PROBE) + return; + spec->gpio_led = 0x08; + spec->num_dmics = 1; + spec->num_smuxes = 1; + spec->num_dmuxes = 1; +} + + +static void stac92hd71bxx_fixup_hp(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct sigmatel_spec *spec = codec->spec; + + if (action != HDA_FIXUP_ACT_PRE_PROBE) + return; + + if (hp_blike_system(codec->subsystem_id)) { + unsigned int pin_cfg = snd_hda_codec_get_pincfg(codec, 0x0f); + if (get_defcfg_device(pin_cfg) == AC_JACK_LINE_OUT || + get_defcfg_device(pin_cfg) == AC_JACK_SPEAKER || + get_defcfg_device(pin_cfg) == AC_JACK_HP_OUT) { + /* It was changed in the BIOS to just satisfy MS DTM. + * Lets turn it back into slaved HP + */ + pin_cfg = (pin_cfg & (~AC_DEFCFG_DEVICE)) + | (AC_JACK_HP_OUT << + AC_DEFCFG_DEVICE_SHIFT); + pin_cfg = (pin_cfg & (~(AC_DEFCFG_DEF_ASSOC + | AC_DEFCFG_SEQUENCE))) + | 0x1f; + snd_hda_codec_set_pincfg(codec, 0x0f, pin_cfg); + } + } + + if (find_mute_led_cfg(codec, 1)) + snd_printd("mute LED gpio %d polarity %d\n", + spec->gpio_led, + spec->gpio_led_polarity); + +} + +static const struct hda_fixup stac92hd71bxx_fixups[] = { + [STAC_92HD71BXX_REF] = { + .type = HDA_FIXUP_FUNC, + .v.func = stac92hd71bxx_fixup_ref, + }, + [STAC_DELL_M4_1] = { + .type = HDA_FIXUP_PINS, + .v.pins = dell_m4_1_pin_configs, + .chained = true, + .chain_id = STAC_92HD71BXX_NO_SMUX, + }, + [STAC_DELL_M4_2] = { + .type = HDA_FIXUP_PINS, + .v.pins = dell_m4_2_pin_configs, + .chained = true, + .chain_id = STAC_92HD71BXX_NO_DMIC, + }, + [STAC_DELL_M4_3] = { + .type = HDA_FIXUP_PINS, + .v.pins = dell_m4_3_pin_configs, + .chained = true, + .chain_id = STAC_92HD71BXX_NO_SMUX, + }, + [STAC_HP_M4] = { + .type = HDA_FIXUP_FUNC, + .v.func = stac92hd71bxx_fixup_hp_m4, + .chained = true, + .chain_id = STAC_92HD71BXX_HP, + }, + [STAC_HP_DV4] = { + .type = HDA_FIXUP_FUNC, + .v.func = stac92hd71bxx_fixup_hp_dv4, + .chained = true, + .chain_id = STAC_HP_DV5, + }, + [STAC_HP_DV5] = { + .type = HDA_FIXUP_FUNC, + .v.func = stac92hd71bxx_fixup_hp_dv5, + .chained = true, + .chain_id = STAC_92HD71BXX_HP, + }, + [STAC_HP_HDX] = { + .type = HDA_FIXUP_FUNC, + .v.func = stac92hd71bxx_fixup_hp_hdx, + .chained = true, + .chain_id = STAC_92HD71BXX_HP, + }, + [STAC_HP_DV4_1222NR] = { + .type = HDA_FIXUP_FUNC, + .v.func = stac92hd71bxx_fixup_hp_dv4_1222nr, + .chained = true, + .chain_id = STAC_HP_DV4, + }, + [STAC_92HD71BXX_NO_DMIC] = { + .type = HDA_FIXUP_FUNC, + .v.func = stac92hd71bxx_fixup_no_dmic, + }, + [STAC_92HD71BXX_NO_SMUX] = { + .type = HDA_FIXUP_FUNC, + .v.func = stac92hd71bxx_fixup_no_smux, + }, + [STAC_92HD71BXX_HP] = { + .type = HDA_FIXUP_FUNC, + .v.func = stac92hd71bxx_fixup_hp, + }, }; -static const char * const stac92hd71bxx_models[STAC_92HD71BXX_MODELS] = { - [STAC_92HD71BXX_AUTO] = "auto", - [STAC_92HD71BXX_REF] = "ref", - [STAC_DELL_M4_1] = "dell-m4-1", - [STAC_DELL_M4_2] = "dell-m4-2", - [STAC_DELL_M4_3] = "dell-m4-3", - [STAC_HP_M4] = "hp-m4", - [STAC_HP_DV4] = "hp-dv4", - [STAC_HP_DV5] = "hp-dv5", - [STAC_HP_HDX] = "hp-hdx", - [STAC_HP_DV4_1222NR] = "hp-dv4-1222nr", +static const struct hda_model_fixup stac92hd71bxx_models[] = { + { .id = STAC_92HD71BXX_REF, .name = "ref" }, + { .id = STAC_DELL_M4_1, .name = "dell-m4-1" }, + { .id = STAC_DELL_M4_2, .name = "dell-m4-2" }, + { .id = STAC_DELL_M4_3, .name = "dell-m4-3" }, + { .id = STAC_HP_M4, .name = "hp-m4" }, + { .id = STAC_HP_DV4, .name = "hp-dv4" }, + { .id = STAC_HP_DV5, .name = "hp-dv5" }, + { .id = STAC_HP_HDX, .name = "hp-hdx" }, + { .id = STAC_HP_DV4_1222NR, .name = "hp-dv4-1222nr" }, + {} }; -static const struct snd_pci_quirk stac92hd71bxx_cfg_tbl[] = { +static const struct snd_pci_quirk stac92hd71bxx_fixup_tbl[] = { /* SigmaTel reference board */ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668, "DFI LanParty", STAC_92HD71BXX_REF), @@ -2101,6 +2356,7 @@ static const struct snd_pci_quirk stac92hd71bxx_cfg_tbl[] = { "HP DV6", STAC_HP_DV5), SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xfff0, 0x7010, "HP", STAC_HP_DV5), + SND_PCI_QUIRK_VENDOR(PCI_VENDOR_ID_HP, "HP", STAC_92HD71BXX_HP), SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0233, "unknown Dell", STAC_DELL_M4_1), SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0234, @@ -2931,9 +3187,6 @@ static void stac9205_fixup_ref(struct hda_codec *codec, } } -static int stac_add_event(struct hda_codec *codec, hda_nid_t nid, - unsigned char type, int data); - static void stac9205_fixup_dell_m43(struct hda_codec *codec, const struct hda_fixup *fix, int action) { @@ -5615,8 +5868,6 @@ static void stac_issue_unsol_event(struct hda_codec *codec, hda_nid_t nid) handle_unsol_event(codec, event); } -static int hp_blike_system(u32 subsystem_id); - static void set_hp_led_gpio(struct hda_codec *codec) { struct sigmatel_spec *spec = codec->spec; @@ -5663,6 +5914,7 @@ static int find_mute_led_cfg(struct hda_codec *codec, int default_polarity) &spec->gpio_led_polarity); return 1; } + if ((codec->subsystem_id >> 16) == PCI_VENDOR_ID_HP) { while ((dev = dmi_find_device(DMI_DEV_TYPE_OEM_STRING, NULL, dev))) { @@ -6466,7 +6718,6 @@ static int patch_stac92hd71bxx(struct hda_codec *codec) { struct sigmatel_spec *spec; const struct hda_verb *unmute_init = stac92hd71bxx_unmute_core_init; - unsigned int pin_cfg; int err; err = alloc_stac_spec(codec, STAC92HD71BXX_NUM_PINS, @@ -6490,24 +6741,14 @@ static int patch_stac92hd71bxx(struct hda_codec *codec) spec->pin_nids = stac92hd71bxx_pin_nids_6port; } spec->num_pwrs = ARRAY_SIZE(stac92hd71bxx_pwr_nids); - spec->board_config = snd_hda_check_board_config(codec, - STAC_92HD71BXX_MODELS, - stac92hd71bxx_models, - stac92hd71bxx_cfg_tbl); -again: - if (spec->board_config < 0) - snd_printdd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", - codec->chip_name); - else - stac92xx_set_config_regs(codec, - stac92hd71bxx_brd_tbl[spec->board_config]); - if (spec->board_config != STAC_92HD71BXX_REF) { - /* GPIO0 = EAPD */ - spec->gpio_mask = 0x01; - spec->gpio_dir = 0x01; - spec->gpio_data = 0x01; - } + snd_hda_pick_fixup(codec, stac92hd71bxx_models, stac92hd71bxx_fixup_tbl, + stac92hd71bxx_fixups); + + /* GPIO0 = EAPD */ + spec->gpio_mask = 0x01; + spec->gpio_dir = 0x01; + spec->gpio_data = 0x01; spec->dmic_nids = stac92hd71bxx_dmic_nids; spec->dmux_nids = stac92hd71bxx_dmux_nids; @@ -6529,19 +6770,6 @@ again: STAC92HD71BXX_NUM_DMICS); break; case 0x111d7608: /* 5 Port with Analog Mixer */ - switch (spec->board_config) { - case STAC_HP_M4: - /* Enable VREF power saving on GPIO1 detect */ - err = stac_add_event(codec, codec->afg, - STAC_VREF_EVENT, 0x02); - if (err < 0) - return err; - snd_hda_codec_write_cache(codec, codec->afg, 0, - AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK, 0x02); - snd_hda_jack_detect_enable(codec, codec->afg, 0); - spec->gpio_mask |= 0x02; - break; - } if ((codec->revision_id & 0xf) == 0 || (codec->revision_id & 0xf) == 1) spec->stream_delay = 40; /* 40 milliseconds */ @@ -6569,7 +6797,7 @@ again: } if (get_wcaps_type(get_wcaps(codec, 0x28)) == AC_WID_VOL_KNB) - spec->init = stac92hd71bxx_core_init; + snd_hda_add_verbs(codec, stac92hd71bxx_core_init); if (get_wcaps(codec, 0xa) & AC_WCAP_IN_AMP) snd_hda_sequence_write_cache(codec, unmute_init); @@ -6590,76 +6818,7 @@ again: spec->num_dmuxes = ARRAY_SIZE(stac92hd71bxx_dmux_nids); spec->num_smuxes = stac92hd71bxx_connected_smuxes(codec, 0x1e); - snd_printdd("Found board config: %d\n", spec->board_config); - - switch (spec->board_config) { - case STAC_HP_M4: - /* enable internal microphone */ - snd_hda_codec_set_pincfg(codec, 0x0e, 0x01813040); - stac92xx_auto_set_pinctl(codec, 0x0e, - AC_PINCTL_IN_EN | AC_PINCTL_VREF_80); - /* fallthru */ - case STAC_DELL_M4_2: - spec->num_dmics = 0; - spec->num_smuxes = 0; - spec->num_dmuxes = 0; - break; - case STAC_DELL_M4_1: - case STAC_DELL_M4_3: - spec->num_dmics = 1; - spec->num_smuxes = 0; - spec->num_dmuxes = 1; - break; - case STAC_HP_DV4_1222NR: - spec->num_dmics = 1; - /* I don't know if it needs 1 or 2 smuxes - will wait for - * bug reports to fix if needed - */ - spec->num_smuxes = 1; - spec->num_dmuxes = 1; - /* fallthrough */ - case STAC_HP_DV4: - spec->gpio_led = 0x01; - /* fallthrough */ - case STAC_HP_DV5: - snd_hda_codec_set_pincfg(codec, 0x0d, 0x90170010); - stac92xx_auto_set_pinctl(codec, 0x0d, AC_PINCTL_OUT_EN); - /* HP dv6 gives the headphone pin as a line-out. Thus we - * need to set hp_detect flag here to force to enable HP - * detection. - */ - spec->hp_detect = 1; - break; - case STAC_HP_HDX: - spec->num_dmics = 1; - spec->num_dmuxes = 1; - spec->num_smuxes = 1; - spec->gpio_led = 0x08; - break; - } - - if (hp_blike_system(codec->subsystem_id)) { - pin_cfg = snd_hda_codec_get_pincfg(codec, 0x0f); - if (get_defcfg_device(pin_cfg) == AC_JACK_LINE_OUT || - get_defcfg_device(pin_cfg) == AC_JACK_SPEAKER || - get_defcfg_device(pin_cfg) == AC_JACK_HP_OUT) { - /* It was changed in the BIOS to just satisfy MS DTM. - * Lets turn it back into slaved HP - */ - pin_cfg = (pin_cfg & (~AC_DEFCFG_DEVICE)) - | (AC_JACK_HP_OUT << - AC_DEFCFG_DEVICE_SHIFT); - pin_cfg = (pin_cfg & (~(AC_DEFCFG_DEF_ASSOC - | AC_DEFCFG_SEQUENCE))) - | 0x1f; - snd_hda_codec_set_pincfg(codec, 0x0f, pin_cfg); - } - } - - if (find_mute_led_cfg(codec, 1)) - snd_printd("mute LED gpio %d polarity %d\n", - spec->gpio_led, - spec->gpio_led_polarity); + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); if (spec->gpio_led) { if (!spec->vref_mute_led_nid) { @@ -6675,33 +6834,17 @@ again: spec->multiout.dac_nids = spec->dac_nids; err = stac92xx_parse_auto_config(codec); - if (!err) { - if (spec->board_config < 0) { - printk(KERN_WARNING "hda_codec: No auto-config is " - "available, default to model=ref\n"); - spec->board_config = STAC_92HD71BXX_REF; - goto again; - } + if (!err) err = -EINVAL; - } - if (err < 0) { stac92xx_free(codec); return err; } - /* enable bass on HP dv7 */ - if (spec->board_config == STAC_HP_DV4 || - spec->board_config == STAC_HP_DV5) { - unsigned int cap; - cap = snd_hda_param_read(codec, 0x1, AC_PAR_GPIO_CAP); - cap &= AC_GPIO_IO_COUNT; - if (cap >= 6) - stac_add_hp_bass_switch(codec); - } - codec->proc_widget_hook = stac92hd7x_proc_hook; + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); + return 0; } -- cgit v1.2.3 From 55e30141d8be9f21f35e5c4999f7043e07347511 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 14 Jan 2013 17:30:04 +0100 Subject: ALSA: hda - Use standard fixup table for IDT92HD73xx This one is rather a simple conversion. The fixups for Dell machines are implemented by fixup functions in the end. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_sigmatel.c | 331 +++++++++++++++++++++++++++-------------- 1 file changed, 218 insertions(+), 113 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 4a2594a7f02c..3cb44c1b395a 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -79,7 +79,6 @@ enum { }; enum { - STAC_92HD73XX_AUTO, STAC_92HD73XX_NO_JD, /* no jack-detection */ STAC_92HD73XX_REF, STAC_92HD73XX_INTEL, @@ -1783,55 +1782,217 @@ static const struct snd_pci_quirk stac925x_fixup_tbl[] = { {} /* terminator */ }; -static const unsigned int ref92hd73xx_pin_configs[13] = { - 0x02214030, 0x02a19040, 0x01a19020, 0x02214030, - 0x0181302e, 0x01014010, 0x01014020, 0x01014030, - 0x02319040, 0x90a000f0, 0x90a000f0, 0x01452050, - 0x01452050, +static const struct hda_pintbl ref92hd73xx_pin_configs[] = { + { 0x0a, 0x02214030 }, + { 0x0b, 0x02a19040 }, + { 0x0c, 0x01a19020 }, + { 0x0d, 0x02214030 }, + { 0x0e, 0x0181302e }, + { 0x0f, 0x01014010 }, + { 0x10, 0x01014020 }, + { 0x11, 0x01014030 }, + { 0x12, 0x02319040 }, + { 0x13, 0x90a000f0 }, + { 0x14, 0x90a000f0 }, + { 0x22, 0x01452050 }, + { 0x23, 0x01452050 }, + {} }; -static const unsigned int dell_m6_pin_configs[13] = { - 0x0321101f, 0x4f00000f, 0x4f0000f0, 0x90170110, - 0x03a11020, 0x0321101f, 0x4f0000f0, 0x4f0000f0, - 0x4f0000f0, 0x90a60160, 0x4f0000f0, 0x4f0000f0, - 0x4f0000f0, +static const struct hda_pintbl dell_m6_pin_configs[] = { + { 0x0a, 0x0321101f }, + { 0x0b, 0x4f00000f }, + { 0x0c, 0x4f0000f0 }, + { 0x0d, 0x90170110 }, + { 0x0e, 0x03a11020 }, + { 0x0f, 0x0321101f }, + { 0x10, 0x4f0000f0 }, + { 0x11, 0x4f0000f0 }, + { 0x12, 0x4f0000f0 }, + { 0x13, 0x90a60160 }, + { 0x14, 0x4f0000f0 }, + { 0x22, 0x4f0000f0 }, + { 0x23, 0x4f0000f0 }, + {} }; -static const unsigned int alienware_m17x_pin_configs[13] = { - 0x0321101f, 0x0321101f, 0x03a11020, 0x03014020, - 0x90170110, 0x4f0000f0, 0x4f0000f0, 0x4f0000f0, - 0x4f0000f0, 0x90a60160, 0x4f0000f0, 0x4f0000f0, - 0x904601b0, +static const struct hda_pintbl alienware_m17x_pin_configs[] = { + { 0x0a, 0x0321101f }, + { 0x0b, 0x0321101f }, + { 0x0c, 0x03a11020 }, + { 0x0d, 0x03014020 }, + { 0x0e, 0x90170110 }, + { 0x0f, 0x4f0000f0 }, + { 0x10, 0x4f0000f0 }, + { 0x11, 0x4f0000f0 }, + { 0x12, 0x4f0000f0 }, + { 0x13, 0x90a60160 }, + { 0x14, 0x4f0000f0 }, + { 0x22, 0x4f0000f0 }, + { 0x23, 0x904601b0 }, + {} }; -static const unsigned int intel_dg45id_pin_configs[13] = { - 0x02214230, 0x02A19240, 0x01013214, 0x01014210, - 0x01A19250, 0x01011212, 0x01016211 +static const struct hda_pintbl intel_dg45id_pin_configs[] = { + { 0x0a, 0x02214230 }, + { 0x0b, 0x02A19240 }, + { 0x0c, 0x01013214 }, + { 0x0d, 0x01014210 }, + { 0x0e, 0x01A19250 }, + { 0x0f, 0x01011212 }, + { 0x10, 0x01016211 }, + {} }; -static const unsigned int *stac92hd73xx_brd_tbl[STAC_92HD73XX_MODELS] = { - [STAC_92HD73XX_REF] = ref92hd73xx_pin_configs, - [STAC_DELL_M6_AMIC] = dell_m6_pin_configs, - [STAC_DELL_M6_DMIC] = dell_m6_pin_configs, - [STAC_DELL_M6_BOTH] = dell_m6_pin_configs, - [STAC_DELL_EQ] = dell_m6_pin_configs, - [STAC_ALIENWARE_M17X] = alienware_m17x_pin_configs, - [STAC_92HD73XX_INTEL] = intel_dg45id_pin_configs, +static void stac92hd73xx_fixup_ref(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct sigmatel_spec *spec = codec->spec; + + if (action != HDA_FIXUP_ACT_PRE_PROBE) + return; + + snd_hda_apply_pincfgs(codec, ref92hd73xx_pin_configs); + spec->gpio_mask = spec->gpio_dir = spec->gpio_data = 0; +} + +static void stac92hd73xx_fixup_dell(struct hda_codec *codec) +{ + struct sigmatel_spec *spec = codec->spec; + + snd_hda_apply_pincfgs(codec, dell_m6_pin_configs); + spec->num_smuxes = 0; + spec->eapd_switch = 0; +} + +static void stac92hd73xx_fixup_dell_eq(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct sigmatel_spec *spec = codec->spec; + + if (action != HDA_FIXUP_ACT_PRE_PROBE) + return; + + stac92hd73xx_fixup_dell(codec); + snd_hda_add_verbs(codec, dell_eq_core_init); + spec->volknob_init = 1; +} + +/* Analog Mics */ +static void stac92hd73xx_fixup_dell_m6_amic(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct sigmatel_spec *spec = codec->spec; + + if (action != HDA_FIXUP_ACT_PRE_PROBE) + return; + + stac92hd73xx_fixup_dell(codec); + snd_hda_codec_set_pincfg(codec, 0x0b, 0x90A70170); + spec->num_dmics = 0; +} + +/* Digital Mics */ +static void stac92hd73xx_fixup_dell_m6_dmic(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct sigmatel_spec *spec = codec->spec; + + if (action != HDA_FIXUP_ACT_PRE_PROBE) + return; + + stac92hd73xx_fixup_dell(codec); + snd_hda_codec_set_pincfg(codec, 0x13, 0x90A60160); + spec->num_dmics = 1; +} + +/* Both */ +static void stac92hd73xx_fixup_dell_m6_both(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct sigmatel_spec *spec = codec->spec; + + if (action != HDA_FIXUP_ACT_PRE_PROBE) + return; + + stac92hd73xx_fixup_dell(codec); + snd_hda_codec_set_pincfg(codec, 0x0b, 0x90A70170); + snd_hda_codec_set_pincfg(codec, 0x13, 0x90A60160); + spec->num_dmics = 1; +} + +static void stac92hd73xx_fixup_alienware_m17x(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct sigmatel_spec *spec = codec->spec; + + if (action != HDA_FIXUP_ACT_PRE_PROBE) + return; + + snd_hda_apply_pincfgs(codec, alienware_m17x_pin_configs); + spec->num_dmics = STAC92HD73XX_NUM_DMICS; + spec->num_smuxes = ARRAY_SIZE(stac92hd73xx_smux_nids); + spec->eapd_switch = 0; +} + +static void stac92hd73xx_fixup_no_jd(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct sigmatel_spec *spec = codec->spec; + + if (action != HDA_FIXUP_ACT_PRE_PROBE) + return; + spec->hp_detect = 0; +} + +static const struct hda_fixup stac92hd73xx_fixups[] = { + [STAC_92HD73XX_REF] = { + .type = HDA_FIXUP_FUNC, + .v.func = stac92hd73xx_fixup_ref, + }, + [STAC_DELL_M6_AMIC] = { + .type = HDA_FIXUP_FUNC, + .v.func = stac92hd73xx_fixup_dell_m6_amic, + }, + [STAC_DELL_M6_DMIC] = { + .type = HDA_FIXUP_FUNC, + .v.func = stac92hd73xx_fixup_dell_m6_dmic, + }, + [STAC_DELL_M6_BOTH] = { + .type = HDA_FIXUP_FUNC, + .v.func = stac92hd73xx_fixup_dell_m6_both, + }, + [STAC_DELL_EQ] = { + .type = HDA_FIXUP_FUNC, + .v.func = stac92hd73xx_fixup_dell_eq, + }, + [STAC_ALIENWARE_M17X] = { + .type = HDA_FIXUP_FUNC, + .v.func = stac92hd73xx_fixup_alienware_m17x, + }, + [STAC_92HD73XX_INTEL] = { + .type = HDA_FIXUP_PINS, + .v.pins = intel_dg45id_pin_configs, + }, + [STAC_92HD73XX_NO_JD] = { + .type = HDA_FIXUP_FUNC, + .v.func = stac92hd73xx_fixup_no_jd, + } }; -static const char * const stac92hd73xx_models[STAC_92HD73XX_MODELS] = { - [STAC_92HD73XX_AUTO] = "auto", - [STAC_92HD73XX_NO_JD] = "no-jd", - [STAC_92HD73XX_REF] = "ref", - [STAC_92HD73XX_INTEL] = "intel", - [STAC_DELL_M6_AMIC] = "dell-m6-amic", - [STAC_DELL_M6_DMIC] = "dell-m6-dmic", - [STAC_DELL_M6_BOTH] = "dell-m6", - [STAC_DELL_EQ] = "dell-eq", - [STAC_ALIENWARE_M17X] = "alienware", +static const struct hda_model_fixup stac92hd73xx_models[] = { + { .id = STAC_92HD73XX_NO_JD, .name = "no-jd" }, + { .id = STAC_92HD73XX_REF, .name = "ref" }, + { .id = STAC_92HD73XX_INTEL, .name = "intel" }, + { .id = STAC_DELL_M6_AMIC, .name = "dell-m6-amic" }, + { .id = STAC_DELL_M6_DMIC, .name = "dell-m6-dmic" }, + { .id = STAC_DELL_M6_BOTH, .name = "dell-m6" }, + { .id = STAC_DELL_EQ, .name = "dell-eq" }, + { .id = STAC_ALIENWARE_M17X, .name = "alienware" }, + {} }; -static const struct snd_pci_quirk stac92hd73xx_cfg_tbl[] = { +static const struct snd_pci_quirk stac92hd73xx_fixup_tbl[] = { /* SigmaTel reference board */ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668, "DFI LanParty", STAC_92HD73XX_REF), @@ -1869,10 +2030,7 @@ static const struct snd_pci_quirk stac92hd73xx_cfg_tbl[] = { "Dell Studio XPS 1645", STAC_DELL_M6_DMIC), SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0413, "Dell Studio 1558", STAC_DELL_M6_DMIC), - {} /* terminator */ -}; - -static const struct snd_pci_quirk stac92hd73xx_codec_id_cfg_tbl[] = { + /* codec SSID matching */ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x02a1, "Alienware M17x", STAC_ALIENWARE_M17X), SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x043a, @@ -6262,23 +6420,9 @@ static int patch_stac92hd73xx(struct hda_codec *codec) spec = codec->spec; spec->linear_tone_beep = 0; codec->slave_dig_outs = stac92hd73xx_slave_dig_outs; - spec->board_config = snd_hda_check_board_config(codec, - STAC_92HD73XX_MODELS, - stac92hd73xx_models, - stac92hd73xx_cfg_tbl); - /* check codec subsystem id if not found */ - if (spec->board_config < 0) - spec->board_config = - snd_hda_check_board_codec_sid_config(codec, - STAC_92HD73XX_MODELS, stac92hd73xx_models, - stac92hd73xx_codec_id_cfg_tbl); -again: - if (spec->board_config < 0) - snd_printdd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", - codec->chip_name); - else - stac92xx_set_config_regs(codec, - stac92hd73xx_brd_tbl[spec->board_config]); + + snd_hda_pick_fixup(codec, stac92hd73xx_models, stac92hd73xx_fixup_tbl, + stac92hd73xx_fixups); num_dacs = snd_hda_get_connections(codec, 0x0a, conn, STAC92HD73_DAC_COUNT + 2) - 1; @@ -6288,7 +6432,7 @@ again: "number of channels defaulting to DAC count\n"); num_dacs = STAC92HD73_DAC_COUNT; } - spec->init = stac92hd73xx_core_init; + switch (num_dacs) { case 0x3: /* 6 Channel */ spec->aloopback_ctl = stac92hd73xx_6ch_loopback; @@ -6320,76 +6464,37 @@ again: spec->capvols = stac92hd73xx_capvols; spec->capsws = stac92hd73xx_capsws; - switch (spec->board_config) { - case STAC_DELL_EQ: - spec->init = dell_eq_core_init; - /* fallthru */ - case STAC_DELL_M6_AMIC: - case STAC_DELL_M6_DMIC: - case STAC_DELL_M6_BOTH: - spec->num_smuxes = 0; - spec->eapd_switch = 0; + /* GPIO0 High = Enable EAPD */ + spec->eapd_mask = spec->gpio_mask = spec->gpio_dir = 0x1; + spec->gpio_data = 0x01; - switch (spec->board_config) { - case STAC_DELL_M6_AMIC: /* Analog Mics */ - snd_hda_codec_set_pincfg(codec, 0x0b, 0x90A70170); - spec->num_dmics = 0; - break; - case STAC_DELL_M6_DMIC: /* Digital Mics */ - snd_hda_codec_set_pincfg(codec, 0x13, 0x90A60160); - spec->num_dmics = 1; - break; - case STAC_DELL_M6_BOTH: /* Both */ - snd_hda_codec_set_pincfg(codec, 0x0b, 0x90A70170); - snd_hda_codec_set_pincfg(codec, 0x13, 0x90A60160); - spec->num_dmics = 1; - break; - } - break; - case STAC_ALIENWARE_M17X: - spec->num_dmics = STAC92HD73XX_NUM_DMICS; - spec->num_smuxes = ARRAY_SIZE(stac92hd73xx_smux_nids); - spec->eapd_switch = 0; - break; - default: - spec->num_dmics = STAC92HD73XX_NUM_DMICS; - spec->num_smuxes = ARRAY_SIZE(stac92hd73xx_smux_nids); - spec->eapd_switch = 1; - break; - } - if (spec->board_config != STAC_92HD73XX_REF) { - /* GPIO0 High = Enable EAPD */ - spec->eapd_mask = spec->gpio_mask = spec->gpio_dir = 0x1; - spec->gpio_data = 0x01; - } + spec->num_dmics = STAC92HD73XX_NUM_DMICS; + spec->num_smuxes = ARRAY_SIZE(stac92hd73xx_smux_nids); + spec->eapd_switch = 1; spec->num_pwrs = ARRAY_SIZE(stac92hd73xx_pwr_nids); spec->pwr_nids = stac92hd73xx_pwr_nids; + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); + + if (!spec->volknob_init) + snd_hda_add_verbs(codec, stac92hd73xx_core_init); + err = stac92xx_parse_auto_config(codec); - if (!err) { - if (spec->board_config < 0) { - printk(KERN_WARNING "hda_codec: No auto-config is " - "available, default to model=ref\n"); - spec->board_config = STAC_92HD73XX_REF; - goto again; - } + if (!err) err = -EINVAL; - } - if (err < 0) { stac92xx_free(codec); return err; } - if (spec->board_config == STAC_92HD73XX_NO_JD) - spec->hp_detect = 0; - codec->patch_ops = stac92xx_patch_ops; codec->proc_widget_hook = stac92hd7x_proc_hook; + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); + return 0; } -- cgit v1.2.3 From 372f8c75025673713ed94f976f9bde8a744d2e47 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 14 Jan 2013 18:06:34 +0100 Subject: ALSA: hda - Use standard fixup table for IDT92HD83xxx Finally all codecs in patch_sigmatel.c have been converted to use the standard fixup helpers. This change also includes trivial cleanups like the call of common setup for GPIO LED or the removal of unused function. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_sigmatel.c | 398 +++++++++++++++++++++++++---------------- 1 file changed, 248 insertions(+), 150 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 3cb44c1b395a..fa16ff79866c 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -91,7 +91,6 @@ enum { }; enum { - STAC_92HD83XXX_AUTO, STAC_92HD83XXX_REF, STAC_92HD83XXX_PWR_REF, STAC_DELL_S14, @@ -103,6 +102,7 @@ enum { STAC_92HD83XXX_HP_INV_LED, STAC_92HD83XXX_HP_MIC_LED, STAC_92HD83XXX_HEADSET_JACK, + STAC_92HD83XXX_HP, STAC_92HD83XXX_MODELS }; @@ -209,6 +209,7 @@ struct sigmatel_spec { unsigned int gpio_led_polarity; unsigned int vref_mute_led_nid; /* pin NID for mute-LED vref control */ unsigned int vref_led; + int default_polarity; unsigned int mic_mute_led_gpio; /* capture mute LED GPIO */ bool mic_mute_led_on; /* current mic mute state */ @@ -595,6 +596,7 @@ static int stac_add_event(struct hda_codec *codec, hda_nid_t nid, static int stac_add_hp_bass_switch(struct hda_codec *codec); static void stac92xx_auto_set_pinctl(struct hda_codec *codec, hda_nid_t nid, int pin_type); +static int hp_bnb2011_with_dock(struct hda_codec *codec); static int hp_blike_system(u32 subsystem_id); static int find_mute_led_cfg(struct hda_codec *codec, int default_polarity); @@ -2040,68 +2042,232 @@ static const struct snd_pci_quirk stac92hd73xx_fixup_tbl[] = { {} /* terminator */ }; -static const unsigned int ref92hd83xxx_pin_configs[10] = { - 0x02214030, 0x02211010, 0x02a19020, 0x02170130, - 0x01014050, 0x01819040, 0x01014020, 0x90a3014e, - 0x01451160, 0x98560170, +static const struct hda_pintbl ref92hd83xxx_pin_configs[] = { + { 0x0a, 0x02214030 }, + { 0x0b, 0x02211010 }, + { 0x0c, 0x02a19020 }, + { 0x0d, 0x02170130 }, + { 0x0e, 0x01014050 }, + { 0x0f, 0x01819040 }, + { 0x10, 0x01014020 }, + { 0x11, 0x90a3014e }, + { 0x1f, 0x01451160 }, + { 0x20, 0x98560170 }, + {} }; -static const unsigned int dell_s14_pin_configs[10] = { - 0x0221403f, 0x0221101f, 0x02a19020, 0x90170110, - 0x40f000f0, 0x40f000f0, 0x40f000f0, 0x90a60160, - 0x40f000f0, 0x40f000f0, +static const struct hda_pintbl dell_s14_pin_configs[] = { + { 0x0a, 0x0221403f }, + { 0x0b, 0x0221101f }, + { 0x0c, 0x02a19020 }, + { 0x0d, 0x90170110 }, + { 0x0e, 0x40f000f0 }, + { 0x0f, 0x40f000f0 }, + { 0x10, 0x40f000f0 }, + { 0x11, 0x90a60160 }, + { 0x1f, 0x40f000f0 }, + { 0x20, 0x40f000f0 }, + {} }; -static const unsigned int dell_vostro_3500_pin_configs[10] = { - 0x02a11020, 0x0221101f, 0x400000f0, 0x90170110, - 0x400000f1, 0x400000f2, 0x400000f3, 0x90a60160, - 0x400000f4, 0x400000f5, +static const struct hda_pintbl dell_vostro_3500_pin_configs[] = { + { 0x0a, 0x02a11020 }, + { 0x0b, 0x0221101f }, + { 0x0c, 0x400000f0 }, + { 0x0d, 0x90170110 }, + { 0x0e, 0x400000f1 }, + { 0x0f, 0x400000f2 }, + { 0x10, 0x400000f3 }, + { 0x11, 0x90a60160 }, + { 0x1f, 0x400000f4 }, + { 0x20, 0x400000f5 }, + {} }; -static const unsigned int hp_dv7_4000_pin_configs[10] = { - 0x03a12050, 0x0321201f, 0x40f000f0, 0x90170110, - 0x40f000f0, 0x40f000f0, 0x90170110, 0xd5a30140, - 0x40f000f0, 0x40f000f0, +static const struct hda_pintbl hp_dv7_4000_pin_configs[] = { + { 0x0a, 0x03a12050 }, + { 0x0b, 0x0321201f }, + { 0x0c, 0x40f000f0 }, + { 0x0d, 0x90170110 }, + { 0x0e, 0x40f000f0 }, + { 0x0f, 0x40f000f0 }, + { 0x10, 0x90170110 }, + { 0x11, 0xd5a30140 }, + { 0x1f, 0x40f000f0 }, + { 0x20, 0x40f000f0 }, + {} }; -static const unsigned int hp_zephyr_pin_configs[10] = { - 0x01813050, 0x0421201f, 0x04a1205e, 0x96130310, - 0x96130310, 0x0101401f, 0x1111611f, 0xd5a30130, - 0, 0, +static const struct hda_pintbl hp_zephyr_pin_configs[] = { + { 0x0a, 0x01813050 }, + { 0x0b, 0x0421201f }, + { 0x0c, 0x04a1205e }, + { 0x0d, 0x96130310 }, + { 0x0e, 0x96130310 }, + { 0x0f, 0x0101401f }, + { 0x10, 0x1111611f }, + { 0x11, 0xd5a30130 }, + {} }; -static const unsigned int hp_cNB11_intquad_pin_configs[10] = { - 0x40f000f0, 0x0221101f, 0x02a11020, 0x92170110, - 0x40f000f0, 0x92170110, 0x40f000f0, 0xd5a30130, - 0x40f000f0, 0x40f000f0, +static const struct hda_pintbl hp_cNB11_intquad_pin_configs[] = { + { 0x0a, 0x40f000f0 }, + { 0x0b, 0x0221101f }, + { 0x0c, 0x02a11020 }, + { 0x0d, 0x92170110 }, + { 0x0e, 0x40f000f0 }, + { 0x0f, 0x92170110 }, + { 0x10, 0x40f000f0 }, + { 0x11, 0xd5a30130 }, + { 0x1f, 0x40f000f0 }, + { 0x20, 0x40f000f0 }, + {} }; -static const unsigned int *stac92hd83xxx_brd_tbl[STAC_92HD83XXX_MODELS] = { - [STAC_92HD83XXX_REF] = ref92hd83xxx_pin_configs, - [STAC_92HD83XXX_PWR_REF] = ref92hd83xxx_pin_configs, - [STAC_DELL_S14] = dell_s14_pin_configs, - [STAC_DELL_VOSTRO_3500] = dell_vostro_3500_pin_configs, - [STAC_92HD83XXX_HP_cNB11_INTQUAD] = hp_cNB11_intquad_pin_configs, - [STAC_HP_DV7_4000] = hp_dv7_4000_pin_configs, - [STAC_HP_ZEPHYR] = hp_zephyr_pin_configs, +static void stac92hd83xxx_fixup_hp(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct sigmatel_spec *spec = codec->spec; + + if (action != HDA_FIXUP_ACT_PRE_PROBE) + return; + + if (hp_bnb2011_with_dock(codec)) { + snd_hda_codec_set_pincfg(codec, 0xa, 0x2101201f); + snd_hda_codec_set_pincfg(codec, 0xf, 0x2181205e); + } + + if (find_mute_led_cfg(codec, spec->default_polarity)) + snd_printd("mute LED gpio %d polarity %d\n", + spec->gpio_led, + spec->gpio_led_polarity); +} + +static void stac92hd83xxx_fixup_hp_zephyr(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + if (action != HDA_FIXUP_ACT_PRE_PROBE) + return; + + snd_hda_apply_pincfgs(codec, hp_zephyr_pin_configs); + snd_hda_add_verbs(codec, stac92hd83xxx_hp_zephyr_init); +} + +static void stac92hd83xxx_fixup_hp_led(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct sigmatel_spec *spec = codec->spec; + + if (action == HDA_FIXUP_ACT_PRE_PROBE) + spec->default_polarity = 0; +} + +static void stac92hd83xxx_fixup_hp_inv_led(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct sigmatel_spec *spec = codec->spec; + + if (action == HDA_FIXUP_ACT_PRE_PROBE) + spec->default_polarity = 1; +} + +static void stac92hd83xxx_fixup_hp_mic_led(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct sigmatel_spec *spec = codec->spec; + + if (action == HDA_FIXUP_ACT_PRE_PROBE) + spec->mic_mute_led_gpio = 0x08; /* GPIO3 */ +} + +static void stac92hd83xxx_fixup_headset_jack(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct sigmatel_spec *spec = codec->spec; + + if (action == HDA_FIXUP_ACT_PRE_PROBE) + spec->headset_jack = 1; +} + +static const struct hda_fixup stac92hd83xxx_fixups[] = { + [STAC_92HD83XXX_REF] = { + .type = HDA_FIXUP_PINS, + .v.pins = ref92hd83xxx_pin_configs, + }, + [STAC_92HD83XXX_PWR_REF] = { + .type = HDA_FIXUP_PINS, + .v.pins = ref92hd83xxx_pin_configs, + }, + [STAC_DELL_S14] = { + .type = HDA_FIXUP_PINS, + .v.pins = dell_s14_pin_configs, + }, + [STAC_DELL_VOSTRO_3500] = { + .type = HDA_FIXUP_PINS, + .v.pins = dell_vostro_3500_pin_configs, + }, + [STAC_92HD83XXX_HP_cNB11_INTQUAD] = { + .type = HDA_FIXUP_PINS, + .v.pins = hp_cNB11_intquad_pin_configs, + .chained = true, + .chain_id = STAC_92HD83XXX_HP, + }, + [STAC_92HD83XXX_HP] = { + .type = HDA_FIXUP_FUNC, + .v.func = stac92hd83xxx_fixup_hp, + }, + [STAC_HP_DV7_4000] = { + .type = HDA_FIXUP_PINS, + .v.pins = hp_dv7_4000_pin_configs, + .chained = true, + .chain_id = STAC_92HD83XXX_HP, + }, + [STAC_HP_ZEPHYR] = { + .type = HDA_FIXUP_FUNC, + .v.func = stac92hd83xxx_fixup_hp_zephyr, + .chained = true, + .chain_id = STAC_92HD83XXX_HP, + }, + [STAC_92HD83XXX_HP_LED] = { + .type = HDA_FIXUP_FUNC, + .v.func = stac92hd83xxx_fixup_hp_led, + .chained = true, + .chain_id = STAC_92HD83XXX_HP, + }, + [STAC_92HD83XXX_HP_INV_LED] = { + .type = HDA_FIXUP_FUNC, + .v.func = stac92hd83xxx_fixup_hp_inv_led, + .chained = true, + .chain_id = STAC_92HD83XXX_HP, + }, + [STAC_92HD83XXX_HP_MIC_LED] = { + .type = HDA_FIXUP_FUNC, + .v.func = stac92hd83xxx_fixup_hp_mic_led, + .chained = true, + .chain_id = STAC_92HD83XXX_HP, + }, + [STAC_92HD83XXX_HEADSET_JACK] = { + .type = HDA_FIXUP_FUNC, + .v.func = stac92hd83xxx_fixup_headset_jack, + }, }; -static const char * const stac92hd83xxx_models[STAC_92HD83XXX_MODELS] = { - [STAC_92HD83XXX_AUTO] = "auto", - [STAC_92HD83XXX_REF] = "ref", - [STAC_92HD83XXX_PWR_REF] = "mic-ref", - [STAC_DELL_S14] = "dell-s14", - [STAC_DELL_VOSTRO_3500] = "dell-vostro-3500", - [STAC_92HD83XXX_HP_cNB11_INTQUAD] = "hp_cNB11_intquad", - [STAC_HP_DV7_4000] = "hp-dv7-4000", - [STAC_HP_ZEPHYR] = "hp-zephyr", - [STAC_92HD83XXX_HP_LED] = "hp-led", - [STAC_92HD83XXX_HP_INV_LED] = "hp-inv-led", - [STAC_92HD83XXX_HP_MIC_LED] = "hp-mic-led", - [STAC_92HD83XXX_HEADSET_JACK] = "headset-jack", +static const struct hda_model_fixup stac92hd83xxx_models[] = { + { .id = STAC_92HD83XXX_REF, .name = "ref" }, + { .id = STAC_92HD83XXX_PWR_REF, .name = "mic-ref" }, + { .id = STAC_DELL_S14, .name = "dell-s14" }, + { .id = STAC_DELL_VOSTRO_3500, .name = "dell-vostro-3500" }, + { .id = STAC_92HD83XXX_HP_cNB11_INTQUAD, .name = "hp_cNB11_intquad" }, + { .id = STAC_HP_DV7_4000, .name = "hp-dv7-4000" }, + { .id = STAC_HP_ZEPHYR, .name = "hp-zephyr" }, + { .id = STAC_92HD83XXX_HP_LED, .name = "hp-led" }, + { .id = STAC_92HD83XXX_HP_INV_LED, .name = "hp-inv-led" }, + { .id = STAC_92HD83XXX_HP_MIC_LED, .name = "hp-mic-led" }, + { .id = STAC_92HD83XXX_HEADSET_JACK, .name = "headset-jack" }, + {} }; -static const struct snd_pci_quirk stac92hd83xxx_cfg_tbl[] = { +static const struct snd_pci_quirk stac92hd83xxx_fixup_tbl[] = { /* SigmaTel reference board */ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668, "DFI LanParty", STAC_92HD83XXX_REF), @@ -2177,12 +2343,7 @@ static const struct snd_pci_quirk stac92hd83xxx_cfg_tbl[] = { "HP Mini", STAC_92HD83XXX_HP_LED), SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x144E, "HP Pavilion dv5", STAC_92HD83XXX_HP_INV_LED), - {} /* terminator */ -}; - -static const struct snd_pci_quirk stac92hd83xxx_codec_id_cfg_tbl[] = { - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3561, - "HP", STAC_HP_ZEPHYR), + SND_PCI_QUIRK_VENDOR(PCI_VENDOR_ID_HP, "HP", STAC_92HD83XXX_HP), {} /* terminator */ }; @@ -3462,21 +3623,6 @@ static const struct snd_pci_quirk stac9205_fixup_tbl[] = { {} /* terminator */ }; -static void stac92xx_set_config_regs(struct hda_codec *codec, - const unsigned int *pincfgs) -{ - int i; - struct sigmatel_spec *spec = codec->spec; - - if (!pincfgs) - return; - - for (i = 0; i < spec->num_pins; i++) - if (spec->pin_nids[i] && pincfgs[i]) - snd_hda_codec_set_pincfg(codec, spec->pin_nids[i], - pincfgs[i]); -} - /* * Analog playback callbacks */ @@ -6637,21 +6783,38 @@ static void stac92hd8x_fill_auto_spec(struct hda_codec *codec) spec->num_dmics = spec->auto_dmic_cnt; } +static void stac_setup_gpio(struct hda_codec *codec) +{ + struct sigmatel_spec *spec = codec->spec; + + if (spec->gpio_led) { + if (!spec->vref_mute_led_nid) { + spec->gpio_mask |= spec->gpio_led; + spec->gpio_dir |= spec->gpio_led; + spec->gpio_data |= spec->gpio_led; + } else { + codec->patch_ops.set_power_state = + stac92xx_set_power_state; + } + } + + if (spec->mic_mute_led_gpio) { + spec->gpio_mask |= spec->mic_mute_led_gpio; + spec->gpio_dir |= spec->mic_mute_led_gpio; + spec->mic_mute_led_on = true; + spec->gpio_data |= spec->mic_mute_led_gpio; + } +} + static int patch_stac92hd83xxx(struct hda_codec *codec) { struct sigmatel_spec *spec; - int default_polarity = -1; /* no default cfg */ int err; err = alloc_stac_spec(codec, 0, NULL); /* pins filled later */ if (err < 0) return err; - if (hp_bnb2011_with_dock(codec)) { - snd_hda_codec_set_pincfg(codec, 0xa, 0x2101201f); - snd_hda_codec_set_pincfg(codec, 0xf, 0x2181205e); - } - codec->epss = 0; /* longer delay needed for D3 */ stac92hd8x_fill_auto_spec(codec); @@ -6662,80 +6825,22 @@ static int patch_stac92hd83xxx(struct hda_codec *codec) spec->pwr_nids = stac92hd83xxx_pwr_nids; spec->num_pwrs = ARRAY_SIZE(stac92hd83xxx_pwr_nids); spec->multiout.dac_nids = spec->dac_nids; - spec->init = stac92hd83xxx_core_init; - - spec->board_config = snd_hda_check_board_config(codec, - STAC_92HD83XXX_MODELS, - stac92hd83xxx_models, - stac92hd83xxx_cfg_tbl); - /* check codec subsystem id if not found */ - if (spec->board_config < 0) - spec->board_config = - snd_hda_check_board_codec_sid_config(codec, - STAC_92HD83XXX_MODELS, stac92hd83xxx_models, - stac92hd83xxx_codec_id_cfg_tbl); -again: - if (spec->board_config < 0) - snd_printdd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", - codec->chip_name); - else - stac92xx_set_config_regs(codec, - stac92hd83xxx_brd_tbl[spec->board_config]); - codec->patch_ops = stac92xx_patch_ops; + snd_hda_pick_fixup(codec, stac92hd83xxx_models, stac92hd83xxx_fixup_tbl, + stac92hd83xxx_fixups); - switch (spec->board_config) { - case STAC_HP_ZEPHYR: - spec->init = stac92hd83xxx_hp_zephyr_init; - break; - case STAC_92HD83XXX_HP_LED: - default_polarity = 0; - break; - case STAC_92HD83XXX_HP_INV_LED: - default_polarity = 1; - break; - case STAC_92HD83XXX_HP_MIC_LED: - spec->mic_mute_led_gpio = 0x08; /* GPIO3 */ - break; - case STAC_92HD83XXX_HEADSET_JACK: - spec->headset_jack = 1; - break; - } + snd_hda_add_verbs(codec, stac92hd83xxx_core_init); + spec->default_polarity = -1; /* no default cfg */ - if (find_mute_led_cfg(codec, default_polarity)) - snd_printd("mute LED gpio %d polarity %d\n", - spec->gpio_led, - spec->gpio_led_polarity); + codec->patch_ops = stac92xx_patch_ops; - if (spec->gpio_led) { - if (!spec->vref_mute_led_nid) { - spec->gpio_mask |= spec->gpio_led; - spec->gpio_dir |= spec->gpio_led; - spec->gpio_data |= spec->gpio_led; - } else { - codec->patch_ops.set_power_state = - stac92xx_set_power_state; - } - } + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); - if (spec->mic_mute_led_gpio) { - spec->gpio_mask |= spec->mic_mute_led_gpio; - spec->gpio_dir |= spec->mic_mute_led_gpio; - spec->mic_mute_led_on = true; - spec->gpio_data |= spec->mic_mute_led_gpio; - } + stac_setup_gpio(codec); err = stac92xx_parse_auto_config(codec); - if (!err) { - if (spec->board_config < 0) { - printk(KERN_WARNING "hda_codec: No auto-config is " - "available, default to model=ref\n"); - spec->board_config = STAC_92HD83XXX_REF; - goto again; - } + if (!err) err = -EINVAL; - } - if (err < 0) { stac92xx_free(codec); return err; @@ -6743,6 +6848,8 @@ again: codec->proc_widget_hook = stac92hd_proc_hook; + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); + return 0; } @@ -6925,16 +7032,7 @@ static int patch_stac92hd71bxx(struct hda_codec *codec) snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); - if (spec->gpio_led) { - if (!spec->vref_mute_led_nid) { - spec->gpio_mask |= spec->gpio_led; - spec->gpio_dir |= spec->gpio_led; - spec->gpio_data |= spec->gpio_led; - } else { - codec->patch_ops.set_power_state = - stac92xx_set_power_state; - } - } + stac_setup_gpio(codec); spec->multiout.dac_nids = spec->dac_nids; -- cgit v1.2.3 From 89bb3e74b1acb7c26306a5aaa522eb7105c25f65 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 15 Jan 2013 11:12:18 +0100 Subject: ALSA: hda/sigmatel - Remove PCI id check in find_mute_led_cfg() The PCI vendor ID check in find_mute_led_cfg() is now superfluous because the function is called in the fixup table entries of HP machines. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_sigmatel.c | 83 ++++++++++++++++++++---------------------- 1 file changed, 40 insertions(+), 43 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index fa16ff79866c..3368f74811ce 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -6219,53 +6219,50 @@ static int find_mute_led_cfg(struct hda_codec *codec, int default_polarity) return 1; } - if ((codec->subsystem_id >> 16) == PCI_VENDOR_ID_HP) { - while ((dev = dmi_find_device(DMI_DEV_TYPE_OEM_STRING, - NULL, dev))) { - if (sscanf(dev->name, "HP_Mute_LED_%d_%x", - &spec->gpio_led_polarity, - &spec->gpio_led) == 2) { - unsigned int max_gpio; - max_gpio = snd_hda_param_read(codec, codec->afg, - AC_PAR_GPIO_CAP); - max_gpio &= AC_GPIO_IO_COUNT; - if (spec->gpio_led < max_gpio) - spec->gpio_led = 1 << spec->gpio_led; - else - spec->vref_mute_led_nid = spec->gpio_led; - return 1; - } - if (sscanf(dev->name, "HP_Mute_LED_%d", - &spec->gpio_led_polarity) == 1) { - set_hp_led_gpio(codec); - return 1; - } - /* BIOS bug: unfilled OEM string */ - if (strstr(dev->name, "HP_Mute_LED_P_G")) { - set_hp_led_gpio(codec); - switch (codec->subsystem_id) { - case 0x103c148a: - spec->gpio_led_polarity = 0; - break; - default: - spec->gpio_led_polarity = 1; - break; - } - return 1; - } + while ((dev = dmi_find_device(DMI_DEV_TYPE_OEM_STRING, NULL, dev))) { + if (sscanf(dev->name, "HP_Mute_LED_%d_%x", + &spec->gpio_led_polarity, + &spec->gpio_led) == 2) { + unsigned int max_gpio; + max_gpio = snd_hda_param_read(codec, codec->afg, + AC_PAR_GPIO_CAP); + max_gpio &= AC_GPIO_IO_COUNT; + if (spec->gpio_led < max_gpio) + spec->gpio_led = 1 << spec->gpio_led; + else + spec->vref_mute_led_nid = spec->gpio_led; + return 1; } - - /* - * Fallback case - if we don't find the DMI strings, - * we statically set the GPIO - if not a B-series system - * and default polarity is provided - */ - if (!hp_blike_system(codec->subsystem_id) && - (default_polarity == 0 || default_polarity == 1)) { + if (sscanf(dev->name, "HP_Mute_LED_%d", + &spec->gpio_led_polarity) == 1) { set_hp_led_gpio(codec); - spec->gpio_led_polarity = default_polarity; return 1; } + /* BIOS bug: unfilled OEM string */ + if (strstr(dev->name, "HP_Mute_LED_P_G")) { + set_hp_led_gpio(codec); + switch (codec->subsystem_id) { + case 0x103c148a: + spec->gpio_led_polarity = 0; + break; + default: + spec->gpio_led_polarity = 1; + break; + } + return 1; + } + } + + /* + * Fallback case - if we don't find the DMI strings, + * we statically set the GPIO - if not a B-series system + * and default polarity is provided + */ + if (!hp_blike_system(codec->subsystem_id) && + (default_polarity == 0 || default_polarity == 1)) { + set_hp_led_gpio(codec); + spec->gpio_led_polarity = default_polarity; + return 1; } return 0; } -- cgit v1.2.3 From 8c698fe21016c19784df8c91586c857ddbdc0440 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 15 Jan 2013 11:20:50 +0100 Subject: ALSA: hda/sigmatel - Move w/a for HP Mini 110 LED to fixup table Instead of checking the codec SSID in find_mute_led_cfg() for HP Mini 110, set the proper spec->default_polairty in the fixup table. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_sigmatel.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 3368f74811ce..ccad9adae928 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -2343,6 +2343,8 @@ static const struct snd_pci_quirk stac92hd83xxx_fixup_tbl[] = { "HP Mini", STAC_92HD83XXX_HP_LED), SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x144E, "HP Pavilion dv5", STAC_92HD83XXX_HP_INV_LED), + SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x148a, + "HP Mini", STAC_92HD83XXX_HP_LED), SND_PCI_QUIRK_VENDOR(PCI_VENDOR_ID_HP, "HP", STAC_92HD83XXX_HP), {} /* terminator */ }; @@ -6241,14 +6243,10 @@ static int find_mute_led_cfg(struct hda_codec *codec, int default_polarity) /* BIOS bug: unfilled OEM string */ if (strstr(dev->name, "HP_Mute_LED_P_G")) { set_hp_led_gpio(codec); - switch (codec->subsystem_id) { - case 0x103c148a: - spec->gpio_led_polarity = 0; - break; - default: + if (default_polarity >= 0) + spec->gpio_led_polarity = default_polarity; + else spec->gpio_led_polarity = 1; - break; - } return 1; } } -- cgit v1.2.3 From 9b473e8516c0d6745dd4c0ec69f9c17f14df0469 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 15 Jan 2013 11:22:01 +0100 Subject: ALSA: hda/sigmatel - Remove superfluous fields from sigmatel_spec Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_sigmatel.c | 1 - 1 file changed, 1 deletion(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index ccad9adae928..a7eed73ab34e 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -187,7 +187,6 @@ struct sigmatel_spec { struct snd_kcontrol_new *mixers[4]; unsigned int num_mixers; - int board_config; unsigned int eapd_switch: 1; unsigned int surr_switch: 1; unsigned int alt_switch: 1; -- cgit v1.2.3 From f038fcaca827a2330d502a5d653ab639419f45db Mon Sep 17 00:00:00 2001 From: David Henningsson Date: Tue, 15 Jan 2013 15:27:19 +0100 Subject: ALSA: hda - fix OOPS in hda_mark_cmd_cache_dirty Obvious copy-paste error. Signed-off-by: David Henningsson Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_codec.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index b28e4031b8a1..e6cdad713734 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -3794,7 +3794,7 @@ static void hda_mark_cmd_cache_dirty(struct hda_codec *codec) } for (i = 0; i < codec->amp_cache.buf.used; i++) { struct hda_amp_info *amp; - amp = snd_array_elem(&codec->cmd_cache.buf, i); + amp = snd_array_elem(&codec->amp_cache.buf, i); amp->head.dirty = 1; } } -- cgit v1.2.3 From ef6b2eada3b8c1b21f6479d7480ea7030183fe1d Mon Sep 17 00:00:00 2001 From: Ian Minett Date: Thu, 20 Dec 2012 18:53:33 -0800 Subject: ALSA: hda/ca0132: Add new definitions and structs for DSP This patch adds definitions and structs used for configuring DSP effects, virtual nodes, effect tuning controls, and mixer control helpers. The effect structs are also initialized. Signed-off-by: Ian Minett Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_ca0132.c | 445 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 442 insertions(+), 3 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index 2fd3121afb10..38ac07b8a4ec 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -35,6 +35,18 @@ #include "ca0132_regs.h" +/* Enable this to see controls for tuning purpose. */ +/*#define ENABLE_TUNING_CONTROLS*/ + +#define FLOAT_ZERO 0x00000000 +#define FLOAT_ONE 0x3f800000 +#define FLOAT_TWO 0x40000000 +#define FLOAT_MINUS_5 0xc0a00000 + +#define UNSOL_TAG_HP 0x10 +#define UNSOL_TAG_AMIC1 0x12 +#define UNSOL_TAG_DSP 0x16 + #define DSP_DMA_WRITE_BUFLEN_INIT (1UL<<18) #define DSP_DMA_WRITE_BUFLEN_OVLY (1UL<<15) @@ -43,7 +55,8 @@ #define DMA_OVERLAY_FRAME_SIZE_NWORDS 2 #define MASTERCONTROL 0x80 -#define MASTERCONTROL_ALLOC_DMA_CHAN 9 +#define MASTERCONTROL_ALLOC_DMA_CHAN 10 +#define MASTERCONTROL_QUERY_SPEAKER_EQ_ADDRESS 60 #define WIDGET_CHIP_CTRL 0x15 #define WIDGET_DSP_CTRL 0x16 @@ -63,6 +76,394 @@ MODULE_FIRMWARE(EFX_FILE); +static char *dirstr[2] = { "Playback", "Capture" }; + +enum { + SPEAKER_OUT, + HEADPHONE_OUT +}; + +enum { + DIGITAL_MIC, + LINE_MIC_IN +}; + +enum { +#define VNODE_START_NID 0x80 + VNID_SPK = VNODE_START_NID, /* Speaker vnid */ + VNID_MIC, + VNID_HP_SEL, + VNID_AMIC1_SEL, + VNID_HP_ASEL, + VNID_AMIC1_ASEL, + VNODE_END_NID, +#define VNODES_COUNT (VNODE_END_NID - VNODE_START_NID) + +#define EFFECT_START_NID 0x90 +#define OUT_EFFECT_START_NID EFFECT_START_NID + SURROUND = OUT_EFFECT_START_NID, + CRYSTALIZER, + DIALOG_PLUS, + SMART_VOLUME, + X_BASS, + EQUALIZER, + OUT_EFFECT_END_NID, +#define OUT_EFFECTS_COUNT (OUT_EFFECT_END_NID - OUT_EFFECT_START_NID) + +#define IN_EFFECT_START_NID OUT_EFFECT_END_NID + ECHO_CANCELLATION = IN_EFFECT_START_NID, + VOICE_FOCUS, + MIC_SVM, + NOISE_REDUCTION, + IN_EFFECT_END_NID, +#define IN_EFFECTS_COUNT (IN_EFFECT_END_NID - IN_EFFECT_START_NID) + + VOICEFX = IN_EFFECT_END_NID, + PLAY_ENHANCEMENT, + CRYSTAL_VOICE, + EFFECT_END_NID +#define EFFECTS_COUNT (EFFECT_END_NID - EFFECT_START_NID) +}; + +/* Effects values size*/ +#define EFFECT_VALS_MAX_COUNT 12 + +struct ct_effect { + char name[44]; + hda_nid_t nid; + int mid; /*effect module ID*/ + int reqs[EFFECT_VALS_MAX_COUNT]; /*effect module request*/ + int direct; /* 0:output; 1:input*/ + int params; /* number of default non-on/off params */ + /*effect default values, 1st is on/off. */ + unsigned int def_vals[EFFECT_VALS_MAX_COUNT]; +}; + +#define EFX_DIR_OUT 0 +#define EFX_DIR_IN 1 + +static struct ct_effect ca0132_effects[EFFECTS_COUNT] = { + { .name = "Surround", + .nid = SURROUND, + .mid = 0x96, + .reqs = {0, 1}, + .direct = EFX_DIR_OUT, + .params = 1, + .def_vals = {0x3F800000, 0x3F2B851F} + }, + { .name = "Crystalizer", + .nid = CRYSTALIZER, + .mid = 0x96, + .reqs = {7, 8}, + .direct = EFX_DIR_OUT, + .params = 1, + .def_vals = {0x3F800000, 0x3F266666} + }, + { .name = "Dialog Plus", + .nid = DIALOG_PLUS, + .mid = 0x96, + .reqs = {2, 3}, + .direct = EFX_DIR_OUT, + .params = 1, + .def_vals = {0x00000000, 0x3F000000} + }, + { .name = "Smart Volume", + .nid = SMART_VOLUME, + .mid = 0x96, + .reqs = {4, 5, 6}, + .direct = EFX_DIR_OUT, + .params = 2, + .def_vals = {0x3F800000, 0x3F3D70A4, 0x00000000} + }, + { .name = "X-Bass", + .nid = X_BASS, + .mid = 0x96, + .reqs = {24, 23, 25}, + .direct = EFX_DIR_OUT, + .params = 2, + .def_vals = {0x3F800000, 0x42A00000, 0x3F000000} + }, + { .name = "Equalizer", + .nid = EQUALIZER, + .mid = 0x96, + .reqs = {9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20}, + .direct = EFX_DIR_OUT, + .params = 11, + .def_vals = {0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000} + }, + { .name = "Echo Cancellation", + .nid = ECHO_CANCELLATION, + .mid = 0x95, + .reqs = {0, 1, 2, 3}, + .direct = EFX_DIR_IN, + .params = 3, + .def_vals = {0x00000000, 0x3F3A9692, 0x00000000, 0x00000000} + }, + { .name = "Voice Focus", + .nid = VOICE_FOCUS, + .mid = 0x95, + .reqs = {6, 7, 8, 9}, + .direct = EFX_DIR_IN, + .params = 3, + .def_vals = {0x3F800000, 0x3D7DF3B6, 0x41F00000, 0x41F00000} + }, + { .name = "Mic SVM", + .nid = MIC_SVM, + .mid = 0x95, + .reqs = {44, 45}, + .direct = EFX_DIR_IN, + .params = 1, + .def_vals = {0x00000000, 0x3F3D70A4} + }, + { .name = "Noise Reduction", + .nid = NOISE_REDUCTION, + .mid = 0x95, + .reqs = {4, 5}, + .direct = EFX_DIR_IN, + .params = 1, + .def_vals = {0x3F800000, 0x3F000000} + }, + { .name = "VoiceFX", + .nid = VOICEFX, + .mid = 0x95, + .reqs = {10, 11, 12, 13, 14, 15, 16, 17, 18}, + .direct = EFX_DIR_IN, + .params = 8, + .def_vals = {0x00000000, 0x43C80000, 0x44AF0000, 0x44FA0000, + 0x3F800000, 0x3F800000, 0x3F800000, 0x00000000, + 0x00000000} + } +}; + +/* Tuning controls */ +#ifdef ENABLE_TUNING_CONTROLS + +enum { +#define TUNING_CTL_START_NID 0xC0 + WEDGE_ANGLE = TUNING_CTL_START_NID, + SVM_LEVEL, + EQUALIZER_BAND_0, + EQUALIZER_BAND_1, + EQUALIZER_BAND_2, + EQUALIZER_BAND_3, + EQUALIZER_BAND_4, + EQUALIZER_BAND_5, + EQUALIZER_BAND_6, + EQUALIZER_BAND_7, + EQUALIZER_BAND_8, + EQUALIZER_BAND_9, + TUNING_CTL_END_NID +#define TUNING_CTLS_COUNT (TUNING_CTL_END_NID - TUNING_CTL_START_NID) +}; + +struct ct_tuning_ctl { + char name[44]; + hda_nid_t parent_nid; + hda_nid_t nid; + int mid; /*effect module ID*/ + int req; /*effect module request*/ + int direct; /* 0:output; 1:input*/ + unsigned int def_val;/*effect default values*/ +}; + +static struct ct_tuning_ctl ca0132_tuning_ctls[] = { + { .name = "Wedge Angle", + .parent_nid = VOICE_FOCUS, + .nid = WEDGE_ANGLE, + .mid = 0x95, + .req = 8, + .direct = EFX_DIR_IN, + .def_val = 0x41F00000 + }, + { .name = "SVM Level", + .parent_nid = MIC_SVM, + .nid = SVM_LEVEL, + .mid = 0x95, + .req = 45, + .direct = EFX_DIR_IN, + .def_val = 0x3F3D70A4 + }, + { .name = "EQ Band0", + .parent_nid = EQUALIZER, + .nid = EQUALIZER_BAND_0, + .mid = 0x96, + .req = 11, + .direct = EFX_DIR_OUT, + .def_val = 0x00000000 + }, + { .name = "EQ Band1", + .parent_nid = EQUALIZER, + .nid = EQUALIZER_BAND_1, + .mid = 0x96, + .req = 12, + .direct = EFX_DIR_OUT, + .def_val = 0x00000000 + }, + { .name = "EQ Band2", + .parent_nid = EQUALIZER, + .nid = EQUALIZER_BAND_2, + .mid = 0x96, + .req = 13, + .direct = EFX_DIR_OUT, + .def_val = 0x00000000 + }, + { .name = "EQ Band3", + .parent_nid = EQUALIZER, + .nid = EQUALIZER_BAND_3, + .mid = 0x96, + .req = 14, + .direct = EFX_DIR_OUT, + .def_val = 0x00000000 + }, + { .name = "EQ Band4", + .parent_nid = EQUALIZER, + .nid = EQUALIZER_BAND_4, + .mid = 0x96, + .req = 15, + .direct = EFX_DIR_OUT, + .def_val = 0x00000000 + }, + { .name = "EQ Band5", + .parent_nid = EQUALIZER, + .nid = EQUALIZER_BAND_5, + .mid = 0x96, + .req = 16, + .direct = EFX_DIR_OUT, + .def_val = 0x00000000 + }, + { .name = "EQ Band6", + .parent_nid = EQUALIZER, + .nid = EQUALIZER_BAND_6, + .mid = 0x96, + .req = 17, + .direct = EFX_DIR_OUT, + .def_val = 0x00000000 + }, + { .name = "EQ Band7", + .parent_nid = EQUALIZER, + .nid = EQUALIZER_BAND_7, + .mid = 0x96, + .req = 18, + .direct = EFX_DIR_OUT, + .def_val = 0x00000000 + }, + { .name = "EQ Band8", + .parent_nid = EQUALIZER, + .nid = EQUALIZER_BAND_8, + .mid = 0x96, + .req = 19, + .direct = EFX_DIR_OUT, + .def_val = 0x00000000 + }, + { .name = "EQ Band9", + .parent_nid = EQUALIZER, + .nid = EQUALIZER_BAND_9, + .mid = 0x96, + .req = 20, + .direct = EFX_DIR_OUT, + .def_val = 0x00000000 + } +}; +#endif + +/* Voice FX Presets */ +#define VOICEFX_MAX_PARAM_COUNT 9 + +struct ct_voicefx { + char *name; + hda_nid_t nid; + int mid; + int reqs[VOICEFX_MAX_PARAM_COUNT]; /*effect module request*/ +}; + +struct ct_voicefx_preset { + char *name; /*preset name*/ + unsigned int vals[VOICEFX_MAX_PARAM_COUNT]; +}; + +struct ct_voicefx ca0132_voicefx = { + .name = "VoiceFX Capture Switch", + .nid = VOICEFX, + .mid = 0x95, + .reqs = {10, 11, 12, 13, 14, 15, 16, 17, 18} +}; + +struct ct_voicefx_preset ca0132_voicefx_presets[] = { + { .name = "Neutral", + .vals = { 0x00000000, 0x43C80000, 0x44AF0000, + 0x44FA0000, 0x3F800000, 0x3F800000, + 0x3F800000, 0x00000000, 0x00000000 } + }, + { .name = "Female2Male", + .vals = { 0x3F800000, 0x43C80000, 0x44AF0000, + 0x44FA0000, 0x3F19999A, 0x3F866666, + 0x3F800000, 0x00000000, 0x00000000 } + }, + { .name = "Male2Female", + .vals = { 0x3F800000, 0x43C80000, 0x44AF0000, + 0x450AC000, 0x4017AE14, 0x3F6B851F, + 0x3F800000, 0x00000000, 0x00000000 } + }, + { .name = "ScrappyKid", + .vals = { 0x3F800000, 0x43C80000, 0x44AF0000, + 0x44FA0000, 0x40400000, 0x3F28F5C3, + 0x3F800000, 0x00000000, 0x00000000 } + }, + { .name = "Elderly", + .vals = { 0x3F800000, 0x44324000, 0x44BB8000, + 0x44E10000, 0x3FB33333, 0x3FB9999A, + 0x3F800000, 0x3E3A2E43, 0x00000000 } + }, + { .name = "Orc", + .vals = { 0x3F800000, 0x43EA0000, 0x44A52000, + 0x45098000, 0x3F266666, 0x3FC00000, + 0x3F800000, 0x00000000, 0x00000000 } + }, + { .name = "Elf", + .vals = { 0x3F800000, 0x43C70000, 0x44AE6000, + 0x45193000, 0x3F8E147B, 0x3F75C28F, + 0x3F800000, 0x00000000, 0x00000000 } + }, + { .name = "Dwarf", + .vals = { 0x3F800000, 0x43930000, 0x44BEE000, + 0x45007000, 0x3F451EB8, 0x3F7851EC, + 0x3F800000, 0x00000000, 0x00000000 } + }, + { .name = "AlienBrute", + .vals = { 0x3F800000, 0x43BFC5AC, 0x44B28FDF, + 0x451F6000, 0x3F266666, 0x3FA7D945, + 0x3F800000, 0x3CF5C28F, 0x00000000 } + }, + { .name = "Robot", + .vals = { 0x3F800000, 0x43C80000, 0x44AF0000, + 0x44FA0000, 0x3FB2718B, 0x3F800000, + 0xBC07010E, 0x00000000, 0x00000000 } + }, + { .name = "Marine", + .vals = { 0x3F800000, 0x43C20000, 0x44906000, + 0x44E70000, 0x3F4CCCCD, 0x3F8A3D71, + 0x3F0A3D71, 0x00000000, 0x00000000 } + }, + { .name = "Emo", + .vals = { 0x3F800000, 0x43C80000, 0x44AF0000, + 0x44FA0000, 0x3F800000, 0x3F800000, + 0x3E4CCCCD, 0x00000000, 0x00000000 } + }, + { .name = "DeepVoice", + .vals = { 0x3F800000, 0x43A9C5AC, 0x44AA4FDF, + 0x44FFC000, 0x3EDBB56F, 0x3F99C4CA, + 0x3F800000, 0x00000000, 0x00000000 } + }, + { .name = "Munchkin", + .vals = { 0x3F800000, 0x43C80000, 0x44AF0000, + 0x44FA0000, 0x3F800000, 0x3F1A043C, + 0x3F800000, 0x00000000, 0x00000000 } + } +}; + enum hda_cmd_vendor_io { /* for DspIO node */ VENDOR_DSPIO_SCP_WRITE_DATA_LOW = 0x000, @@ -184,8 +585,16 @@ enum control_flag_id { * Control parameter IDs */ enum control_param_id { + /* 0: None, 1: Mic1In*/ + CONTROL_PARAM_VIP_SOURCE = 1, /* 0: force HDA, 1: allow DSP if HDA Spdif1Out stream is idle */ CONTROL_PARAM_SPDIF1_SOURCE = 2, + /* Port A output stage gain setting to use when 16 Ohm output + * impedance is selected*/ + CONTROL_PARAM_PORTA_160OHM_GAIN = 8, + /* Port D output stage gain setting to use when 16 Ohm output + * impedance is selected*/ + CONTROL_PARAM_PORTD_160OHM_GAIN = 10, /* Stream Control */ @@ -304,8 +713,6 @@ static void init_input(struct hda_codec *codec, hda_nid_t pin, hda_nid_t adc) AMP_IN_UNMUTE(0)); } -static char *dirstr[2] = { "Playback", "Capture" }; - static int _add_switch(struct hda_codec *codec, hda_nid_t nid, const char *pfx, int chan, int dir) { @@ -2190,6 +2597,38 @@ static bool dspload_wait_loaded(struct hda_codec *codec) return false; } + +/* + * Mixer controls helpers. + */ +#define CA0132_CODEC_VOL_MONO(xname, nid, channel, dir) \ + { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = xname, \ + .subdevice = HDA_SUBDEV_AMP_FLAG, \ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \ + SNDRV_CTL_ELEM_ACCESS_TLV_READ | \ + SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK, \ + .info = ca0132_volume_info, \ + .get = ca0132_volume_get, \ + .put = ca0132_volume_put, \ + .tlv = { .c = ca0132_volume_tlv }, \ + .private_value = HDA_COMPOSE_AMP_VAL(nid, channel, 0, dir) } + +#define CA0132_CODEC_MUTE_MONO(xname, nid, channel, dir) \ + { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = xname, \ + .subdevice = HDA_SUBDEV_AMP_FLAG, \ + .info = snd_hda_mixer_amp_switch_info, \ + .get = ca0132_switch_get, \ + .put = ca0132_switch_put, \ + .private_value = HDA_COMPOSE_AMP_VAL(nid, channel, 0, dir) } + +/* stereo */ +#define CA0132_CODEC_VOL(xname, nid, dir) \ + CA0132_CODEC_VOL_MONO(xname, nid, 3, dir) +#define CA0132_CODEC_MUTE(xname, nid, dir) \ + CA0132_CODEC_MUTE_MONO(xname, nid, 3, dir) + /* * PCM callbacks */ -- cgit v1.2.3 From 5aaca44d8d05d144eec891498ff529c6ad4f5794 Mon Sep 17 00:00:00 2001 From: Ian Minett Date: Thu, 20 Dec 2012 18:53:34 -0800 Subject: ALSA: hda/ca0132: Init chip, DSP effects and mixer settings This patch adds the framework to set effect parameters: ca0132_effects_set() and ca0132_setup_defaults() are general functions for parameter setting and initializing to default values. dspio_set_param() and dspio_set_uint_param() are lower-level fns to simplify setting individual DSP parameters via an SCP buffer transfer to the firmware. The CA0132 chip parameter init code is added in ca0132_init_params(). In chipio_[write,read]_data(), the current chip address is auto-incremented if no error has occurred. ca0132_select_out() selects the current output. If autodetect is enabled, use headphones (if jack detected) or speakers (if no jack). ca0132_select_mic() selects the current mic in. If autodetect is enabled, use exterior mic (if jack detected) or built-in mic (if no jack). Init digital mic and switch between dmic and amic with ca0132_init_dmic(), ca0132_set_dmic(). amic2 is initialized in ca0132_init_analog_mic2(). Finally, add verb tables for configuring DSP firmware. Signed-off-by: Ian Minett Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_ca0132.c | 732 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 700 insertions(+), 32 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index 38ac07b8a4ec..e4e1684ace6f 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -32,6 +32,7 @@ #include "hda_codec.h" #include "hda_local.h" #include "hda_auto_parser.h" +#include "hda_jack.h" #include "ca0132_regs.h" @@ -672,22 +673,11 @@ enum ca0132_sample_rate { SR_RATE_UNKNOWN = 0x1F }; -/* - * Scp Helper function - */ -enum get_set { - IS_SET = 0, - IS_GET = 1, -}; - -/* - * Duplicated from ca0110 codec - */ - static void init_output(struct hda_codec *codec, hda_nid_t pin, hda_nid_t dac) { if (pin) { - snd_hda_set_pin_ctl(codec, pin, PIN_HP); + snd_hda_codec_write(codec, pin, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP); if (get_wcaps(codec, pin) & AC_WCAP_OUT_AMP) snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_AMP_GAIN_MUTE, @@ -701,16 +691,23 @@ static void init_output(struct hda_codec *codec, hda_nid_t pin, hda_nid_t dac) static void init_input(struct hda_codec *codec, hda_nid_t pin, hda_nid_t adc) { if (pin) { - snd_hda_set_pin_ctl(codec, pin, PIN_IN | - snd_hda_get_default_vref(codec, pin)); + snd_hda_codec_write(codec, pin, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80); if (get_wcaps(codec, pin) & AC_WCAP_IN_AMP) snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)); } - if (adc && (get_wcaps(codec, adc) & AC_WCAP_IN_AMP)) + if (adc && (get_wcaps(codec, adc) & AC_WCAP_IN_AMP)) { snd_hda_codec_write(codec, adc, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)); + + /* init to 0 dB and unmute. */ + snd_hda_codec_amp_stereo(codec, adc, HDA_INPUT, 0, + HDA_AMP_VOLMASK, 0x5a); + snd_hda_codec_amp_stereo(codec, adc, HDA_INPUT, 0, + HDA_AMP_MUTE, 0); + } } static int _add_switch(struct hda_codec *codec, hda_nid_t nid, const char *pfx, @@ -774,11 +771,18 @@ enum dsp_download_state { */ struct ca0132_spec { + const struct hda_verb *base_init_verbs; + const struct hda_verb *base_exit_verbs; + const struct hda_verb *init_verbs[5]; + unsigned int num_init_verbs; /* exclude base init verbs */ struct auto_pin_cfg autocfg; + + /* Nodes configurations */ struct hda_multi_out multiout; hda_nid_t out_pins[AUTO_CFG_MAX_OUTS]; hda_nid_t dacs[AUTO_CFG_MAX_OUTS]; hda_nid_t hp_dac; + unsigned int num_outputs; hda_nid_t input_pins[AUTO_PIN_LAST]; hda_nid_t adcs[AUTO_PIN_LAST]; hda_nid_t dig_out; @@ -788,7 +792,7 @@ struct ca0132_spec { long curr_hp_volume[2]; long curr_speaker_switch; const char *input_labels[AUTO_PIN_LAST]; - struct hda_pcm pcm_rec[2]; /* PCM information */ + struct hda_pcm pcm_rec[5]; /* PCM information */ /* chip access */ struct mutex chipio_mutex; /* chip access mutex */ @@ -803,6 +807,18 @@ struct ca0132_spec { unsigned int scp_resp_header; unsigned int scp_resp_data[4]; unsigned int scp_resp_count; + + /* mixer and effects related */ + unsigned char dmic_ctl; + int cur_out_type; + int cur_mic_type; + long vnode_lvol[VNODES_COUNT]; + long vnode_rvol[VNODES_COUNT]; + long vnode_lswitch[VNODES_COUNT]; + long vnode_rswitch[VNODES_COUNT]; + long effects_switch[EFFECTS_COUNT]; + long voicefx_val; + long cur_mic_boost; }; /* @@ -886,6 +902,7 @@ static int chipio_write_address(struct hda_codec *codec, */ static int chipio_write_data(struct hda_codec *codec, unsigned int data) { + struct ca0132_spec *spec = codec->spec; int res; /* send low 16 bits of the data */ @@ -897,6 +914,10 @@ static int chipio_write_data(struct hda_codec *codec, unsigned int data) data >> 16); } + /*If no error encountered, automatically increment the address + as per chip behaviour*/ + spec->curr_chip_addx = (res != -EIO) ? + (spec->curr_chip_addx + 4) : ~0UL; return res; } @@ -926,6 +947,7 @@ static int chipio_write_data_multiple(struct hda_codec *codec, */ static int chipio_read_data(struct hda_codec *codec, unsigned int *data) { + struct ca0132_spec *spec = codec->spec; int res; /* post read */ @@ -943,6 +965,10 @@ static int chipio_read_data(struct hda_codec *codec, unsigned int *data) 0); } + /*If no error encountered, automatically increment the address + as per chip behaviour*/ + spec->curr_chip_addx = (res != -EIO) ? + (spec->curr_chip_addx + 4) : ~0UL; return res; } @@ -1418,6 +1444,21 @@ static int dspio_scp(struct hda_codec *codec, return status; } +/* + * Set DSP parameters + */ +static int dspio_set_param(struct hda_codec *codec, int mod_id, + int req, void *data, unsigned int len) +{ + return dspio_scp(codec, mod_id, req, SCP_SET, data, len, NULL, NULL); +} + +static int dspio_set_uint_param(struct hda_codec *codec, int mod_id, + int req, unsigned int data) +{ + return dspio_set_param(codec, mod_id, req, &data, sizeof(unsigned int)); +} + /* * Allocate a DSP DMA channel via an SCP message */ @@ -2597,6 +2638,9 @@ static bool dspload_wait_loaded(struct hda_codec *codec) return false; } +/* + * Controls stuffs. + */ /* * Mixer controls helpers. @@ -2698,6 +2742,300 @@ static int ca0132_dig_playback_pcm_close(struct hda_pcm_stream *hinfo, return snd_hda_multi_out_dig_close(codec, &spec->multiout); } +/* + * Select the active output. + * If autodetect is enabled, output will be selected based on jack detection. + * If jack inserted, headphone will be selected, else built-in speakers + * If autodetect is disabled, output will be selected based on selection. + */ +static int ca0132_select_out(struct hda_codec *codec) +{ + struct ca0132_spec *spec = codec->spec; + unsigned int pin_ctl; + int jack_present; + int auto_jack; + unsigned int tmp; + int err; + + snd_printdd(KERN_INFO "ca0132_select_out\n"); + + snd_hda_power_up(codec); + + auto_jack = spec->vnode_lswitch[VNID_HP_ASEL - VNODE_START_NID]; + + if (auto_jack) + jack_present = snd_hda_jack_detect(codec, spec->out_pins[1]); + else + jack_present = + spec->vnode_lswitch[VNID_HP_SEL - VNODE_START_NID]; + + if (jack_present) + spec->cur_out_type = HEADPHONE_OUT; + else + spec->cur_out_type = SPEAKER_OUT; + + if (spec->cur_out_type == SPEAKER_OUT) { + snd_printdd(KERN_INFO "ca0132_select_out speaker\n"); + /*speaker out config*/ + tmp = FLOAT_ONE; + err = dspio_set_uint_param(codec, 0x80, 0x04, tmp); + if (err < 0) + goto exit; + /*enable speaker EQ*/ + tmp = FLOAT_ONE; + err = dspio_set_uint_param(codec, 0x8f, 0x00, tmp); + if (err < 0) + goto exit; + + /* Setup EAPD */ + snd_hda_codec_write(codec, spec->out_pins[1], 0, + VENDOR_CHIPIO_EAPD_SEL_SET, 0x02); + snd_hda_codec_write(codec, spec->out_pins[0], 0, + AC_VERB_SET_EAPD_BTLENABLE, 0x00); + snd_hda_codec_write(codec, spec->out_pins[0], 0, + VENDOR_CHIPIO_EAPD_SEL_SET, 0x00); + snd_hda_codec_write(codec, spec->out_pins[0], 0, + AC_VERB_SET_EAPD_BTLENABLE, 0x02); + + /* disable headphone node */ + pin_ctl = snd_hda_codec_read(codec, spec->out_pins[1], 0, + AC_VERB_GET_PIN_WIDGET_CONTROL, 0); + snd_hda_codec_write(codec, spec->out_pins[1], 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, + pin_ctl & 0xBF); + /* enable speaker node */ + pin_ctl = snd_hda_codec_read(codec, spec->out_pins[0], 0, + AC_VERB_GET_PIN_WIDGET_CONTROL, 0); + snd_hda_codec_write(codec, spec->out_pins[0], 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, + pin_ctl | 0x40); + } else { + snd_printdd(KERN_INFO "ca0132_select_out hp\n"); + /*headphone out config*/ + tmp = FLOAT_ZERO; + err = dspio_set_uint_param(codec, 0x80, 0x04, tmp); + if (err < 0) + goto exit; + /*disable speaker EQ*/ + tmp = FLOAT_ZERO; + err = dspio_set_uint_param(codec, 0x8f, 0x00, tmp); + if (err < 0) + goto exit; + + /* Setup EAPD */ + snd_hda_codec_write(codec, spec->out_pins[0], 0, + VENDOR_CHIPIO_EAPD_SEL_SET, 0x00); + snd_hda_codec_write(codec, spec->out_pins[0], 0, + AC_VERB_SET_EAPD_BTLENABLE, 0x00); + snd_hda_codec_write(codec, spec->out_pins[1], 0, + VENDOR_CHIPIO_EAPD_SEL_SET, 0x02); + snd_hda_codec_write(codec, spec->out_pins[0], 0, + AC_VERB_SET_EAPD_BTLENABLE, 0x02); + + /* disable speaker*/ + pin_ctl = snd_hda_codec_read(codec, spec->out_pins[0], 0, + AC_VERB_GET_PIN_WIDGET_CONTROL, 0); + snd_hda_codec_write(codec, spec->out_pins[0], 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, + pin_ctl & 0xBF); + /* enable headphone*/ + pin_ctl = snd_hda_codec_read(codec, spec->out_pins[1], 0, + AC_VERB_GET_PIN_WIDGET_CONTROL, 0); + snd_hda_codec_write(codec, spec->out_pins[1], 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, + pin_ctl | 0x40); + } + +exit: + snd_hda_power_down(codec); + + return err < 0 ? err : 0; +} + +static void ca0132_set_dmic(struct hda_codec *codec, int enable); +static int ca0132_mic_boost_set(struct hda_codec *codec, long val); +static int ca0132_effects_set(struct hda_codec *codec, hda_nid_t nid, long val); + +/* + * Select the active VIP source + */ +static int ca0132_set_vipsource(struct hda_codec *codec, int val) +{ + struct ca0132_spec *spec = codec->spec; + unsigned int tmp; + + if (!dspload_is_loaded(codec)) + return 0; + + /* if CrystalVoice if off, vipsource should be 0 */ + if (!spec->effects_switch[CRYSTAL_VOICE - EFFECT_START_NID] || + (val == 0)) { + chipio_set_control_param(codec, CONTROL_PARAM_VIP_SOURCE, 0); + chipio_set_conn_rate(codec, MEM_CONNID_MICIN1, SR_96_000); + chipio_set_conn_rate(codec, MEM_CONNID_MICOUT1, SR_96_000); + if (spec->cur_mic_type == DIGITAL_MIC) + tmp = FLOAT_TWO; + else + tmp = FLOAT_ONE; + dspio_set_uint_param(codec, 0x80, 0x00, tmp); + tmp = FLOAT_ZERO; + dspio_set_uint_param(codec, 0x80, 0x05, tmp); + } else { + chipio_set_conn_rate(codec, MEM_CONNID_MICIN1, SR_16_000); + chipio_set_conn_rate(codec, MEM_CONNID_MICOUT1, SR_16_000); + if (spec->cur_mic_type == DIGITAL_MIC) + tmp = FLOAT_TWO; + else + tmp = FLOAT_ONE; + dspio_set_uint_param(codec, 0x80, 0x00, tmp); + tmp = FLOAT_ONE; + dspio_set_uint_param(codec, 0x80, 0x05, tmp); + msleep(20); + chipio_set_control_param(codec, CONTROL_PARAM_VIP_SOURCE, val); + } + + return 1; +} + +/* + * Select the active microphone. + * If autodetect is enabled, mic will be selected based on jack detection. + * If jack inserted, ext.mic will be selected, else built-in mic + * If autodetect is disabled, mic will be selected based on selection. + */ +static int ca0132_select_mic(struct hda_codec *codec) +{ + struct ca0132_spec *spec = codec->spec; + int jack_present; + int auto_jack; + + snd_printdd(KERN_INFO "ca0132_select_mic\n"); + + snd_hda_power_up(codec); + + auto_jack = spec->vnode_lswitch[VNID_AMIC1_ASEL - VNODE_START_NID]; + + if (auto_jack) + jack_present = snd_hda_jack_detect(codec, spec->input_pins[0]); + else + jack_present = + spec->vnode_lswitch[VNID_AMIC1_SEL - VNODE_START_NID]; + + if (jack_present) + spec->cur_mic_type = LINE_MIC_IN; + else + spec->cur_mic_type = DIGITAL_MIC; + + if (spec->cur_mic_type == DIGITAL_MIC) { + /* enable digital Mic */ + chipio_set_conn_rate(codec, MEM_CONNID_DMIC, SR_32_000); + ca0132_set_dmic(codec, 1); + ca0132_mic_boost_set(codec, 0); + /* set voice focus */ + ca0132_effects_set(codec, VOICE_FOCUS, + spec->effects_switch + [VOICE_FOCUS - EFFECT_START_NID]); + } else { + /* disable digital Mic */ + chipio_set_conn_rate(codec, MEM_CONNID_DMIC, SR_96_000); + ca0132_set_dmic(codec, 0); + ca0132_mic_boost_set(codec, spec->cur_mic_boost); + /* disable voice focus */ + ca0132_effects_set(codec, VOICE_FOCUS, 0); + } + + snd_hda_power_down(codec); + + return 0; +} + +/* + * Set the effects parameters + */ +static int ca0132_effects_set(struct hda_codec *codec, hda_nid_t nid, long val) +{ + struct ca0132_spec *spec = codec->spec; + unsigned int on; + int num_fx = OUT_EFFECTS_COUNT + IN_EFFECTS_COUNT; + int err = 0; + int idx = nid - EFFECT_START_NID; + + if ((idx < 0) || (idx >= num_fx)) + return 0; /* no changed */ + + /* for out effect, qualify with PE */ + if ((nid >= OUT_EFFECT_START_NID) && (nid < OUT_EFFECT_END_NID)) { + /* if PE if off, turn off out effects. */ + if (!spec->effects_switch[PLAY_ENHANCEMENT - EFFECT_START_NID]) + val = 0; + } + + /* for in effect, qualify with CrystalVoice */ + if ((nid >= IN_EFFECT_START_NID) && (nid < IN_EFFECT_END_NID)) { + /* if CrystalVoice if off, turn off in effects. */ + if (!spec->effects_switch[CRYSTAL_VOICE - EFFECT_START_NID]) + val = 0; + + /* Voice Focus applies to 2-ch Mic, Digital Mic */ + if ((nid == VOICE_FOCUS) && (spec->cur_mic_type != DIGITAL_MIC)) + val = 0; + } + + snd_printdd(KERN_INFO, "ca0132_effect_set: nid=0x%x, val=%ld\n", + nid, val); + + on = (val == 0) ? FLOAT_ZERO : FLOAT_ONE; + err = dspio_set_uint_param(codec, ca0132_effects[idx].mid, + ca0132_effects[idx].reqs[0], on); + + if (err < 0) + return 0; /* no changed */ + + return 1; +} + +/* Check if Mic1 is streaming, if so, stop streaming */ +static int stop_mic1(struct hda_codec *codec) +{ + struct ca0132_spec *spec = codec->spec; + unsigned int oldval = snd_hda_codec_read(codec, spec->adcs[0], 0, + AC_VERB_GET_CONV, 0); + if (oldval != 0) + snd_hda_codec_write(codec, spec->adcs[0], 0, + AC_VERB_SET_CHANNEL_STREAMID, + 0); + return oldval; +} + +/* Resume Mic1 streaming if it was stopped. */ +static void resume_mic1(struct hda_codec *codec, unsigned int oldval) +{ + struct ca0132_spec *spec = codec->spec; + /* Restore the previous stream and channel */ + if (oldval != 0) + snd_hda_codec_write(codec, spec->adcs[0], 0, + AC_VERB_SET_CHANNEL_STREAMID, + oldval); +} + +/* + * Set Mic Boost + */ +static int ca0132_mic_boost_set(struct hda_codec *codec, long val) +{ + struct ca0132_spec *spec = codec->spec; + int ret = 0; + + if (val) /* on */ + ret = snd_hda_codec_amp_update(codec, spec->input_pins[0], 0, + HDA_INPUT, 0, HDA_AMP_VOLMASK, 3); + else /* off */ + ret = snd_hda_codec_amp_update(codec, spec->input_pins[0], 0, + HDA_INPUT, 0, HDA_AMP_VOLMASK, 0); + + return ret; +} + /* */ static struct hda_pcm_stream ca0132_pcm_analog_playback = { @@ -2892,7 +3230,7 @@ static int ca0132_hp_volume_put(struct snd_kcontrol *kcontrol, /* any change? */ if ((spec->curr_hp_volume[0] == left_vol) && - (spec->curr_hp_volume[1] == right_vol)) + (spec->curr_hp_volume[1] == right_vol)) return 0; snd_hda_power_up(codec); @@ -2925,7 +3263,7 @@ static int add_hp_switch(struct hda_codec *codec, hda_nid_t nid) { struct snd_kcontrol_new knew = HDA_CODEC_MUTE_MONO("Headphone Playback Switch", - nid, 1, 0, HDA_OUTPUT); + nid, 1, 0, HDA_OUTPUT); knew.get = ca0132_hp_switch_get; knew.put = ca0132_hp_switch_put; return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec)); @@ -2935,7 +3273,7 @@ static int add_hp_volume(struct hda_codec *codec, hda_nid_t nid) { struct snd_kcontrol_new knew = HDA_CODEC_VOLUME_MONO("Headphone Playback Volume", - nid, 3, 0, HDA_OUTPUT); + nid, 3, 0, HDA_OUTPUT); knew.get = ca0132_hp_volume_get; knew.put = ca0132_hp_volume_put; return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec)); @@ -2945,7 +3283,7 @@ static int add_speaker_switch(struct hda_codec *codec, hda_nid_t nid) { struct snd_kcontrol_new knew = HDA_CODEC_MUTE_MONO("Speaker Playback Switch", - nid, 1, 0, HDA_OUTPUT); + nid, 1, 0, HDA_OUTPUT); knew.get = ca0132_speaker_switch_get; knew.put = ca0132_speaker_switch_put; return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec)); @@ -3021,6 +3359,215 @@ static int ca0132_build_controls(struct hda_codec *codec) return 0; } +static void refresh_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir) +{ + unsigned int caps; + + caps = snd_hda_param_read(codec, nid, dir == HDA_OUTPUT ? + AC_PAR_AMP_OUT_CAP : AC_PAR_AMP_IN_CAP); + snd_hda_override_amp_caps(codec, nid, dir, caps); +} + +/* + * Switch between Digital built-in mic and analog mic. + */ +static void ca0132_set_dmic(struct hda_codec *codec, int enable) +{ + struct ca0132_spec *spec = codec->spec; + unsigned int tmp; + u8 val; + unsigned int oldval; + + snd_printdd(KERN_INFO "ca0132_set_dmic: enable=%d\n", enable); + + oldval = stop_mic1(codec); + ca0132_set_vipsource(codec, 0); + if (enable) { + /* set DMic input as 2-ch */ + tmp = FLOAT_TWO; + dspio_set_uint_param(codec, 0x80, 0x00, tmp); + + val = spec->dmic_ctl; + val |= 0x80; + snd_hda_codec_write(codec, spec->input_pins[0], 0, + VENDOR_CHIPIO_DMIC_CTL_SET, val); + + if (!(spec->dmic_ctl & 0x20)) + chipio_set_control_flag(codec, CONTROL_FLAG_DMIC, 1); + } else { + /* set AMic input as mono */ + tmp = FLOAT_ONE; + dspio_set_uint_param(codec, 0x80, 0x00, tmp); + + val = spec->dmic_ctl; + /* clear bit7 and bit5 to disable dmic */ + val &= 0x5f; + snd_hda_codec_write(codec, spec->input_pins[0], 0, + VENDOR_CHIPIO_DMIC_CTL_SET, val); + + if (!(spec->dmic_ctl & 0x20)) + chipio_set_control_flag(codec, CONTROL_FLAG_DMIC, 0); + } + ca0132_set_vipsource(codec, 1); + resume_mic1(codec, oldval); +} + +/* + * Initialization for Digital Mic. + */ +static void ca0132_init_dmic(struct hda_codec *codec) +{ + struct ca0132_spec *spec = codec->spec; + u8 val; + + /* Setup Digital Mic here, but don't enable. + * Enable based on jack detect. + */ + + /* MCLK uses MPIO1, set to enable. + * Bit 2-0: MPIO select + * Bit 3: set to disable + * Bit 7-4: reserved + */ + val = 0x01; + snd_hda_codec_write(codec, spec->input_pins[0], 0, + VENDOR_CHIPIO_DMIC_MCLK_SET, val); + + /* Data1 uses MPIO3. Data2 not use + * Bit 2-0: Data1 MPIO select + * Bit 3: set disable Data1 + * Bit 6-4: Data2 MPIO select + * Bit 7: set disable Data2 + */ + val = 0x83; + snd_hda_codec_write(codec, spec->input_pins[0], 0, + VENDOR_CHIPIO_DMIC_PIN_SET, val); + + /* Use Ch-0 and Ch-1. Rate is 48K, mode 1. Disable DMic first. + * Bit 3-0: Channel mask + * Bit 4: set for 48KHz, clear for 32KHz + * Bit 5: mode + * Bit 6: set to select Data2, clear for Data1 + * Bit 7: set to enable DMic, clear for AMic + */ + val = 0x23; + /* keep a copy of dmic ctl val for enable/disable dmic purpuse */ + spec->dmic_ctl = val; + snd_hda_codec_write(codec, spec->input_pins[0], 0, + VENDOR_CHIPIO_DMIC_CTL_SET, val); +} + +/* + * Initialization for Analog Mic 2 + */ +static void ca0132_init_analog_mic2(struct hda_codec *codec) +{ + struct ca0132_spec *spec = codec->spec; + + mutex_lock(&spec->chipio_mutex); + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_8051_ADDRESS_LOW, 0x20); + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_8051_ADDRESS_HIGH, 0x19); + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_8051_DATA_WRITE, 0x00); + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_8051_ADDRESS_LOW, 0x2D); + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_8051_ADDRESS_HIGH, 0x19); + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_8051_DATA_WRITE, 0x00); + mutex_unlock(&spec->chipio_mutex); +} + +static void ca0132_refresh_widget_caps(struct hda_codec *codec) +{ + struct ca0132_spec *spec = codec->spec; + int i; + hda_nid_t nid; + + snd_printdd(KERN_INFO "ca0132_refresh_widget_caps.\n"); + nid = codec->start_nid; + for (i = 0; i < codec->num_nodes; i++, nid++) + codec->wcaps[i] = snd_hda_param_read(codec, nid, + AC_PAR_AUDIO_WIDGET_CAP); + + for (i = 0; i < spec->multiout.num_dacs; i++) + refresh_amp_caps(codec, spec->dacs[i], HDA_OUTPUT); + + for (i = 0; i < spec->num_outputs; i++) + refresh_amp_caps(codec, spec->out_pins[i], HDA_OUTPUT); + + for (i = 0; i < spec->num_inputs; i++) { + refresh_amp_caps(codec, spec->adcs[i], HDA_INPUT); + refresh_amp_caps(codec, spec->input_pins[i], HDA_INPUT); + } +} + +/* + * Setup default parameters for DSP + */ +static void ca0132_setup_defaults(struct hda_codec *codec) +{ + unsigned int tmp; + int num_fx; + int idx, i; + + if (!dspload_is_loaded(codec)) + return; + + /* out, in effects + voicefx */ + num_fx = OUT_EFFECTS_COUNT + IN_EFFECTS_COUNT + 1; + for (idx = 0; idx < num_fx; idx++) { + for (i = 0; i <= ca0132_effects[idx].params; i++) { + dspio_set_uint_param(codec, ca0132_effects[idx].mid, + ca0132_effects[idx].reqs[i], + ca0132_effects[idx].def_vals[i]); + } + } + + /*remove DSP headroom*/ + tmp = FLOAT_ZERO; + dspio_set_uint_param(codec, 0x96, 0x3C, tmp); + + /*set speaker EQ bypass attenuation*/ + dspio_set_uint_param(codec, 0x8f, 0x01, tmp); + + /* set AMic1 and AMic2 as mono mic */ + tmp = FLOAT_ONE; + dspio_set_uint_param(codec, 0x80, 0x00, tmp); + dspio_set_uint_param(codec, 0x80, 0x01, tmp); + + /* set AMic1 as CrystalVoice input */ + tmp = FLOAT_ONE; + dspio_set_uint_param(codec, 0x80, 0x05, tmp); + + /* set WUH source */ + tmp = FLOAT_TWO; + dspio_set_uint_param(codec, 0x31, 0x00, tmp); +} + +/* + * Initialization of flags in chip + */ +static void ca0132_init_flags(struct hda_codec *codec) +{ + chipio_set_control_flag(codec, CONTROL_FLAG_IDLE_ENABLE, 0); + chipio_set_control_flag(codec, CONTROL_FLAG_PORT_A_COMMON_MODE, 0); + chipio_set_control_flag(codec, CONTROL_FLAG_PORT_D_COMMON_MODE, 0); + chipio_set_control_flag(codec, CONTROL_FLAG_PORT_A_10KOHM_LOAD, 0); + chipio_set_control_flag(codec, CONTROL_FLAG_PORT_D_10KOHM_LOAD, 0); + chipio_set_control_flag(codec, CONTROL_FLAG_ADC_C_HIGH_PASS, 1); +} + +/* + * Initialization of parameters in chip + */ +static void ca0132_init_params(struct hda_codec *codec) +{ + chipio_set_control_param(codec, CONTROL_PARAM_PORTA_160OHM_GAIN, 6); + chipio_set_control_param(codec, CONTROL_PARAM_PORTD_160OHM_GAIN, 6); +} static void ca0132_set_ct_ext(struct hda_codec *codec, int enable) { @@ -3038,7 +3585,6 @@ static void ca0132_config(struct hda_codec *codec) struct ca0132_spec *spec = codec->spec; struct auto_pin_cfg *cfg = &spec->autocfg; - codec->pcm_format_first = 1; codec->no_sticky_stream = 1; /* line-outs */ @@ -3088,16 +3634,115 @@ static void ca0132_config(struct hda_codec *codec) cfg->dig_in_type = HDA_PCM_TYPE_SPDIF; } +/* + * Verbs tables. + */ + +/* Sends before DSP download. */ +static struct hda_verb ca0132_base_init_verbs[] = { + /*enable ct extension*/ + {0x15, VENDOR_CHIPIO_CT_EXTENSIONS_ENABLE, 0x1}, + /*enable DSP node unsol, needed for DSP download*/ + {0x16, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | UNSOL_TAG_DSP}, + {} +}; + +/* Send at exit. */ +static struct hda_verb ca0132_base_exit_verbs[] = { + /*set afg to D3*/ + {0x01, AC_VERB_SET_POWER_STATE, 0x03}, + /*disable ct extension*/ + {0x15, VENDOR_CHIPIO_CT_EXTENSIONS_ENABLE, 0}, + {} +}; + +/* Other verbs tables. Sends after DSP download. */ +static struct hda_verb ca0132_init_verbs0[] = { + /* chip init verbs */ + {0x15, 0x70D, 0xF0}, + {0x15, 0x70E, 0xFE}, + {0x15, 0x707, 0x75}, + {0x15, 0x707, 0xD3}, + {0x15, 0x707, 0x09}, + {0x15, 0x707, 0x53}, + {0x15, 0x707, 0xD4}, + {0x15, 0x707, 0xEF}, + {0x15, 0x707, 0x75}, + {0x15, 0x707, 0xD3}, + {0x15, 0x707, 0x09}, + {0x15, 0x707, 0x02}, + {0x15, 0x707, 0x37}, + {0x15, 0x707, 0x78}, + {0x15, 0x53C, 0xCE}, + {0x15, 0x575, 0xC9}, + {0x15, 0x53D, 0xCE}, + {0x15, 0x5B7, 0xC9}, + {0x15, 0x70D, 0xE8}, + {0x15, 0x70E, 0xFE}, + {0x15, 0x707, 0x02}, + {0x15, 0x707, 0x68}, + {0x15, 0x707, 0x62}, + {0x15, 0x53A, 0xCE}, + {0x15, 0x546, 0xC9}, + {0x15, 0x53B, 0xCE}, + {0x15, 0x5E8, 0xC9}, + {0x15, 0x717, 0x0D}, + {0x15, 0x718, 0x20}, + {} +}; + +static struct hda_verb ca0132_init_verbs1[] = { + {0x10, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | UNSOL_TAG_HP}, + {0x12, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | UNSOL_TAG_AMIC1}, + /* config EAPD */ + {0x0b, 0x78D, 0x00}, + /*{0x0b, AC_VERB_SET_EAPD_BTLENABLE, 0x02},*/ + /*{0x10, 0x78D, 0x02},*/ + /*{0x10, AC_VERB_SET_EAPD_BTLENABLE, 0x02},*/ + {} +}; + static void ca0132_init_chip(struct hda_codec *codec) { struct ca0132_spec *spec = codec->spec; + int num_fx; + int i; + unsigned int on; mutex_init(&spec->chipio_mutex); + + spec->cur_out_type = SPEAKER_OUT; + spec->cur_mic_type = DIGITAL_MIC; + spec->cur_mic_boost = 0; + + for (i = 0; i < VNODES_COUNT; i++) { + spec->vnode_lvol[i] = 0x5a; + spec->vnode_rvol[i] = 0x5a; + spec->vnode_lswitch[i] = 0; + spec->vnode_rswitch[i] = 0; + } + + /* + * Default states for effects are in ca0132_effects[]. + */ + num_fx = OUT_EFFECTS_COUNT + IN_EFFECTS_COUNT; + for (i = 0; i < num_fx; i++) { + on = (unsigned int)ca0132_effects[i].reqs[0]; + spec->effects_switch[i] = on ? 1 : 0; + } + + spec->voicefx_val = 0; + spec->effects_switch[PLAY_ENHANCEMENT - EFFECT_START_NID] = 1; + spec->effects_switch[CRYSTAL_VOICE - EFFECT_START_NID] = 0; + } static void ca0132_exit_chip(struct hda_codec *codec) { /* put any chip cleanup stuffs here. */ + + if (dspload_is_loaded(codec)) + dsp_reset(codec); } static void ca0132_set_dsp_msr(struct hda_codec *codec, bool is96k) @@ -3155,15 +3800,25 @@ static int ca0132_init(struct hda_codec *codec) struct auto_pin_cfg *cfg = &spec->autocfg; int i; + spec->dsp_state = DSP_DOWNLOAD_INIT; + spec->curr_chip_addx = (unsigned int)INVALID_CHIP_ADDRESS; + + snd_hda_power_up(codec); + + ca0132_init_params(codec); + ca0132_init_flags(codec); + snd_hda_sequence_write(codec, spec->base_init_verbs); #ifdef CONFIG_SND_HDA_DSP_LOADER ca0132_download_dsp(codec); #endif + ca0132_refresh_widget_caps(codec); + ca0132_setup_defaults(codec); + ca0132_init_analog_mic2(codec); + ca0132_init_dmic(codec); + + for (i = 0; i < spec->num_outputs; i++) + init_output(codec, spec->out_pins[i], spec->dacs[0]); - for (i = 0; i < spec->multiout.num_dacs; i++) { - init_output(codec, spec->out_pins[i], - spec->multiout.dac_nids[i]); - } - init_output(codec, cfg->hp_pins[0], spec->hp_dac); init_output(codec, cfg->dig_out_pins[0], spec->dig_out); for (i = 0; i < spec->num_inputs; i++) @@ -3171,16 +3826,25 @@ static int ca0132_init(struct hda_codec *codec) init_input(codec, cfg->dig_in_pin, spec->dig_in); - ca0132_set_ct_ext(codec, 1); + for (i = 0; i < spec->num_init_verbs; i++) + snd_hda_sequence_write(codec, spec->init_verbs[i]); + + ca0132_select_out(codec); + ca0132_select_mic(codec); + + snd_hda_power_down(codec); return 0; } - static void ca0132_free(struct hda_codec *codec) { - ca0132_set_ct_ext(codec, 0); + struct ca0132_spec *spec = codec->spec; + + snd_hda_power_up(codec); + snd_hda_sequence_write(codec, spec->base_exit_verbs); ca0132_exit_chip(codec); + snd_hda_power_down(codec); kfree(codec->spec); } @@ -3191,8 +3855,6 @@ static struct hda_codec_ops ca0132_patch_ops = { .free = ca0132_free, }; - - static int patch_ca0132(struct hda_codec *codec) { struct ca0132_spec *spec; @@ -3204,6 +3866,12 @@ static int patch_ca0132(struct hda_codec *codec) return -ENOMEM; codec->spec = spec; + spec->base_init_verbs = ca0132_base_init_verbs; + spec->base_exit_verbs = ca0132_base_exit_verbs; + spec->init_verbs[0] = ca0132_init_verbs0; + spec->init_verbs[1] = ca0132_init_verbs1; + spec->num_init_verbs = 2; + ca0132_init_chip(codec); ca0132_config(codec); -- cgit v1.2.3 From a7e76271bdca5b85adb42fed05aae10ff6adeef3 Mon Sep 17 00:00:00 2001 From: Ian Minett Date: Thu, 20 Dec 2012 18:53:35 -0800 Subject: ALSA: hda/ca0132: Add DSP mixer controls and helpers This patch adds the kcontrols for the DSP effects, playback and recording source selection. ca0132_is_vnode_effective() checks whether virtual node settings have taken effect. The control change helpers ca0132_pe_switch_set(), ca0132_voicefx_set() and ca0132_cvoice_switch_set() are added to toggle playback / capture DSP effects, ca0132_voicefx_info(), _get() and _put() are added for input path DSP effect value access. The volume helpers are updated to volume_info(), _get() and _set() to use the virtual nodes. The redundant headphone and speaker switches and ct_extension function are removed. Signed-off-by: Ian Minett Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_ca0132.c | 832 ++++++++++++++++++++++++++++--------------- 1 file changed, 544 insertions(+), 288 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index e4e1684ace6f..91c4a5017282 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -710,49 +710,6 @@ static void init_input(struct hda_codec *codec, hda_nid_t pin, hda_nid_t adc) } } -static int _add_switch(struct hda_codec *codec, hda_nid_t nid, const char *pfx, - int chan, int dir) -{ - char namestr[44]; - int type = dir ? HDA_INPUT : HDA_OUTPUT; - struct snd_kcontrol_new knew = - HDA_CODEC_MUTE_MONO(namestr, nid, chan, 0, type); - if ((query_amp_caps(codec, nid, type) & AC_AMPCAP_MUTE) == 0) { - snd_printdd("Skipping '%s %s Switch' (no mute on node 0x%x)\n", pfx, dirstr[dir], nid); - return 0; - } - sprintf(namestr, "%s %s Switch", pfx, dirstr[dir]); - return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec)); -} - -static int _add_volume(struct hda_codec *codec, hda_nid_t nid, const char *pfx, - int chan, int dir) -{ - char namestr[44]; - int type = dir ? HDA_INPUT : HDA_OUTPUT; - struct snd_kcontrol_new knew = - HDA_CODEC_VOLUME_MONO(namestr, nid, chan, 0, type); - if ((query_amp_caps(codec, nid, type) & AC_AMPCAP_NUM_STEPS) == 0) { - snd_printdd("Skipping '%s %s Volume' (no amp on node 0x%x)\n", pfx, dirstr[dir], nid); - return 0; - } - sprintf(namestr, "%s %s Volume", pfx, dirstr[dir]); - return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec)); -} - -#define add_out_switch(codec, nid, pfx) _add_switch(codec, nid, pfx, 3, 0) -#define add_out_volume(codec, nid, pfx) _add_volume(codec, nid, pfx, 3, 0) -#define add_in_switch(codec, nid, pfx) _add_switch(codec, nid, pfx, 3, 1) -#define add_in_volume(codec, nid, pfx) _add_volume(codec, nid, pfx, 3, 1) -#define add_mono_switch(codec, nid, pfx, chan) \ - _add_switch(codec, nid, pfx, chan, 0) -#define add_mono_volume(codec, nid, pfx, chan) \ - _add_volume(codec, nid, pfx, chan, 0) -#define add_in_mono_switch(codec, nid, pfx, chan) \ - _add_switch(codec, nid, pfx, chan, 1) -#define add_in_mono_volume(codec, nid, pfx, chan) \ - _add_volume(codec, nid, pfx, chan, 1) - enum dsp_download_state { DSP_DOWNLOAD_FAILED = -1, DSP_DOWNLOAD_INIT = 0, @@ -771,6 +728,8 @@ enum dsp_download_state { */ struct ca0132_spec { + struct snd_kcontrol_new *mixers[5]; + unsigned int num_mixers; const struct hda_verb *base_init_verbs; const struct hda_verb *base_exit_verbs; const struct hda_verb *init_verbs[5]; @@ -781,17 +740,14 @@ struct ca0132_spec { struct hda_multi_out multiout; hda_nid_t out_pins[AUTO_CFG_MAX_OUTS]; hda_nid_t dacs[AUTO_CFG_MAX_OUTS]; - hda_nid_t hp_dac; unsigned int num_outputs; hda_nid_t input_pins[AUTO_PIN_LAST]; hda_nid_t adcs[AUTO_PIN_LAST]; hda_nid_t dig_out; hda_nid_t dig_in; unsigned int num_inputs; - long curr_hp_switch; - long curr_hp_volume[2]; - long curr_speaker_switch; - const char *input_labels[AUTO_PIN_LAST]; + hda_nid_t shared_mic_nid; + hda_nid_t shared_out_nid; struct hda_pcm pcm_rec[5]; /* PCM information */ /* chip access */ @@ -2949,6 +2905,59 @@ static int ca0132_select_mic(struct hda_codec *codec) return 0; } +/* + * Check if VNODE settings take effect immediately. + */ +static bool ca0132_is_vnode_effective(struct hda_codec *codec, + hda_nid_t vnid, + hda_nid_t *shared_nid) +{ + struct ca0132_spec *spec = codec->spec; + hda_nid_t nid; + bool effective = false; + + switch (vnid) { + case VNID_SPK: + nid = spec->shared_out_nid; + effective = true; + break; + case VNID_MIC: + nid = spec->shared_mic_nid; + effective = true; + break; + default: + break; + } + + if (effective && shared_nid) + *shared_nid = nid; + + return effective; +} + +/* +* The following functions are control change helpers. +* They return 0 if no changed. Return 1 if changed. +*/ +static int ca0132_voicefx_set(struct hda_codec *codec, int enable) +{ + struct ca0132_spec *spec = codec->spec; + unsigned int tmp; + + /* based on CrystalVoice state to enable VoiceFX. */ + if (enable) { + tmp = spec->effects_switch[CRYSTAL_VOICE - EFFECT_START_NID] ? + FLOAT_ONE : FLOAT_ZERO; + } else { + tmp = FLOAT_ZERO; + } + + dspio_set_uint_param(codec, ca0132_voicefx.mid, + ca0132_voicefx.reqs[0], tmp); + + return 1; +} + /* * Set the effects parameters */ @@ -2994,6 +3003,27 @@ static int ca0132_effects_set(struct hda_codec *codec, hda_nid_t nid, long val) return 1; } +/* + * Turn on/off Playback Enhancements + */ +static int ca0132_pe_switch_set(struct hda_codec *codec) +{ + struct ca0132_spec *spec = codec->spec; + hda_nid_t nid; + int i, ret = 0; + + snd_printdd(KERN_INFO "ca0132_pe_switch_set: val=%ld\n", + spec->effects_switch[PLAY_ENHANCEMENT - EFFECT_START_NID]); + + i = OUT_EFFECT_START_NID - EFFECT_START_NID; + nid = OUT_EFFECT_START_NID; + /* PE affects all out effects */ + for (; nid < OUT_EFFECT_END_NID; nid++, i++) + ret |= ca0132_effects_set(codec, nid, spec->effects_switch[i]); + + return ret; +} + /* Check if Mic1 is streaming, if so, stop streaming */ static int stop_mic1(struct hda_codec *codec) { @@ -3019,8 +3049,34 @@ static void resume_mic1(struct hda_codec *codec, unsigned int oldval) } /* - * Set Mic Boost + * Turn on/off CrystalVoice */ +static int ca0132_cvoice_switch_set(struct hda_codec *codec) +{ + struct ca0132_spec *spec = codec->spec; + hda_nid_t nid; + int i, ret = 0; + unsigned int oldval; + + snd_printdd(KERN_INFO "ca0132_cvoice_switch_set: val=%ld\n", + spec->effects_switch[CRYSTAL_VOICE - EFFECT_START_NID]); + + i = IN_EFFECT_START_NID - EFFECT_START_NID; + nid = IN_EFFECT_START_NID; + /* CrystalVoice affects all in effects */ + for (; nid < IN_EFFECT_END_NID; nid++, i++) + ret |= ca0132_effects_set(codec, nid, spec->effects_switch[i]); + + /* including VoiceFX */ + ret |= ca0132_voicefx_set(codec, (spec->voicefx_val ? 1 : 0)); + + /* set correct vipsource */ + oldval = stop_mic1(codec); + ret |= ca0132_set_vipsource(codec, 1); + resume_mic1(codec, oldval); + return ret; +} + static int ca0132_mic_boost_set(struct hda_codec *codec, long val) { struct ca0132_spec *spec = codec->spec; @@ -3036,309 +3092,532 @@ static int ca0132_mic_boost_set(struct hda_codec *codec, long val) return ret; } -/* - */ -static struct hda_pcm_stream ca0132_pcm_analog_playback = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, - .ops = { - .open = ca0132_playback_pcm_open, - .prepare = ca0132_playback_pcm_prepare, - .cleanup = ca0132_playback_pcm_cleanup - }, -}; - -static struct hda_pcm_stream ca0132_pcm_analog_capture = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, -}; - -static struct hda_pcm_stream ca0132_pcm_digital_playback = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, - .ops = { - .open = ca0132_dig_playback_pcm_open, - .close = ca0132_dig_playback_pcm_close, - .prepare = ca0132_dig_playback_pcm_prepare, - .cleanup = ca0132_dig_playback_pcm_cleanup - }, -}; - -static struct hda_pcm_stream ca0132_pcm_digital_capture = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, -}; - -static int ca0132_build_pcms(struct hda_codec *codec) +static int ca0132_vnode_switch_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + hda_nid_t nid = get_amp_nid(kcontrol); + hda_nid_t shared_nid = 0; + bool effective; + int ret = 0; struct ca0132_spec *spec = codec->spec; - struct hda_pcm *info = spec->pcm_rec; + int auto_jack; - codec->pcm_info = info; - codec->num_pcms = 0; + if (nid == VNID_HP_SEL) { + auto_jack = + spec->vnode_lswitch[VNID_HP_ASEL - VNODE_START_NID]; + if (!auto_jack) + ca0132_select_out(codec); + return 1; + } - info->name = "CA0132 Analog"; - info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ca0132_pcm_analog_playback; - info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->dacs[0]; - info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = - spec->multiout.max_channels; - info->stream[SNDRV_PCM_STREAM_CAPTURE] = ca0132_pcm_analog_capture; - info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = spec->num_inputs; - info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adcs[0]; - codec->num_pcms++; + if (nid == VNID_AMIC1_SEL) { + auto_jack = + spec->vnode_lswitch[VNID_AMIC1_ASEL - VNODE_START_NID]; + if (!auto_jack) + ca0132_select_mic(codec); + return 1; + } - if (!spec->dig_out && !spec->dig_in) - return 0; + if (nid == VNID_HP_ASEL) { + ca0132_select_out(codec); + return 1; + } - info++; - info->name = "CA0132 Digital"; - info->pcm_type = HDA_PCM_TYPE_SPDIF; - if (spec->dig_out) { - info->stream[SNDRV_PCM_STREAM_PLAYBACK] = - ca0132_pcm_digital_playback; - info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->dig_out; + if (nid == VNID_AMIC1_ASEL) { + ca0132_select_mic(codec); + return 1; } - if (spec->dig_in) { - info->stream[SNDRV_PCM_STREAM_CAPTURE] = - ca0132_pcm_digital_capture; - info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in; + + /* if effective conditions, then update hw immediately. */ + effective = ca0132_is_vnode_effective(codec, nid, &shared_nid); + if (effective) { + int dir = get_amp_direction(kcontrol); + int ch = get_amp_channels(kcontrol); + unsigned long pval; + + mutex_lock(&codec->control_mutex); + pval = kcontrol->private_value; + kcontrol->private_value = HDA_COMPOSE_AMP_VAL(shared_nid, ch, + 0, dir); + ret = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol); + kcontrol->private_value = pval; + mutex_unlock(&codec->control_mutex); } - codec->num_pcms++; - return 0; + return ret; } +/* End of control change helpers. */ -#define REG_CODEC_MUTE 0x18b014 -#define REG_CODEC_HP_VOL_L 0x18b070 -#define REG_CODEC_HP_VOL_R 0x18b074 +static int ca0132_voicefx_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + unsigned int items = sizeof(ca0132_voicefx_presets) + / sizeof(struct ct_voicefx_preset); + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = items; + if (uinfo->value.enumerated.item >= items) + uinfo->value.enumerated.item = items - 1; + strcpy(uinfo->value.enumerated.name, + ca0132_voicefx_presets[uinfo->value.enumerated.item].name); + return 0; +} -static int ca0132_hp_switch_get(struct snd_kcontrol *kcontrol, +static int ca0132_voicefx_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct ca0132_spec *spec = codec->spec; - long *valp = ucontrol->value.integer.value; - *valp = spec->curr_hp_switch; + ucontrol->value.enumerated.item[0] = spec->voicefx_val; return 0; } -static int ca0132_hp_switch_put(struct snd_kcontrol *kcontrol, +static int ca0132_voicefx_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct ca0132_spec *spec = codec->spec; - long *valp = ucontrol->value.integer.value; - unsigned int data; - int err; + int i, err = 0; + int sel = ucontrol->value.enumerated.item[0]; + unsigned int items = sizeof(ca0132_voicefx_presets) + / sizeof(struct ct_voicefx_preset); - /* any change? */ - if (spec->curr_hp_switch == *valp) + if (sel >= items) return 0; - snd_hda_power_up(codec); - - err = chipio_read(codec, REG_CODEC_MUTE, &data); - if (err < 0) - goto exit; + snd_printdd(KERN_INFO "ca0132_voicefx_put: sel=%d, preset=%s\n", + sel, ca0132_voicefx_presets[sel].name); - /* *valp 0 is mute, 1 is unmute */ - data = (data & 0x7f) | (*valp ? 0 : 0x80); - err = chipio_write(codec, REG_CODEC_MUTE, data); - if (err < 0) - goto exit; + /* + * Idx 0 is default. + * Default needs to qualify with CrystalVoice state. + */ + for (i = 0; i < VOICEFX_MAX_PARAM_COUNT; i++) { + err = dspio_set_uint_param(codec, ca0132_voicefx.mid, + ca0132_voicefx.reqs[i], + ca0132_voicefx_presets[sel].vals[i]); + if (err < 0) + break; + } - spec->curr_hp_switch = *valp; + if (err >= 0) { + spec->voicefx_val = sel; + /* enable voice fx */ + ca0132_voicefx_set(codec, (sel ? 1 : 0)); + } - exit: - snd_hda_power_down(codec); - return err < 0 ? err : 1; + return 1; } -static int ca0132_speaker_switch_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +static int ca0132_switch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct ca0132_spec *spec = codec->spec; + hda_nid_t nid = get_amp_nid(kcontrol); + int ch = get_amp_channels(kcontrol); long *valp = ucontrol->value.integer.value; - *valp = spec->curr_speaker_switch; + /* vnode */ + if ((nid >= VNODE_START_NID) && (nid < VNODE_END_NID)) { + if (ch & 1) { + *valp = spec->vnode_lswitch[nid - VNODE_START_NID]; + valp++; + } + if (ch & 2) { + *valp = spec->vnode_rswitch[nid - VNODE_START_NID]; + valp++; + } + return 0; + } + + /* effects, include PE and CrystalVoice */ + if ((nid >= EFFECT_START_NID) && (nid < EFFECT_END_NID)) { + *valp = spec->effects_switch[nid - EFFECT_START_NID]; + return 0; + } + + /* mic boost */ + if (nid == spec->input_pins[0]) { + *valp = spec->cur_mic_boost; + return 0; + } + return 0; } -static int ca0132_speaker_switch_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +static int ca0132_switch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct ca0132_spec *spec = codec->spec; + hda_nid_t nid = get_amp_nid(kcontrol); + int ch = get_amp_channels(kcontrol); long *valp = ucontrol->value.integer.value; - unsigned int data; - int err; + int changed = 1; - /* any change? */ - if (spec->curr_speaker_switch == *valp) - return 0; + snd_printdd(KERN_INFO "ca0132_switch_put: nid=0x%x, val=%ld\n", + nid, *valp); snd_hda_power_up(codec); + /* vnode */ + if ((nid >= VNODE_START_NID) && (nid < VNODE_END_NID)) { + if (ch & 1) { + spec->vnode_lswitch[nid - VNODE_START_NID] = *valp; + valp++; + } + if (ch & 2) { + spec->vnode_rswitch[nid - VNODE_START_NID] = *valp; + valp++; + } + changed = ca0132_vnode_switch_set(kcontrol, ucontrol); + goto exit; + } - err = chipio_read(codec, REG_CODEC_MUTE, &data); - if (err < 0) + /* PE */ + if (nid == PLAY_ENHANCEMENT) { + spec->effects_switch[nid - EFFECT_START_NID] = *valp; + changed = ca0132_pe_switch_set(codec); goto exit; + } - /* *valp 0 is mute, 1 is unmute */ - data = (data & 0xef) | (*valp ? 0 : 0x10); - err = chipio_write(codec, REG_CODEC_MUTE, data); - if (err < 0) + /* CrystalVoice */ + if (nid == CRYSTAL_VOICE) { + spec->effects_switch[nid - EFFECT_START_NID] = *valp; + changed = ca0132_cvoice_switch_set(codec); goto exit; + } - spec->curr_speaker_switch = *valp; + /* out and in effects */ + if (((nid >= OUT_EFFECT_START_NID) && (nid < OUT_EFFECT_END_NID)) || + ((nid >= IN_EFFECT_START_NID) && (nid < IN_EFFECT_END_NID))) { + spec->effects_switch[nid - EFFECT_START_NID] = *valp; + changed = ca0132_effects_set(codec, nid, *valp); + goto exit; + } + + /* mic boost */ + if (nid == spec->input_pins[0]) { + spec->cur_mic_boost = *valp; + + /* Mic boost does not apply to Digital Mic */ + if (spec->cur_mic_type != DIGITAL_MIC) + changed = ca0132_mic_boost_set(codec, *valp); + goto exit; + } - exit: +exit: snd_hda_power_down(codec); - return err < 0 ? err : 1; + return changed; } -static int ca0132_hp_volume_get(struct snd_kcontrol *kcontrol, +/* + * Volume related + */ +static int ca0132_volume_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct ca0132_spec *spec = codec->spec; + hda_nid_t nid = get_amp_nid(kcontrol); + int ch = get_amp_channels(kcontrol); + int dir = get_amp_direction(kcontrol); + unsigned long pval; + int err; + + switch (nid) { + case VNID_SPK: + /* follow shared_out info */ + nid = spec->shared_out_nid; + mutex_lock(&codec->control_mutex); + pval = kcontrol->private_value; + kcontrol->private_value = HDA_COMPOSE_AMP_VAL(nid, ch, 0, dir); + err = snd_hda_mixer_amp_volume_info(kcontrol, uinfo); + kcontrol->private_value = pval; + mutex_unlock(&codec->control_mutex); + break; + case VNID_MIC: + /* follow shared_mic info */ + nid = spec->shared_mic_nid; + mutex_lock(&codec->control_mutex); + pval = kcontrol->private_value; + kcontrol->private_value = HDA_COMPOSE_AMP_VAL(nid, ch, 0, dir); + err = snd_hda_mixer_amp_volume_info(kcontrol, uinfo); + kcontrol->private_value = pval; + mutex_unlock(&codec->control_mutex); + break; + default: + err = snd_hda_mixer_amp_volume_info(kcontrol, uinfo); + } + return err; +} + +static int ca0132_volume_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct ca0132_spec *spec = codec->spec; + hda_nid_t nid = get_amp_nid(kcontrol); + int ch = get_amp_channels(kcontrol); long *valp = ucontrol->value.integer.value; - *valp++ = spec->curr_hp_volume[0]; - *valp = spec->curr_hp_volume[1]; + /* store the left and right volume */ + if (ch & 1) { + *valp = spec->vnode_lvol[nid - VNODE_START_NID]; + valp++; + } + if (ch & 2) { + *valp = spec->vnode_rvol[nid - VNODE_START_NID]; + valp++; + } return 0; } -static int ca0132_hp_volume_put(struct snd_kcontrol *kcontrol, +static int ca0132_volume_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct ca0132_spec *spec = codec->spec; + hda_nid_t nid = get_amp_nid(kcontrol); + int ch = get_amp_channels(kcontrol); long *valp = ucontrol->value.integer.value; - long left_vol, right_vol; - unsigned int data; - int val; - int err; - - left_vol = *valp++; - right_vol = *valp; - - /* any change? */ - if ((spec->curr_hp_volume[0] == left_vol) && - (spec->curr_hp_volume[1] == right_vol)) - return 0; - - snd_hda_power_up(codec); - - err = chipio_read(codec, REG_CODEC_HP_VOL_L, &data); - if (err < 0) - goto exit; - - val = 31 - left_vol; - data = (data & 0xe0) | val; - err = chipio_write(codec, REG_CODEC_HP_VOL_L, data); - if (err < 0) - goto exit; - - val = 31 - right_vol; - data = (data & 0xe0) | val; - err = chipio_write(codec, REG_CODEC_HP_VOL_R, data); - if (err < 0) - goto exit; + hda_nid_t shared_nid = 0; + bool effective; + int changed = 1; + + /* store the left and right volume */ + if (ch & 1) { + spec->vnode_lvol[nid - VNODE_START_NID] = *valp; + valp++; + } + if (ch & 2) { + spec->vnode_rvol[nid - VNODE_START_NID] = *valp; + valp++; + } - spec->curr_hp_volume[0] = left_vol; - spec->curr_hp_volume[1] = right_vol; + /* if effective conditions, then update hw immediately. */ + effective = ca0132_is_vnode_effective(codec, nid, &shared_nid); + if (effective) { + int dir = get_amp_direction(kcontrol); + unsigned long pval; + + snd_hda_power_up(codec); + mutex_lock(&codec->control_mutex); + pval = kcontrol->private_value; + kcontrol->private_value = HDA_COMPOSE_AMP_VAL(shared_nid, ch, + 0, dir); + changed = snd_hda_mixer_amp_volume_put(kcontrol, ucontrol); + kcontrol->private_value = pval; + mutex_unlock(&codec->control_mutex); + snd_hda_power_down(codec); + } - exit: - snd_hda_power_down(codec); - return err < 0 ? err : 1; + return changed; } -static int add_hp_switch(struct hda_codec *codec, hda_nid_t nid) +static int ca0132_volume_tlv(struct snd_kcontrol *kcontrol, int op_flag, + unsigned int size, unsigned int __user *tlv) { - struct snd_kcontrol_new knew = - HDA_CODEC_MUTE_MONO("Headphone Playback Switch", - nid, 1, 0, HDA_OUTPUT); - knew.get = ca0132_hp_switch_get; - knew.put = ca0132_hp_switch_put; - return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec)); + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct ca0132_spec *spec = codec->spec; + hda_nid_t nid = get_amp_nid(kcontrol); + int ch = get_amp_channels(kcontrol); + int dir = get_amp_direction(kcontrol); + unsigned long pval; + int err; + + switch (nid) { + case VNID_SPK: + /* follow shared_out tlv */ + nid = spec->shared_out_nid; + mutex_lock(&codec->control_mutex); + pval = kcontrol->private_value; + kcontrol->private_value = HDA_COMPOSE_AMP_VAL(nid, ch, 0, dir); + err = snd_hda_mixer_amp_tlv(kcontrol, op_flag, size, tlv); + kcontrol->private_value = pval; + mutex_unlock(&codec->control_mutex); + break; + case VNID_MIC: + /* follow shared_mic tlv */ + nid = spec->shared_mic_nid; + mutex_lock(&codec->control_mutex); + pval = kcontrol->private_value; + kcontrol->private_value = HDA_COMPOSE_AMP_VAL(nid, ch, 0, dir); + err = snd_hda_mixer_amp_tlv(kcontrol, op_flag, size, tlv); + kcontrol->private_value = pval; + mutex_unlock(&codec->control_mutex); + break; + default: + err = snd_hda_mixer_amp_tlv(kcontrol, op_flag, size, tlv); + } + return err; } -static int add_hp_volume(struct hda_codec *codec, hda_nid_t nid) +static int add_fx_switch(struct hda_codec *codec, hda_nid_t nid, + const char *pfx, int dir) { + char namestr[44]; + int type = dir ? HDA_INPUT : HDA_OUTPUT; struct snd_kcontrol_new knew = - HDA_CODEC_VOLUME_MONO("Headphone Playback Volume", - nid, 3, 0, HDA_OUTPUT); - knew.get = ca0132_hp_volume_get; - knew.put = ca0132_hp_volume_put; + CA0132_CODEC_MUTE_MONO(namestr, nid, 1, type); + sprintf(namestr, "%s %s Switch", pfx, dirstr[dir]); return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec)); } -static int add_speaker_switch(struct hda_codec *codec, hda_nid_t nid) +static int add_voicefx(struct hda_codec *codec) { struct snd_kcontrol_new knew = - HDA_CODEC_MUTE_MONO("Speaker Playback Switch", - nid, 1, 0, HDA_OUTPUT); - knew.get = ca0132_speaker_switch_get; - knew.put = ca0132_speaker_switch_put; - return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec)); + HDA_CODEC_MUTE_MONO(ca0132_voicefx.name, + VOICEFX, 1, 0, HDA_INPUT); + knew.info = ca0132_voicefx_info; + knew.get = ca0132_voicefx_get; + knew.put = ca0132_voicefx_put; + return snd_hda_ctl_add(codec, VOICEFX, snd_ctl_new1(&knew, codec)); } -static void ca0132_fix_hp_caps(struct hda_codec *codec) +/* + * When changing Node IDs for Mixer Controls below, make sure to update + * Node IDs in ca0132_config() as well. + */ +static struct snd_kcontrol_new ca0132_mixer[] = { + CA0132_CODEC_VOL("Master Playback Volume", VNID_SPK, HDA_OUTPUT), + CA0132_CODEC_MUTE("Master Playback Switch", VNID_SPK, HDA_OUTPUT), + CA0132_CODEC_VOL("Capture Volume", VNID_MIC, HDA_INPUT), + CA0132_CODEC_MUTE("Capture Switch", VNID_MIC, HDA_INPUT), + HDA_CODEC_VOLUME("Analog-Mic2 Capture Volume", 0x08, 0, HDA_INPUT), + HDA_CODEC_MUTE("Analog-Mic2 Capture Switch", 0x08, 0, HDA_INPUT), + HDA_CODEC_VOLUME("What U Hear Capture Volume", 0x0a, 0, HDA_INPUT), + HDA_CODEC_MUTE("What U Hear Capture Switch", 0x0a, 0, HDA_INPUT), + CA0132_CODEC_MUTE_MONO("Mic1-Boost (30dB) Capture Switch", + 0x12, 1, HDA_INPUT), + CA0132_CODEC_MUTE_MONO("HP/Speaker Playback Switch", + VNID_HP_SEL, 1, HDA_OUTPUT), + CA0132_CODEC_MUTE_MONO("AMic1/DMic Capture Switch", + VNID_AMIC1_SEL, 1, HDA_INPUT), + CA0132_CODEC_MUTE_MONO("HP/Speaker Auto Detect Playback Switch", + VNID_HP_ASEL, 1, HDA_OUTPUT), + CA0132_CODEC_MUTE_MONO("AMic1/DMic Auto Detect Capture Switch", + VNID_AMIC1_ASEL, 1, HDA_INPUT), + { } /* end */ +}; + +/* + */ +static struct hda_pcm_stream ca0132_pcm_analog_playback = { + .substreams = 1, + .channels_min = 2, + .channels_max = 2, + .ops = { + .open = ca0132_playback_pcm_open, + .prepare = ca0132_playback_pcm_prepare, + .cleanup = ca0132_playback_pcm_cleanup + }, +}; + +static struct hda_pcm_stream ca0132_pcm_analog_capture = { + .substreams = 1, + .channels_min = 2, + .channels_max = 2, +}; + +static struct hda_pcm_stream ca0132_pcm_digital_playback = { + .substreams = 1, + .channels_min = 2, + .channels_max = 2, + .ops = { + .open = ca0132_dig_playback_pcm_open, + .close = ca0132_dig_playback_pcm_close, + .prepare = ca0132_dig_playback_pcm_prepare, + .cleanup = ca0132_dig_playback_pcm_cleanup + }, +}; + +static struct hda_pcm_stream ca0132_pcm_digital_capture = { + .substreams = 1, + .channels_min = 2, + .channels_max = 2, +}; + +static int ca0132_build_pcms(struct hda_codec *codec) { struct ca0132_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - unsigned int caps; + struct hda_pcm *info = spec->pcm_rec; + + codec->pcm_info = info; + codec->num_pcms = 0; + + info->name = "CA0132 Analog"; + info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ca0132_pcm_analog_playback; + info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->dacs[0]; + info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = + spec->multiout.max_channels; + info->stream[SNDRV_PCM_STREAM_CAPTURE] = ca0132_pcm_analog_capture; + info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = spec->num_inputs; + info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adcs[0]; + codec->num_pcms++; + + if (!spec->dig_out && !spec->dig_in) + return 0; + + info++; + info->name = "CA0132 Digital"; + info->pcm_type = HDA_PCM_TYPE_SPDIF; + if (spec->dig_out) { + info->stream[SNDRV_PCM_STREAM_PLAYBACK] = + ca0132_pcm_digital_playback; + info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->dig_out; + } + if (spec->dig_in) { + info->stream[SNDRV_PCM_STREAM_CAPTURE] = + ca0132_pcm_digital_capture; + info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in; + } + codec->num_pcms++; - /* set mute-capable, 1db step, 32 steps, ofs 6 */ - caps = 0x80031f06; - snd_hda_override_amp_caps(codec, cfg->hp_pins[0], HDA_OUTPUT, caps); + return 0; } static int ca0132_build_controls(struct hda_codec *codec) { struct ca0132_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - int i, err; + int i, num_fx; + int err = 0; - if (spec->multiout.num_dacs) { - err = add_speaker_switch(codec, spec->out_pins[0]); + /* Add Mixer controls */ + for (i = 0; i < spec->num_mixers; i++) { + err = snd_hda_add_new_ctls(codec, spec->mixers[i]); if (err < 0) return err; } - if (cfg->hp_outs) { - ca0132_fix_hp_caps(codec); - err = add_hp_switch(codec, cfg->hp_pins[0]); - if (err < 0) - return err; - err = add_hp_volume(codec, cfg->hp_pins[0]); + /* Add in and out effects controls. + * VoiceFX, PE and CrystalVoice are added separately. + */ + num_fx = OUT_EFFECTS_COUNT + IN_EFFECTS_COUNT; + for (i = 0; i < num_fx; i++) { + err = add_fx_switch(codec, ca0132_effects[i].nid, + ca0132_effects[i].name, + ca0132_effects[i].direct); if (err < 0) return err; } - for (i = 0; i < spec->num_inputs; i++) { - const char *label = spec->input_labels[i]; + err = add_fx_switch(codec, PLAY_ENHANCEMENT, "PlayEnhancement", 0); + if (err < 0) + return err; - err = add_in_switch(codec, spec->adcs[i], label); - if (err < 0) - return err; - err = add_in_volume(codec, spec->adcs[i], label); - if (err < 0) - return err; - if (cfg->inputs[i].type == AUTO_PIN_MIC) { - /* add Mic-Boost */ - err = add_in_mono_volume(codec, spec->input_pins[i], - "Mic Boost", 1); - if (err < 0) - return err; - } - } + err = add_fx_switch(codec, CRYSTAL_VOICE, "CrystalVoice", 1); + if (err < 0) + return err; + + add_voicefx(codec); + + err = snd_hda_jack_add_kctls(codec, &spec->autocfg); + if (err < 0) + return err; if (spec->dig_out) { err = snd_hda_create_spdif_out_ctls(codec, spec->dig_out, @@ -3569,59 +3848,33 @@ static void ca0132_init_params(struct hda_codec *codec) chipio_set_control_param(codec, CONTROL_PARAM_PORTD_160OHM_GAIN, 6); } -static void ca0132_set_ct_ext(struct hda_codec *codec, int enable) -{ - /* Set Creative extension */ - snd_printdd("SET CREATIVE EXTENSION\n"); - snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, - VENDOR_CHIPIO_CT_EXTENSIONS_ENABLE, - enable); - msleep(20); -} - - static void ca0132_config(struct hda_codec *codec) { struct ca0132_spec *spec = codec->spec; struct auto_pin_cfg *cfg = &spec->autocfg; - codec->no_sticky_stream = 1; - - /* line-outs */ - cfg->line_outs = 1; - cfg->line_out_pins[0] = 0x0b; /* front */ - cfg->line_out_type = AUTO_PIN_LINE_OUT; + spec->dacs[0] = 0x2; + spec->dacs[1] = 0x3; + spec->dacs[2] = 0x4; - spec->dacs[0] = 0x02; - spec->out_pins[0] = 0x0b; spec->multiout.dac_nids = spec->dacs; - spec->multiout.num_dacs = 1; + spec->multiout.num_dacs = 3; spec->multiout.max_channels = 2; - /* headphone */ - cfg->hp_outs = 1; - cfg->hp_pins[0] = 0x0f; - - spec->hp_dac = 0; - spec->multiout.hp_nid = 0; + spec->num_outputs = 2; + spec->out_pins[0] = 0x0b; /* speaker out */ + spec->out_pins[1] = 0x10; /* headphone out */ + spec->shared_out_nid = 0x2; - /* inputs */ - cfg->num_inputs = 2; /* Mic-in and line-in */ - cfg->inputs[0].pin = 0x12; - cfg->inputs[0].type = AUTO_PIN_MIC; - cfg->inputs[1].pin = 0x11; - cfg->inputs[1].type = AUTO_PIN_LINE_IN; + spec->num_inputs = 3; + spec->adcs[0] = 0x7; /* digital mic / analog mic1 */ + spec->adcs[1] = 0x8; /* analog mic2 */ + spec->adcs[2] = 0xa; /* what u hear */ + spec->shared_mic_nid = 0x7; - /* Mic-in */ spec->input_pins[0] = 0x12; - spec->input_labels[0] = "Mic"; - spec->adcs[0] = 0x07; - - /* Line-In */ spec->input_pins[1] = 0x11; - spec->input_labels[1] = "Line"; - spec->adcs[1] = 0x08; - spec->num_inputs = 2; + spec->input_pins[2] = 0x13; /* SPDIF I/O */ spec->dig_out = 0x05; @@ -3866,6 +4119,9 @@ static int patch_ca0132(struct hda_codec *codec) return -ENOMEM; codec->spec = spec; + spec->num_mixers = 1; + spec->mixers[0] = ca0132_mixer; + spec->base_init_verbs = ca0132_base_init_verbs; spec->base_exit_verbs = ca0132_base_exit_verbs; spec->init_verbs[0] = ca0132_init_verbs0; -- cgit v1.2.3 From 825315bc5b5c33e5af5124ff100ef05a30ad722f Mon Sep 17 00:00:00 2001 From: Ian Minett Date: Thu, 20 Dec 2012 18:53:36 -0800 Subject: ALSA: hda/ca0132: Add PCM enhancements Remove the playback PCM open callback. PCM stream setup and cleanup functions are added for use by PCM callbacks. Delay stream cleanup if effects are on, to allow time for any effects tail to finish. Add the analog capture PCM callbacks. Change the max channels of analog playback to 6. Add two new PCMs: AMic2 and What-U-Hear. Signed-off-by: Ian Minett Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_ca0132.c | 131 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 118 insertions(+), 13 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index 91c4a5017282..748fca78131c 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -2630,17 +2630,62 @@ static bool dspload_wait_loaded(struct hda_codec *codec) CA0132_CODEC_MUTE_MONO(xname, nid, 3, dir) /* - * PCM callbacks + * PCM stuffs */ -static int ca0132_playback_pcm_open(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) +static void ca0132_setup_stream(struct hda_codec *codec, hda_nid_t nid, + u32 stream_tag, + int channel_id, int format) { - struct ca0132_spec *spec = codec->spec; - return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream, - hinfo); + unsigned int oldval, newval; + + if (!nid) + return; + + snd_printdd( + "ca0132_setup_stream: NID=0x%x, stream=0x%x, " + "channel=%d, format=0x%x\n", + nid, stream_tag, channel_id, format); + + /* update the format-id if changed */ + oldval = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_STREAM_FORMAT, + 0); + if (oldval != format) { + msleep(20); + snd_hda_codec_write(codec, nid, 0, + AC_VERB_SET_STREAM_FORMAT, + format); + } + + oldval = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0); + newval = (stream_tag << 4) | channel_id; + if (oldval != newval) { + snd_hda_codec_write(codec, nid, 0, + AC_VERB_SET_CHANNEL_STREAMID, + newval); + } +} + +static void ca0132_cleanup_stream(struct hda_codec *codec, hda_nid_t nid) +{ + unsigned int val; + + if (!nid) + return; + + snd_printdd(KERN_INFO "ca0132_cleanup_stream: NID=0x%x\n", nid); + + val = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0); + if (!val) + return; + + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_STREAM_FORMAT, 0); + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CHANNEL_STREAMID, 0); } +/* + * PCM callbacks + */ static int ca0132_playback_pcm_prepare(struct hda_pcm_stream *hinfo, struct hda_codec *codec, unsigned int stream_tag, @@ -2648,8 +2693,10 @@ static int ca0132_playback_pcm_prepare(struct hda_pcm_stream *hinfo, struct snd_pcm_substream *substream) { struct ca0132_spec *spec = codec->spec; - return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, - stream_tag, format, substream); + + ca0132_setup_stream(codec, spec->dacs[0], stream_tag, 0, format); + + return 0; } static int ca0132_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, @@ -2657,7 +2704,18 @@ static int ca0132_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, struct snd_pcm_substream *substream) { struct ca0132_spec *spec = codec->spec; - return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout); + + if (spec->dsp_state == DSP_DOWNLOADING) + return 0; + + /*If Playback effects are on, allow stream some time to flush + *effects tail*/ + if (spec->effects_switch[PLAY_ENHANCEMENT - EFFECT_START_NID]) + msleep(50); + + ca0132_cleanup_stream(codec, spec->dacs[0]); + + return 0; } /* @@ -2698,6 +2756,36 @@ static int ca0132_dig_playback_pcm_close(struct hda_pcm_stream *hinfo, return snd_hda_multi_out_dig_close(codec, &spec->multiout); } +/* + * Analog capture + */ +static int ca0132_capture_pcm_prepare(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + unsigned int stream_tag, + unsigned int format, + struct snd_pcm_substream *substream) +{ + struct ca0132_spec *spec = codec->spec; + + ca0132_setup_stream(codec, spec->adcs[substream->number], + stream_tag, 0, format); + + return 0; +} + +static int ca0132_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct ca0132_spec *spec = codec->spec; + + if (spec->dsp_state == DSP_DOWNLOADING) + return 0; + + ca0132_cleanup_stream(codec, hinfo->nid); + return 0; +} + /* * Select the active output. * If autodetect is enabled, output will be selected based on jack detection. @@ -3509,9 +3597,8 @@ static struct snd_kcontrol_new ca0132_mixer[] = { static struct hda_pcm_stream ca0132_pcm_analog_playback = { .substreams = 1, .channels_min = 2, - .channels_max = 2, + .channels_max = 6, .ops = { - .open = ca0132_playback_pcm_open, .prepare = ca0132_playback_pcm_prepare, .cleanup = ca0132_playback_pcm_cleanup }, @@ -3521,6 +3608,10 @@ static struct hda_pcm_stream ca0132_pcm_analog_capture = { .substreams = 1, .channels_min = 2, .channels_max = 2, + .ops = { + .prepare = ca0132_capture_pcm_prepare, + .cleanup = ca0132_capture_pcm_cleanup + }, }; static struct hda_pcm_stream ca0132_pcm_digital_playback = { @@ -3555,10 +3646,24 @@ static int ca0132_build_pcms(struct hda_codec *codec) info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = spec->multiout.max_channels; info->stream[SNDRV_PCM_STREAM_CAPTURE] = ca0132_pcm_analog_capture; - info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = spec->num_inputs; + info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = 1; info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adcs[0]; codec->num_pcms++; + info++; + info->name = "CA0132 Analog Mic-In2"; + info->stream[SNDRV_PCM_STREAM_CAPTURE] = ca0132_pcm_analog_capture; + info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = 1; + info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adcs[1]; + codec->num_pcms++; + + info++; + info->name = "CA0132 What U Hear"; + info->stream[SNDRV_PCM_STREAM_CAPTURE] = ca0132_pcm_analog_capture; + info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = 1; + info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adcs[2]; + codec->num_pcms++; + if (!spec->dig_out && !spec->dig_in) return 0; -- cgit v1.2.3 From a73d511c4867c5aa75a9ab50f7e73d5086c48cda Mon Sep 17 00:00:00 2001 From: Ian Minett Date: Thu, 20 Dec 2012 18:53:37 -0800 Subject: ALSA: hda/ca0132: Add unsol handler for DSP and jack detection This patch adds the unsolicited response handler for incoming DSP responses and jack detection reporting, and routines for reading the incoming DSP response. Signed-off-by: Ian Minett Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_ca0132.c | 142 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 142 insertions(+) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index 748fca78131c..9ea5660e1a3a 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -1172,6 +1172,59 @@ static int dspio_write_multiple(struct hda_codec *codec, return status; } +static int dspio_read(struct hda_codec *codec, unsigned int *data) +{ + int status; + + status = dspio_send(codec, VENDOR_DSPIO_SCP_POST_READ_DATA, 0); + if (status == -EIO) + return status; + + status = dspio_send(codec, VENDOR_DSPIO_STATUS, 0); + if (status == -EIO || + status == VENDOR_STATUS_DSPIO_SCP_RESPONSE_QUEUE_EMPTY) + return -EIO; + + *data = snd_hda_codec_read(codec, WIDGET_DSP_CTRL, 0, + VENDOR_DSPIO_SCP_READ_DATA, 0); + + return 0; +} + +static int dspio_read_multiple(struct hda_codec *codec, unsigned int *buffer, + unsigned int *buf_size, unsigned int size_count) +{ + int status = 0; + unsigned int size = *buf_size; + unsigned int count; + unsigned int skip_count; + unsigned int dummy; + + if ((buffer == NULL)) + return -1; + + count = 0; + while (count < size && count < size_count) { + status = dspio_read(codec, buffer++); + if (status != 0) + break; + count++; + } + + skip_count = count; + if (status == 0) { + while (skip_count < size) { + status = dspio_read(codec, &dummy); + if (status != 0) + break; + skip_count++; + } + } + *buf_size = count; + + return status; +} + /* * Construct the SCP header using corresponding fields */ @@ -1231,6 +1284,38 @@ struct scp_msg { unsigned int data[SCP_MAX_DATA_WORDS]; }; +static void dspio_clear_response_queue(struct hda_codec *codec) +{ + unsigned int dummy = 0; + int status = -1; + + /* clear all from the response queue */ + do { + status = dspio_read(codec, &dummy); + } while (status == 0); +} + +static int dspio_get_response_data(struct hda_codec *codec) +{ + struct ca0132_spec *spec = codec->spec; + unsigned int data = 0; + unsigned int count; + + if (dspio_read(codec, &data) < 0) + return -EIO; + + if ((data & 0x00ffffff) == spec->wait_scp_header) { + spec->scp_resp_header = data; + spec->scp_resp_count = data >> 27; + count = spec->wait_num_data; + dspio_read_multiple(codec, spec->scp_resp_data, + &spec->scp_resp_count, count); + return 0; + } + + return -EIO; +} + /* * Send SCP message to DSP */ @@ -3743,6 +3828,12 @@ static int ca0132_build_controls(struct hda_codec *codec) return 0; } +static void ca0132_init_unsol(struct hda_codec *codec) +{ + snd_hda_jack_detect_enable(codec, UNSOL_TAG_HP, UNSOL_TAG_HP); + snd_hda_jack_detect_enable(codec, UNSOL_TAG_AMIC1, UNSOL_TAG_AMIC1); +} + static void refresh_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir) { unsigned int caps; @@ -4152,6 +4243,47 @@ static void ca0132_download_dsp(struct hda_codec *codec) ca0132_set_dsp_msr(codec, true); } +static void ca0132_process_dsp_response(struct hda_codec *codec) +{ + struct ca0132_spec *spec = codec->spec; + + snd_printdd(KERN_INFO "ca0132_process_dsp_response\n"); + if (spec->wait_scp) { + if (dspio_get_response_data(codec) >= 0) + spec->wait_scp = 0; + } + + dspio_clear_response_queue(codec); +} + +static void ca0132_unsol_event(struct hda_codec *codec, unsigned int res) +{ + snd_printdd(KERN_INFO "ca0132_unsol_event: 0x%x\n", res); + + + if (((res >> AC_UNSOL_RES_TAG_SHIFT) & 0x3f) == UNSOL_TAG_DSP) { + ca0132_process_dsp_response(codec); + } else { + res = snd_hda_jack_get_action(codec, + (res >> AC_UNSOL_RES_TAG_SHIFT) & 0x3f); + + snd_printdd(KERN_INFO "snd_hda_jack_get_action: 0x%x\n", res); + + switch (res) { + case UNSOL_TAG_HP: + ca0132_select_out(codec); + snd_hda_jack_report_sync(codec); + break; + case UNSOL_TAG_AMIC1: + ca0132_select_mic(codec); + snd_hda_jack_report_sync(codec); + break; + default: + break; + } + } +} + static int ca0132_init(struct hda_codec *codec) { struct ca0132_spec *spec = codec->spec; @@ -4187,9 +4319,13 @@ static int ca0132_init(struct hda_codec *codec) for (i = 0; i < spec->num_init_verbs; i++) snd_hda_sequence_write(codec, spec->init_verbs[i]); + ca0132_init_unsol(codec); + ca0132_select_out(codec); ca0132_select_mic(codec); + snd_hda_jack_report_sync(codec); + snd_hda_power_down(codec); return 0; @@ -4211,11 +4347,13 @@ static struct hda_codec_ops ca0132_patch_ops = { .build_pcms = ca0132_build_pcms, .init = ca0132_init, .free = ca0132_free, + .unsol_event = ca0132_unsol_event, }; static int patch_ca0132(struct hda_codec *codec) { struct ca0132_spec *spec; + int err; snd_printdd("patch_ca0132\n"); @@ -4237,6 +4375,10 @@ static int patch_ca0132(struct hda_codec *codec) ca0132_config(codec); + err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); + if (err < 0) + return err; + codec->patch_ops = ca0132_patch_ops; return 0; -- cgit v1.2.3 From 44f0c9782cc6ab71ea947f8f710a46f2078a151c Mon Sep 17 00:00:00 2001 From: Ian Minett Date: Thu, 20 Dec 2012 18:53:38 -0800 Subject: ALSA: hda/ca0132: Add tuning controls This patch adds the controls used for tuning the DSP effects. Signed-off-by: Ian Minett Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_ca0132.c | 289 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 289 insertions(+) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index 9ea5660e1a3a..c1391f43cc61 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -775,6 +775,10 @@ struct ca0132_spec { long effects_switch[EFFECTS_COUNT]; long voicefx_val; long cur_mic_boost; + +#ifdef ENABLE_TUNING_CONTROLS + long cur_ctl_vals[TUNING_CTLS_COUNT]; +#endif }; /* @@ -2871,6 +2875,284 @@ static int ca0132_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, return 0; } +/* The followings are for tuning of products */ +#ifdef ENABLE_TUNING_CONTROLS + +static unsigned int voice_focus_vals_lookup[] = { +0x41A00000, 0x41A80000, 0x41B00000, 0x41B80000, 0x41C00000, 0x41C80000, +0x41D00000, 0x41D80000, 0x41E00000, 0x41E80000, 0x41F00000, 0x41F80000, +0x42000000, 0x42040000, 0x42080000, 0x420C0000, 0x42100000, 0x42140000, +0x42180000, 0x421C0000, 0x42200000, 0x42240000, 0x42280000, 0x422C0000, +0x42300000, 0x42340000, 0x42380000, 0x423C0000, 0x42400000, 0x42440000, +0x42480000, 0x424C0000, 0x42500000, 0x42540000, 0x42580000, 0x425C0000, +0x42600000, 0x42640000, 0x42680000, 0x426C0000, 0x42700000, 0x42740000, +0x42780000, 0x427C0000, 0x42800000, 0x42820000, 0x42840000, 0x42860000, +0x42880000, 0x428A0000, 0x428C0000, 0x428E0000, 0x42900000, 0x42920000, +0x42940000, 0x42960000, 0x42980000, 0x429A0000, 0x429C0000, 0x429E0000, +0x42A00000, 0x42A20000, 0x42A40000, 0x42A60000, 0x42A80000, 0x42AA0000, +0x42AC0000, 0x42AE0000, 0x42B00000, 0x42B20000, 0x42B40000, 0x42B60000, +0x42B80000, 0x42BA0000, 0x42BC0000, 0x42BE0000, 0x42C00000, 0x42C20000, +0x42C40000, 0x42C60000, 0x42C80000, 0x42CA0000, 0x42CC0000, 0x42CE0000, +0x42D00000, 0x42D20000, 0x42D40000, 0x42D60000, 0x42D80000, 0x42DA0000, +0x42DC0000, 0x42DE0000, 0x42E00000, 0x42E20000, 0x42E40000, 0x42E60000, +0x42E80000, 0x42EA0000, 0x42EC0000, 0x42EE0000, 0x42F00000, 0x42F20000, +0x42F40000, 0x42F60000, 0x42F80000, 0x42FA0000, 0x42FC0000, 0x42FE0000, +0x43000000, 0x43010000, 0x43020000, 0x43030000, 0x43040000, 0x43050000, +0x43060000, 0x43070000, 0x43080000, 0x43090000, 0x430A0000, 0x430B0000, +0x430C0000, 0x430D0000, 0x430E0000, 0x430F0000, 0x43100000, 0x43110000, +0x43120000, 0x43130000, 0x43140000, 0x43150000, 0x43160000, 0x43170000, +0x43180000, 0x43190000, 0x431A0000, 0x431B0000, 0x431C0000, 0x431D0000, +0x431E0000, 0x431F0000, 0x43200000, 0x43210000, 0x43220000, 0x43230000, +0x43240000, 0x43250000, 0x43260000, 0x43270000, 0x43280000, 0x43290000, +0x432A0000, 0x432B0000, 0x432C0000, 0x432D0000, 0x432E0000, 0x432F0000, +0x43300000, 0x43310000, 0x43320000, 0x43330000, 0x43340000 +}; + +static unsigned int mic_svm_vals_lookup[] = { +0x00000000, 0x3C23D70A, 0x3CA3D70A, 0x3CF5C28F, 0x3D23D70A, 0x3D4CCCCD, +0x3D75C28F, 0x3D8F5C29, 0x3DA3D70A, 0x3DB851EC, 0x3DCCCCCD, 0x3DE147AE, +0x3DF5C28F, 0x3E051EB8, 0x3E0F5C29, 0x3E19999A, 0x3E23D70A, 0x3E2E147B, +0x3E3851EC, 0x3E428F5C, 0x3E4CCCCD, 0x3E570A3D, 0x3E6147AE, 0x3E6B851F, +0x3E75C28F, 0x3E800000, 0x3E851EB8, 0x3E8A3D71, 0x3E8F5C29, 0x3E947AE1, +0x3E99999A, 0x3E9EB852, 0x3EA3D70A, 0x3EA8F5C3, 0x3EAE147B, 0x3EB33333, +0x3EB851EC, 0x3EBD70A4, 0x3EC28F5C, 0x3EC7AE14, 0x3ECCCCCD, 0x3ED1EB85, +0x3ED70A3D, 0x3EDC28F6, 0x3EE147AE, 0x3EE66666, 0x3EEB851F, 0x3EF0A3D7, +0x3EF5C28F, 0x3EFAE148, 0x3F000000, 0x3F028F5C, 0x3F051EB8, 0x3F07AE14, +0x3F0A3D71, 0x3F0CCCCD, 0x3F0F5C29, 0x3F11EB85, 0x3F147AE1, 0x3F170A3D, +0x3F19999A, 0x3F1C28F6, 0x3F1EB852, 0x3F2147AE, 0x3F23D70A, 0x3F266666, +0x3F28F5C3, 0x3F2B851F, 0x3F2E147B, 0x3F30A3D7, 0x3F333333, 0x3F35C28F, +0x3F3851EC, 0x3F3AE148, 0x3F3D70A4, 0x3F400000, 0x3F428F5C, 0x3F451EB8, +0x3F47AE14, 0x3F4A3D71, 0x3F4CCCCD, 0x3F4F5C29, 0x3F51EB85, 0x3F547AE1, +0x3F570A3D, 0x3F59999A, 0x3F5C28F6, 0x3F5EB852, 0x3F6147AE, 0x3F63D70A, +0x3F666666, 0x3F68F5C3, 0x3F6B851F, 0x3F6E147B, 0x3F70A3D7, 0x3F733333, +0x3F75C28F, 0x3F7851EC, 0x3F7AE148, 0x3F7D70A4, 0x3F800000 +}; + +static unsigned int equalizer_vals_lookup[] = { +0xC1C00000, 0xC1B80000, 0xC1B00000, 0xC1A80000, 0xC1A00000, 0xC1980000, +0xC1900000, 0xC1880000, 0xC1800000, 0xC1700000, 0xC1600000, 0xC1500000, +0xC1400000, 0xC1300000, 0xC1200000, 0xC1100000, 0xC1000000, 0xC0E00000, +0xC0C00000, 0xC0A00000, 0xC0800000, 0xC0400000, 0xC0000000, 0xBF800000, +0x00000000, 0x3F800000, 0x40000000, 0x40400000, 0x40800000, 0x40A00000, +0x40C00000, 0x40E00000, 0x41000000, 0x41100000, 0x41200000, 0x41300000, +0x41400000, 0x41500000, 0x41600000, 0x41700000, 0x41800000, 0x41880000, +0x41900000, 0x41980000, 0x41A00000, 0x41A80000, 0x41B00000, 0x41B80000, +0x41C00000 +}; + +static int tuning_ctl_set(struct hda_codec *codec, hda_nid_t nid, + unsigned int *lookup, int idx) +{ + int i = 0; + + for (i = 0; i < TUNING_CTLS_COUNT; i++) + if (nid == ca0132_tuning_ctls[i].nid) + break; + + snd_hda_power_up(codec); + dspio_set_param(codec, ca0132_tuning_ctls[i].mid, + ca0132_tuning_ctls[i].req, + &(lookup[idx]), sizeof(unsigned int)); + snd_hda_power_down(codec); + + return 1; +} + +static int tuning_ctl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct ca0132_spec *spec = codec->spec; + hda_nid_t nid = get_amp_nid(kcontrol); + long *valp = ucontrol->value.integer.value; + int idx = nid - TUNING_CTL_START_NID; + + *valp = spec->cur_ctl_vals[idx]; + return 0; +} + +static int voice_focus_ctl_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + int chs = get_amp_channels(kcontrol); + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = chs == 3 ? 2 : 1; + uinfo->value.integer.min = 20; + uinfo->value.integer.max = 180; + uinfo->value.integer.step = 1; + + return 0; +} + +static int voice_focus_ctl_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct ca0132_spec *spec = codec->spec; + hda_nid_t nid = get_amp_nid(kcontrol); + long *valp = ucontrol->value.integer.value; + int idx; + + idx = nid - TUNING_CTL_START_NID; + /* any change? */ + if (spec->cur_ctl_vals[idx] == *valp) + return 0; + + spec->cur_ctl_vals[idx] = *valp; + + idx = *valp - 20; + tuning_ctl_set(codec, nid, voice_focus_vals_lookup, idx); + + return 1; +} + +static int mic_svm_ctl_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + int chs = get_amp_channels(kcontrol); + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = chs == 3 ? 2 : 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 100; + uinfo->value.integer.step = 1; + + return 0; +} + +static int mic_svm_ctl_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct ca0132_spec *spec = codec->spec; + hda_nid_t nid = get_amp_nid(kcontrol); + long *valp = ucontrol->value.integer.value; + int idx; + + idx = nid - TUNING_CTL_START_NID; + /* any change? */ + if (spec->cur_ctl_vals[idx] == *valp) + return 0; + + spec->cur_ctl_vals[idx] = *valp; + + idx = *valp; + tuning_ctl_set(codec, nid, mic_svm_vals_lookup, idx); + + return 0; +} + +static int equalizer_ctl_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + int chs = get_amp_channels(kcontrol); + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = chs == 3 ? 2 : 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 48; + uinfo->value.integer.step = 1; + + return 0; +} + +static int equalizer_ctl_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct ca0132_spec *spec = codec->spec; + hda_nid_t nid = get_amp_nid(kcontrol); + long *valp = ucontrol->value.integer.value; + int idx; + + idx = nid - TUNING_CTL_START_NID; + /* any change? */ + if (spec->cur_ctl_vals[idx] == *valp) + return 0; + + spec->cur_ctl_vals[idx] = *valp; + + idx = *valp; + tuning_ctl_set(codec, nid, equalizer_vals_lookup, idx); + + return 1; +} + +static const DECLARE_TLV_DB_SCALE(voice_focus_db_scale, 2000, 100, 0); +static const DECLARE_TLV_DB_SCALE(eq_db_scale, -2400, 100, 0); + +static int add_tuning_control(struct hda_codec *codec, + hda_nid_t pnid, hda_nid_t nid, + const char *name, int dir) +{ + char namestr[44]; + int type = dir ? HDA_INPUT : HDA_OUTPUT; + struct snd_kcontrol_new knew = + HDA_CODEC_VOLUME_MONO(namestr, nid, 1, 0, type); + + knew.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ; + knew.tlv.c = 0; + knew.tlv.p = 0; + switch (pnid) { + case VOICE_FOCUS: + knew.info = voice_focus_ctl_info; + knew.get = tuning_ctl_get; + knew.put = voice_focus_ctl_put; + knew.tlv.p = voice_focus_db_scale; + break; + case MIC_SVM: + knew.info = mic_svm_ctl_info; + knew.get = tuning_ctl_get; + knew.put = mic_svm_ctl_put; + break; + case EQUALIZER: + knew.info = equalizer_ctl_info; + knew.get = tuning_ctl_get; + knew.put = equalizer_ctl_put; + knew.tlv.p = eq_db_scale; + break; + default: + return 0; + } + knew.private_value = + HDA_COMPOSE_AMP_VAL(nid, 1, 0, type); + sprintf(namestr, "%s %s Volume", name, dirstr[dir]); + return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec)); +} + +static int add_tuning_ctls(struct hda_codec *codec) +{ + int i; + int err; + + for (i = 0; i < TUNING_CTLS_COUNT; i++) { + err = add_tuning_control(codec, + ca0132_tuning_ctls[i].parent_nid, + ca0132_tuning_ctls[i].nid, + ca0132_tuning_ctls[i].name, + ca0132_tuning_ctls[i].direct); + if (err < 0) + return err; + } + + return 0; +} + +static void ca0132_init_tuning_defaults(struct hda_codec *codec) +{ + struct ca0132_spec *spec = codec->spec; + int i; + + /* Wedge Angle defaults to 30. 10 below is 30 - 20. 20 is min. */ + spec->cur_ctl_vals[WEDGE_ANGLE - TUNING_CTL_START_NID] = 10; + /* SVM level defaults to 0.74. */ + spec->cur_ctl_vals[SVM_LEVEL - TUNING_CTL_START_NID] = 74; + + /* EQ defaults to 0dB. */ + for (i = 2; i < TUNING_CTLS_COUNT; i++) + spec->cur_ctl_vals[i] = 24; +} +#endif /*ENABLE_TUNING_CONTROLS*/ + /* * Select the active output. * If autodetect is enabled, output will be selected based on jack detection. @@ -3805,6 +4087,10 @@ static int ca0132_build_controls(struct hda_codec *codec) add_voicefx(codec); +#ifdef ENABLE_TUNING_CONTROLS + add_tuning_ctls(codec); +#endif + err = snd_hda_jack_add_kctls(codec, &spec->autocfg); if (err < 0) return err; @@ -4184,6 +4470,9 @@ static void ca0132_init_chip(struct hda_codec *codec) spec->effects_switch[PLAY_ENHANCEMENT - EFFECT_START_NID] = 1; spec->effects_switch[CRYSTAL_VOICE - EFFECT_START_NID] = 0; +#ifdef ENABLE_TUNING_CONTROLS + ca0132_init_tuning_defaults(codec); +#endif } static void ca0132_exit_chip(struct hda_codec *codec) -- cgit v1.2.3 From e90f29e44273867392d9d1e0fd94bbe7bffe0335 Mon Sep 17 00:00:00 2001 From: Ian Minett Date: Thu, 20 Dec 2012 18:53:39 -0800 Subject: ALSA: hda/ca0132: Code shuffle to group similar functions. Signed-off-by: Ian Minett Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_ca0132.c | 375 ++++++++++++++++++++++--------------------- 1 file changed, 188 insertions(+), 187 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index c1391f43cc61..77903a398289 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -2683,41 +2683,6 @@ static bool dspload_wait_loaded(struct hda_codec *codec) return false; } -/* - * Controls stuffs. - */ - -/* - * Mixer controls helpers. - */ -#define CA0132_CODEC_VOL_MONO(xname, nid, channel, dir) \ - { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ - .name = xname, \ - .subdevice = HDA_SUBDEV_AMP_FLAG, \ - .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \ - SNDRV_CTL_ELEM_ACCESS_TLV_READ | \ - SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK, \ - .info = ca0132_volume_info, \ - .get = ca0132_volume_get, \ - .put = ca0132_volume_put, \ - .tlv = { .c = ca0132_volume_tlv }, \ - .private_value = HDA_COMPOSE_AMP_VAL(nid, channel, 0, dir) } - -#define CA0132_CODEC_MUTE_MONO(xname, nid, channel, dir) \ - { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ - .name = xname, \ - .subdevice = HDA_SUBDEV_AMP_FLAG, \ - .info = snd_hda_mixer_amp_switch_info, \ - .get = ca0132_switch_get, \ - .put = ca0132_switch_put, \ - .private_value = HDA_COMPOSE_AMP_VAL(nid, channel, 0, dir) } - -/* stereo */ -#define CA0132_CODEC_VOL(xname, nid, dir) \ - CA0132_CODEC_VOL_MONO(xname, nid, 3, dir) -#define CA0132_CODEC_MUTE(xname, nid, dir) \ - CA0132_CODEC_MUTE_MONO(xname, nid, 3, dir) - /* * PCM stuffs */ @@ -2875,6 +2840,41 @@ static int ca0132_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, return 0; } +/* + * Controls stuffs. + */ + +/* + * Mixer controls helpers. + */ +#define CA0132_CODEC_VOL_MONO(xname, nid, channel, dir) \ + { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = xname, \ + .subdevice = HDA_SUBDEV_AMP_FLAG, \ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \ + SNDRV_CTL_ELEM_ACCESS_TLV_READ | \ + SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK, \ + .info = ca0132_volume_info, \ + .get = ca0132_volume_get, \ + .put = ca0132_volume_put, \ + .tlv = { .c = ca0132_volume_tlv }, \ + .private_value = HDA_COMPOSE_AMP_VAL(nid, channel, 0, dir) } + +#define CA0132_CODEC_MUTE_MONO(xname, nid, channel, dir) \ + { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = xname, \ + .subdevice = HDA_SUBDEV_AMP_FLAG, \ + .info = snd_hda_mixer_amp_switch_info, \ + .get = ca0132_switch_get, \ + .put = ca0132_switch_put, \ + .private_value = HDA_COMPOSE_AMP_VAL(nid, channel, 0, dir) } + +/* stereo */ +#define CA0132_CODEC_VOL(xname, nid, dir) \ + CA0132_CODEC_VOL_MONO(xname, nid, 3, dir) +#define CA0132_CODEC_MUTE(xname, nid, dir) \ + CA0132_CODEC_MUTE_MONO(xname, nid, 3, dir) + /* The followings are for tuning of products */ #ifdef ENABLE_TUNING_CONTROLS @@ -3959,7 +3959,70 @@ static struct snd_kcontrol_new ca0132_mixer[] = { { } /* end */ }; +static int ca0132_build_controls(struct hda_codec *codec) +{ + struct ca0132_spec *spec = codec->spec; + int i, num_fx; + int err = 0; + + /* Add Mixer controls */ + for (i = 0; i < spec->num_mixers; i++) { + err = snd_hda_add_new_ctls(codec, spec->mixers[i]); + if (err < 0) + return err; + } + + /* Add in and out effects controls. + * VoiceFX, PE and CrystalVoice are added separately. + */ + num_fx = OUT_EFFECTS_COUNT + IN_EFFECTS_COUNT; + for (i = 0; i < num_fx; i++) { + err = add_fx_switch(codec, ca0132_effects[i].nid, + ca0132_effects[i].name, + ca0132_effects[i].direct); + if (err < 0) + return err; + } + + err = add_fx_switch(codec, PLAY_ENHANCEMENT, "PlayEnhancement", 0); + if (err < 0) + return err; + + err = add_fx_switch(codec, CRYSTAL_VOICE, "CrystalVoice", 1); + if (err < 0) + return err; + + add_voicefx(codec); + +#ifdef ENABLE_TUNING_CONTROLS + add_tuning_ctls(codec); +#endif + + err = snd_hda_jack_add_kctls(codec, &spec->autocfg); + if (err < 0) + return err; + + if (spec->dig_out) { + err = snd_hda_create_spdif_out_ctls(codec, spec->dig_out, + spec->dig_out); + if (err < 0) + return err; + err = snd_hda_create_spdif_share_sw(codec, &spec->multiout); + if (err < 0) + return err; + /* spec->multiout.share_spdif = 1; */ + } + + if (spec->dig_in) { + err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in); + if (err < 0) + return err; + } + return 0; +} + /* + * PCM */ static struct hda_pcm_stream ca0132_pcm_analog_playback = { .substreams = 1, @@ -4052,68 +4115,6 @@ static int ca0132_build_pcms(struct hda_codec *codec) return 0; } -static int ca0132_build_controls(struct hda_codec *codec) -{ - struct ca0132_spec *spec = codec->spec; - int i, num_fx; - int err = 0; - - /* Add Mixer controls */ - for (i = 0; i < spec->num_mixers; i++) { - err = snd_hda_add_new_ctls(codec, spec->mixers[i]); - if (err < 0) - return err; - } - - /* Add in and out effects controls. - * VoiceFX, PE and CrystalVoice are added separately. - */ - num_fx = OUT_EFFECTS_COUNT + IN_EFFECTS_COUNT; - for (i = 0; i < num_fx; i++) { - err = add_fx_switch(codec, ca0132_effects[i].nid, - ca0132_effects[i].name, - ca0132_effects[i].direct); - if (err < 0) - return err; - } - - err = add_fx_switch(codec, PLAY_ENHANCEMENT, "PlayEnhancement", 0); - if (err < 0) - return err; - - err = add_fx_switch(codec, CRYSTAL_VOICE, "CrystalVoice", 1); - if (err < 0) - return err; - - add_voicefx(codec); - -#ifdef ENABLE_TUNING_CONTROLS - add_tuning_ctls(codec); -#endif - - err = snd_hda_jack_add_kctls(codec, &spec->autocfg); - if (err < 0) - return err; - - if (spec->dig_out) { - err = snd_hda_create_spdif_out_ctls(codec, spec->dig_out, - spec->dig_out); - if (err < 0) - return err; - err = snd_hda_create_spdif_share_sw(codec, &spec->multiout); - if (err < 0) - return err; - /* spec->multiout.share_spdif = 1; */ - } - - if (spec->dig_in) { - err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in); - if (err < 0) - return err; - } - return 0; -} - static void ca0132_init_unsol(struct hda_codec *codec) { snd_hda_jack_detect_enable(codec, UNSOL_TAG_HP, UNSOL_TAG_HP); @@ -4330,6 +4331,55 @@ static void ca0132_init_params(struct hda_codec *codec) chipio_set_control_param(codec, CONTROL_PARAM_PORTD_160OHM_GAIN, 6); } +static void ca0132_set_dsp_msr(struct hda_codec *codec, bool is96k) +{ + chipio_set_control_flag(codec, CONTROL_FLAG_DSP_96KHZ, is96k); + chipio_set_control_flag(codec, CONTROL_FLAG_DAC_96KHZ, is96k); + chipio_set_control_flag(codec, CONTROL_FLAG_SRC_RATE_96KHZ, is96k); + chipio_set_control_flag(codec, CONTROL_FLAG_SRC_CLOCK_196MHZ, is96k); + chipio_set_control_flag(codec, CONTROL_FLAG_ADC_B_96KHZ, is96k); + chipio_set_control_flag(codec, CONTROL_FLAG_ADC_C_96KHZ, is96k); + + chipio_set_conn_rate(codec, MEM_CONNID_MICIN1, SR_16_000); + chipio_set_conn_rate(codec, MEM_CONNID_MICOUT1, SR_16_000); + chipio_set_conn_rate(codec, MEM_CONNID_WUH, SR_48_000); +} + +static bool ca0132_download_dsp_images(struct hda_codec *codec) +{ + bool dsp_loaded = false; + const struct dsp_image_seg *dsp_os_image; + + if (request_firmware_cached(&fw_efx, EFX_FILE, + codec->bus->card->dev) != 0) + return false; + + dsp_os_image = (struct dsp_image_seg *)(fw_efx->data); + dspload_image(codec, dsp_os_image, 0, 0, true, 0); + dsp_loaded = dspload_wait_loaded(codec); + + return dsp_loaded; +} + +static void ca0132_download_dsp(struct hda_codec *codec) +{ + struct ca0132_spec *spec = codec->spec; + + spec->dsp_state = DSP_DOWNLOAD_INIT; + + if (spec->dsp_state == DSP_DOWNLOAD_INIT) { + chipio_enable_clocks(codec); + spec->dsp_state = DSP_DOWNLOADING; + if (!ca0132_download_dsp_images(codec)) + spec->dsp_state = DSP_DOWNLOAD_FAILED; + else + spec->dsp_state = DSP_DOWNLOADED; + } + + if (spec->dsp_state == DSP_DOWNLOADED) + ca0132_set_dsp_msr(codec, true); +} + static void ca0132_config(struct hda_codec *codec) { struct ca0132_spec *spec = codec->spec; @@ -4369,6 +4419,47 @@ static void ca0132_config(struct hda_codec *codec) cfg->dig_in_type = HDA_PCM_TYPE_SPDIF; } +static void ca0132_process_dsp_response(struct hda_codec *codec) +{ + struct ca0132_spec *spec = codec->spec; + + snd_printdd(KERN_INFO "ca0132_process_dsp_response\n"); + if (spec->wait_scp) { + if (dspio_get_response_data(codec) >= 0) + spec->wait_scp = 0; + } + + dspio_clear_response_queue(codec); +} + +static void ca0132_unsol_event(struct hda_codec *codec, unsigned int res) +{ + snd_printdd(KERN_INFO "ca0132_unsol_event: 0x%x\n", res); + + + if (((res >> AC_UNSOL_RES_TAG_SHIFT) & 0x3f) == UNSOL_TAG_DSP) { + ca0132_process_dsp_response(codec); + } else { + res = snd_hda_jack_get_action(codec, + (res >> AC_UNSOL_RES_TAG_SHIFT) & 0x3f); + + snd_printdd(KERN_INFO "snd_hda_jack_get_action: 0x%x\n", res); + + switch (res) { + case UNSOL_TAG_HP: + ca0132_select_out(codec); + snd_hda_jack_report_sync(codec); + break; + case UNSOL_TAG_AMIC1: + ca0132_select_mic(codec); + snd_hda_jack_report_sync(codec); + break; + default: + break; + } + } +} + /* * Verbs tables. */ @@ -4483,96 +4574,6 @@ static void ca0132_exit_chip(struct hda_codec *codec) dsp_reset(codec); } -static void ca0132_set_dsp_msr(struct hda_codec *codec, bool is96k) -{ - chipio_set_control_flag(codec, CONTROL_FLAG_DSP_96KHZ, is96k); - chipio_set_control_flag(codec, CONTROL_FLAG_DAC_96KHZ, is96k); - chipio_set_control_flag(codec, CONTROL_FLAG_SRC_RATE_96KHZ, is96k); - chipio_set_control_flag(codec, CONTROL_FLAG_SRC_CLOCK_196MHZ, is96k); - chipio_set_control_flag(codec, CONTROL_FLAG_ADC_B_96KHZ, is96k); - chipio_set_control_flag(codec, CONTROL_FLAG_ADC_C_96KHZ, is96k); - - chipio_set_conn_rate(codec, MEM_CONNID_MICIN1, SR_16_000); - chipio_set_conn_rate(codec, MEM_CONNID_MICOUT1, SR_16_000); - chipio_set_conn_rate(codec, MEM_CONNID_WUH, SR_48_000); -} - -static bool ca0132_download_dsp_images(struct hda_codec *codec) -{ - bool dsp_loaded = false; - const struct dsp_image_seg *dsp_os_image; - - if (request_firmware_cached(&fw_efx, EFX_FILE, - codec->bus->card->dev) != 0) - return false; - - dsp_os_image = (struct dsp_image_seg *)(fw_efx->data); - dspload_image(codec, dsp_os_image, 0, 0, true, 0); - dsp_loaded = dspload_wait_loaded(codec); - - return dsp_loaded; -} - -static void ca0132_download_dsp(struct hda_codec *codec) -{ - struct ca0132_spec *spec = codec->spec; - - spec->dsp_state = DSP_DOWNLOAD_INIT; - - if (spec->dsp_state == DSP_DOWNLOAD_INIT) { - chipio_enable_clocks(codec); - spec->dsp_state = DSP_DOWNLOADING; - if (!ca0132_download_dsp_images(codec)) - spec->dsp_state = DSP_DOWNLOAD_FAILED; - else - spec->dsp_state = DSP_DOWNLOADED; - } - - if (spec->dsp_state == DSP_DOWNLOADED) - ca0132_set_dsp_msr(codec, true); -} - -static void ca0132_process_dsp_response(struct hda_codec *codec) -{ - struct ca0132_spec *spec = codec->spec; - - snd_printdd(KERN_INFO "ca0132_process_dsp_response\n"); - if (spec->wait_scp) { - if (dspio_get_response_data(codec) >= 0) - spec->wait_scp = 0; - } - - dspio_clear_response_queue(codec); -} - -static void ca0132_unsol_event(struct hda_codec *codec, unsigned int res) -{ - snd_printdd(KERN_INFO "ca0132_unsol_event: 0x%x\n", res); - - - if (((res >> AC_UNSOL_RES_TAG_SHIFT) & 0x3f) == UNSOL_TAG_DSP) { - ca0132_process_dsp_response(codec); - } else { - res = snd_hda_jack_get_action(codec, - (res >> AC_UNSOL_RES_TAG_SHIFT) & 0x3f); - - snd_printdd(KERN_INFO "snd_hda_jack_get_action: 0x%x\n", res); - - switch (res) { - case UNSOL_TAG_HP: - ca0132_select_out(codec); - snd_hda_jack_report_sync(codec); - break; - case UNSOL_TAG_AMIC1: - ca0132_select_mic(codec); - snd_hda_jack_report_sync(codec); - break; - default: - break; - } - } -} - static int ca0132_init(struct hda_codec *codec) { struct ca0132_spec *spec = codec->spec; -- cgit v1.2.3 From 441aa6a016f66f2d20a95d1afafe3e47254a666f Mon Sep 17 00:00:00 2001 From: Ian Minett Date: Thu, 20 Dec 2012 18:53:40 -0800 Subject: ALSA: hda/ca0132: Shuffle to group together related code Signed-off-by: Ian Minett Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_ca0132.c | 152 +++++++++++++++++++++---------------------- 1 file changed, 76 insertions(+), 76 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index 77903a398289..4c9b95e56567 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -673,43 +673,6 @@ enum ca0132_sample_rate { SR_RATE_UNKNOWN = 0x1F }; -static void init_output(struct hda_codec *codec, hda_nid_t pin, hda_nid_t dac) -{ - if (pin) { - snd_hda_codec_write(codec, pin, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP); - if (get_wcaps(codec, pin) & AC_WCAP_OUT_AMP) - snd_hda_codec_write(codec, pin, 0, - AC_VERB_SET_AMP_GAIN_MUTE, - AMP_OUT_UNMUTE); - } - if (dac && (get_wcaps(codec, dac) & AC_WCAP_OUT_AMP)) - snd_hda_codec_write(codec, dac, 0, - AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO); -} - -static void init_input(struct hda_codec *codec, hda_nid_t pin, hda_nid_t adc) -{ - if (pin) { - snd_hda_codec_write(codec, pin, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80); - if (get_wcaps(codec, pin) & AC_WCAP_IN_AMP) - snd_hda_codec_write(codec, pin, 0, - AC_VERB_SET_AMP_GAIN_MUTE, - AMP_IN_UNMUTE(0)); - } - if (adc && (get_wcaps(codec, adc) & AC_WCAP_IN_AMP)) { - snd_hda_codec_write(codec, adc, 0, AC_VERB_SET_AMP_GAIN_MUTE, - AMP_IN_UNMUTE(0)); - - /* init to 0 dB and unmute. */ - snd_hda_codec_amp_stereo(codec, adc, HDA_INPUT, 0, - HDA_AMP_VOLMASK, 0x5a); - snd_hda_codec_amp_stereo(codec, adc, HDA_INPUT, 0, - HDA_AMP_MUTE, 0); - } -} - enum dsp_download_state { DSP_DOWNLOAD_FAILED = -1, DSP_DOWNLOAD_INIT = 0, @@ -4115,6 +4078,43 @@ static int ca0132_build_pcms(struct hda_codec *codec) return 0; } +static void init_output(struct hda_codec *codec, hda_nid_t pin, hda_nid_t dac) +{ + if (pin) { + snd_hda_codec_write(codec, pin, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP); + if (get_wcaps(codec, pin) & AC_WCAP_OUT_AMP) + snd_hda_codec_write(codec, pin, 0, + AC_VERB_SET_AMP_GAIN_MUTE, + AMP_OUT_UNMUTE); + } + if (dac && (get_wcaps(codec, dac) & AC_WCAP_OUT_AMP)) + snd_hda_codec_write(codec, dac, 0, + AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO); +} + +static void init_input(struct hda_codec *codec, hda_nid_t pin, hda_nid_t adc) +{ + if (pin) { + snd_hda_codec_write(codec, pin, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80); + if (get_wcaps(codec, pin) & AC_WCAP_IN_AMP) + snd_hda_codec_write(codec, pin, 0, + AC_VERB_SET_AMP_GAIN_MUTE, + AMP_IN_UNMUTE(0)); + } + if (adc && (get_wcaps(codec, adc) & AC_WCAP_IN_AMP)) { + snd_hda_codec_write(codec, adc, 0, AC_VERB_SET_AMP_GAIN_MUTE, + AMP_IN_UNMUTE(0)); + + /* init to 0 dB and unmute. */ + snd_hda_codec_amp_stereo(codec, adc, HDA_INPUT, 0, + HDA_AMP_VOLMASK, 0x5a); + snd_hda_codec_amp_stereo(codec, adc, HDA_INPUT, 0, + HDA_AMP_MUTE, 0); + } +} + static void ca0132_init_unsol(struct hda_codec *codec) { snd_hda_jack_detect_enable(codec, UNSOL_TAG_HP, UNSOL_TAG_HP); @@ -4380,45 +4380,6 @@ static void ca0132_download_dsp(struct hda_codec *codec) ca0132_set_dsp_msr(codec, true); } -static void ca0132_config(struct hda_codec *codec) -{ - struct ca0132_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - - spec->dacs[0] = 0x2; - spec->dacs[1] = 0x3; - spec->dacs[2] = 0x4; - - spec->multiout.dac_nids = spec->dacs; - spec->multiout.num_dacs = 3; - spec->multiout.max_channels = 2; - - spec->num_outputs = 2; - spec->out_pins[0] = 0x0b; /* speaker out */ - spec->out_pins[1] = 0x10; /* headphone out */ - spec->shared_out_nid = 0x2; - - spec->num_inputs = 3; - spec->adcs[0] = 0x7; /* digital mic / analog mic1 */ - spec->adcs[1] = 0x8; /* analog mic2 */ - spec->adcs[2] = 0xa; /* what u hear */ - spec->shared_mic_nid = 0x7; - - spec->input_pins[0] = 0x12; - spec->input_pins[1] = 0x11; - spec->input_pins[2] = 0x13; - - /* SPDIF I/O */ - spec->dig_out = 0x05; - spec->multiout.dig_out_nid = spec->dig_out; - cfg->dig_out_pins[0] = 0x0c; - cfg->dig_outs = 1; - cfg->dig_out_type[0] = HDA_PCM_TYPE_SPDIF; - spec->dig_in = 0x09; - cfg->dig_in_pin = 0x0e; - cfg->dig_in_type = HDA_PCM_TYPE_SPDIF; -} - static void ca0132_process_dsp_response(struct hda_codec *codec) { struct ca0132_spec *spec = codec->spec; @@ -4640,6 +4601,45 @@ static struct hda_codec_ops ca0132_patch_ops = { .unsol_event = ca0132_unsol_event, }; +static void ca0132_config(struct hda_codec *codec) +{ + struct ca0132_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + + spec->dacs[0] = 0x2; + spec->dacs[1] = 0x3; + spec->dacs[2] = 0x4; + + spec->multiout.dac_nids = spec->dacs; + spec->multiout.num_dacs = 3; + spec->multiout.max_channels = 2; + + spec->num_outputs = 2; + spec->out_pins[0] = 0x0b; /* speaker out */ + spec->out_pins[1] = 0x10; /* headphone out */ + spec->shared_out_nid = 0x2; + + spec->num_inputs = 3; + spec->adcs[0] = 0x7; /* digital mic / analog mic1 */ + spec->adcs[1] = 0x8; /* analog mic2 */ + spec->adcs[2] = 0xa; /* what u hear */ + spec->shared_mic_nid = 0x7; + + spec->input_pins[0] = 0x12; + spec->input_pins[1] = 0x11; + spec->input_pins[2] = 0x13; + + /* SPDIF I/O */ + spec->dig_out = 0x05; + spec->multiout.dig_out_nid = spec->dig_out; + cfg->dig_out_pins[0] = 0x0c; + cfg->dig_outs = 1; + cfg->dig_out_type[0] = HDA_PCM_TYPE_SPDIF; + spec->dig_in = 0x09; + cfg->dig_in_pin = 0x0e; + cfg->dig_in_type = HDA_PCM_TYPE_SPDIF; +} + static int patch_ca0132(struct hda_codec *codec) { struct ca0132_spec *spec; -- cgit v1.2.3 From 406261ce998589dc980d9a6683a5ef3153eec1a5 Mon Sep 17 00:00:00 2001 From: Ian Minett Date: Thu, 20 Dec 2012 18:53:41 -0800 Subject: ALSA: hda/ca0132: Fix potential init errors and update module description Handle a potential dma_engine alloc error and fix the possible use of an uninitialized status variable in dspxfr_one_seg(). Also correct the initial sampling rate for Mic 1. Update the module description. Signed-off-by: Ian Minett Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_ca0132.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index 4c9b95e56567..2b026e2733c8 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -2223,7 +2223,7 @@ static int dspxfr_one_seg(struct hda_codec *codec, unsigned int port_map_mask, bool ovly) { - int status; + int status = 0; bool comm_dma_setup_done = false; const unsigned int *data; unsigned int chip_addx; @@ -2416,8 +2416,10 @@ static int dspxfr_image(struct hda_codec *codec, return -EINVAL; dma_engine = kzalloc(sizeof(*dma_engine), GFP_KERNEL); - if (!dma_engine) - return -ENOMEM; + if (!dma_engine) { + status = -ENOMEM; + goto exit; + } dma_engine->dmab = kzalloc(sizeof(*dma_engine->dmab), GFP_KERNEL); if (!dma_engine->dmab) { @@ -4340,8 +4342,8 @@ static void ca0132_set_dsp_msr(struct hda_codec *codec, bool is96k) chipio_set_control_flag(codec, CONTROL_FLAG_ADC_B_96KHZ, is96k); chipio_set_control_flag(codec, CONTROL_FLAG_ADC_C_96KHZ, is96k); - chipio_set_conn_rate(codec, MEM_CONNID_MICIN1, SR_16_000); - chipio_set_conn_rate(codec, MEM_CONNID_MICOUT1, SR_16_000); + chipio_set_conn_rate(codec, MEM_CONNID_MICIN1, SR_96_000); + chipio_set_conn_rate(codec, MEM_CONNID_MICOUT1, SR_96_000); chipio_set_conn_rate(codec, MEM_CONNID_WUH, SR_48_000); } @@ -4685,7 +4687,7 @@ static struct hda_codec_preset snd_hda_preset_ca0132[] = { MODULE_ALIAS("snd-hda-codec-id:11020011"); MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("Creative CA0132, CA0132 HD-audio codec"); +MODULE_DESCRIPTION("Creative Sound Core3D codec"); static struct hda_codec_preset_list ca0132_list = { .preset = snd_hda_preset_ca0132, -- cgit v1.2.3 From 15e4ba666ca6c2fcc00184cef56fb971a20e8e04 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 15 Jan 2013 17:08:38 +0100 Subject: Revert "ALSA: hda - Add firmware caching to CA0132 codec" This reverts commit c3b4eea26208b8e247ece9d3a9ec8b2eab48c464. Since the recent firmware loader code supports caching at S3/S4 by itself, we don't have to handle f/w caching in the driver. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_ca0132.c | 28 ++++++---------------------- 1 file changed, 6 insertions(+), 22 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index 2b026e2733c8..0d2c2f83ca7d 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -2602,24 +2602,6 @@ static int dspload_image(struct hda_codec *codec, return status; } -static const struct firmware *fw_efx; - -static int request_firmware_cached(const struct firmware **firmware_p, - const char *name, struct device *device) -{ - if (*firmware_p) - return 0; /* already loaded */ - return request_firmware(firmware_p, name, device); -} - -static void release_cached_firmware(void) -{ - if (fw_efx) { - release_firmware(fw_efx); - fw_efx = NULL; - } -} - static bool dspload_is_loaded(struct hda_codec *codec) { unsigned int data = 0; @@ -4351,15 +4333,18 @@ static bool ca0132_download_dsp_images(struct hda_codec *codec) { bool dsp_loaded = false; const struct dsp_image_seg *dsp_os_image; + const struct firmware *fw_entry; - if (request_firmware_cached(&fw_efx, EFX_FILE, - codec->bus->card->dev) != 0) + if (request_firmware(&fw_entry, EFX_FILE, codec->bus->card->dev) != 0) return false; - dsp_os_image = (struct dsp_image_seg *)(fw_efx->data); + dsp_os_image = (struct dsp_image_seg *)(fw_entry->data); dspload_image(codec, dsp_os_image, 0, 0, true, 0); dsp_loaded = dspload_wait_loaded(codec); + release_firmware(fw_entry); + + return dsp_loaded; } @@ -4701,7 +4686,6 @@ static int __init patch_ca0132_init(void) static void __exit patch_ca0132_exit(void) { - release_cached_firmware(); snd_hda_delete_codec_preset(&ca0132_list); } -- cgit v1.2.3 From a0c041cb6f74f9734d9a3af1061a718b3879a255 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 15 Jan 2013 17:13:31 +0100 Subject: ALSA: hda/ca0132 - Use snd_hda_set_pin_ctl() helper again The recent update of ca0132 driver replaced the pinctl setup to the direct write via snd_hda_codec_write() again. This should be covered by snd_hda_set_pin_ctl() to be safer. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_ca0132.c | 26 ++++++++++---------------- 1 file changed, 10 insertions(+), 16 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index 0d2c2f83ca7d..467c9a18819b 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -3158,15 +3158,13 @@ static int ca0132_select_out(struct hda_codec *codec) /* disable headphone node */ pin_ctl = snd_hda_codec_read(codec, spec->out_pins[1], 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0); - snd_hda_codec_write(codec, spec->out_pins[1], 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, - pin_ctl & 0xBF); + snd_hda_set_pin_ctl(codec, spec->out_pins[1], + pin_ctl & ~PIN_HP); /* enable speaker node */ pin_ctl = snd_hda_codec_read(codec, spec->out_pins[0], 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0); - snd_hda_codec_write(codec, spec->out_pins[0], 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, - pin_ctl | 0x40); + snd_hda_set_pin_ctl(codec, spec->out_pins[0], + pin_ctl | PIN_OUT); } else { snd_printdd(KERN_INFO "ca0132_select_out hp\n"); /*headphone out config*/ @@ -3193,15 +3191,13 @@ static int ca0132_select_out(struct hda_codec *codec) /* disable speaker*/ pin_ctl = snd_hda_codec_read(codec, spec->out_pins[0], 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0); - snd_hda_codec_write(codec, spec->out_pins[0], 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, - pin_ctl & 0xBF); + snd_hda_set_pin_ctl(codec, spec->out_pins[0], + pin_ctl & ~PIN_HP); /* enable headphone*/ pin_ctl = snd_hda_codec_read(codec, spec->out_pins[1], 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0); - snd_hda_codec_write(codec, spec->out_pins[1], 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, - pin_ctl | 0x40); + snd_hda_set_pin_ctl(codec, spec->out_pins[1], + pin_ctl | PIN_HP); } exit: @@ -4065,8 +4061,7 @@ static int ca0132_build_pcms(struct hda_codec *codec) static void init_output(struct hda_codec *codec, hda_nid_t pin, hda_nid_t dac) { if (pin) { - snd_hda_codec_write(codec, pin, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP); + snd_hda_set_pin_ctl(codec, pin, PIN_HP); if (get_wcaps(codec, pin) & AC_WCAP_OUT_AMP) snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_AMP_GAIN_MUTE, @@ -4080,8 +4075,7 @@ static void init_output(struct hda_codec *codec, hda_nid_t pin, hda_nid_t dac) static void init_input(struct hda_codec *codec, hda_nid_t pin, hda_nid_t adc) { if (pin) { - snd_hda_codec_write(codec, pin, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80); + snd_hda_set_pin_ctl(codec, pin, PIN_VREF80); if (get_wcaps(codec, pin) & AC_WCAP_IN_AMP) snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_AMP_GAIN_MUTE, -- cgit v1.2.3 From b645d79619e8b15e91cc7df23c5f8a23d0d69377 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 15 Jan 2013 17:39:29 +0100 Subject: ALSA: hda/ca0132 - Fix superfluous unsigned check Fix a warning by smatch, sound/pci/hda/patch_ca0132.c:714 dspio_send() warn: always true condition '(res >= 0) => (0-u32max >= 0)' Reported-by: Dan Carpenter Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_ca0132.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index 467c9a18819b..7668388e8b56 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -1054,7 +1054,7 @@ static void chipio_enable_clocks(struct hda_codec *codec) static int dspio_send(struct hda_codec *codec, unsigned int reg, unsigned int data) { - unsigned int res; + int res; int retry = 50; /* send bits of data specified by reg to dsp */ -- cgit v1.2.3 From 425a7880e804f6147b520aecee522e4172c98e83 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 15 Jan 2013 17:41:21 +0100 Subject: ALSA: hda/ca0132 - Fix another smatch warning sound/pci/hda/patch_ca0132.c:1781 dspxfr_one_seg() info: why not propagate 'status' from dsp_dma_stop() instead of (-5)? Reported-by: Dan Carpenter Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_ca0132.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index 7668388e8b56..f6c949039cab 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -2325,7 +2325,7 @@ static int dspxfr_one_seg(struct hda_codec *codec, if (!comm_dma_setup_done) { status = dsp_dma_stop(codec, dma_chan, ovly); if (status < 0) - return -EIO; + return status; status = dsp_dma_setup_common(codec, chip_addx, dma_chan, port_map_mask, ovly); if (status < 0) -- cgit v1.2.3 From 549e8292a1e7712d401cc8b8df88286cdfff9f08 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 15 Jan 2013 17:42:15 +0100 Subject: ALSA: hda/ca0132 - Fix possible NULL dereference Spotted by smatch, sound/pci/hda/patch_ca0132.c:1950 dspxfr_image() error: potential null dereference 'dma_engine'. (kzalloc returns null) sound/pci/hda/patch_ca0132.c:1950 dspxfr_image() error: we previously assumed 'dma_engine' could be null (see line 1857) Reported-by: Dan Carpenter Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_ca0132.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index f6c949039cab..ee2b9c6600ff 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -2416,15 +2416,13 @@ static int dspxfr_image(struct hda_codec *codec, return -EINVAL; dma_engine = kzalloc(sizeof(*dma_engine), GFP_KERNEL); - if (!dma_engine) { - status = -ENOMEM; - goto exit; - } + if (!dma_engine) + return -ENOMEM; dma_engine->dmab = kzalloc(sizeof(*dma_engine->dmab), GFP_KERNEL); if (!dma_engine->dmab) { - status = -ENOMEM; - goto exit; + kfree(dma_engine); + return -ENOMEM; } dma_engine->codec = codec; -- cgit v1.2.3 From 8ae3124b8f0f3f97f928be22ccc816118d4b0ecb Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 15 Jan 2013 17:43:09 +0100 Subject: ALSA: hda/ca0132 - Fix possible invalid DMA channel deallocation ... in the error path in dspxfr_image(). Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_ca0132.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index ee2b9c6600ff..cc6c8a590316 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -2167,7 +2167,7 @@ static const struct dsp_image_seg *get_next_seg_ptr( /* * CA0132 chip DSP transfer stuffs. For DSP download. */ -#define INVALID_DMA_CHANNEL (~0UL) +#define INVALID_DMA_CHANNEL (~0U) /* * Program a list of address/data pairs via the ChipIO widget. @@ -2431,7 +2431,7 @@ static int dspxfr_image(struct hda_codec *codec, dma_engine->buf_size = (ovly ? DSP_DMA_WRITE_BUFLEN_OVLY : DSP_DMA_WRITE_BUFLEN_INIT) * 2; - dma_chan = 0; + dma_chan = ovly ? INVALID_DMA_CHANNEL : 0; status = codec_set_converter_format(codec, WIDGET_CHIP_CTRL, hda_format, &response); @@ -2453,7 +2453,7 @@ static int dspxfr_image(struct hda_codec *codec, status = dspio_alloc_dma_chan(codec, &dma_chan); if (status < 0) { snd_printdd(KERN_ERR "alloc dmachan fail"); - dma_chan = (unsigned int)INVALID_DMA_CHANNEL; + dma_chan = INVALID_DMA_CHANNEL; goto exit; } } -- cgit v1.2.3 From 7a527edee43a3c6c861e4a269f2bd3799b9bf8e8 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 15 Jan 2013 17:44:20 +0100 Subject: ALSA: hda/ca0132 - Declare firmware only when really built Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_ca0132.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index cc6c8a590316..70a2c552c49c 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -75,7 +75,9 @@ #define EFX_FILE "ctefx.bin" +#ifdef CONFIG_SND_HDA_DSP_LOADER MODULE_FIRMWARE(EFX_FILE); +#endif static char *dirstr[2] = { "Playback", "Capture" }; -- cgit v1.2.3 From dea500c7c6e507c72ef94d0f6cd039d81b4c645f Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 15 Jan 2013 17:49:56 +0100 Subject: ALSA: hda/ca0132 - Fix a wrong comma in snd_printdd() call MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit sound/pci/hda/patch_ca0132.c: In function ‘ca0132_effects_set’: sound/pci/hda/patch_ca0132.c:3391:2: warning: too many arguments for format [-Wformat-extra-args] Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_ca0132.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index 70a2c552c49c..daf5ee381e5c 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -3388,7 +3388,7 @@ static int ca0132_effects_set(struct hda_codec *codec, hda_nid_t nid, long val) val = 0; } - snd_printdd(KERN_INFO, "ca0132_effect_set: nid=0x%x, val=%ld\n", + snd_printdd(KERN_INFO "ca0132_effect_set: nid=0x%x, val=%ld\n", nid, val); on = (val == 0) ? FLOAT_ZERO : FLOAT_ONE; -- cgit v1.2.3 From ea46c3c87c35b90139b4dca43917d0f605d568ed Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 15 Jan 2013 18:45:53 +0100 Subject: ALSA: hda - Add prefer_hp_amp flag to hda_gen_spec Add a new flag to indicate whether HP amp is turned on as default for speaker or line-outs, and enable this for ALC260 codec, as many machines with this codec require the HP amp even for speakers. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 14 ++++++++++---- sound/pci/hda/hda_generic.h | 1 + sound/pci/hda/patch_realtek.c | 7 +++++-- 3 files changed, 16 insertions(+), 6 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 932e6a133f3d..e878a9effc96 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -1284,6 +1284,7 @@ static int fill_and_eval_dacs(struct hda_codec *codec, struct hda_gen_spec *spec = codec->spec; struct auto_pin_cfg *cfg = &spec->autocfg; int i, err, badness; + unsigned int val; /* set num_dacs once to full for look_for_dac() */ spec->multiout.num_dacs = cfg->line_outs; @@ -1421,13 +1422,18 @@ static int fill_and_eval_dacs(struct hda_codec *codec, spec->speaker_paths); /* set initial pinctl targets */ - set_pin_targets(codec, cfg->line_outs, cfg->line_out_pins, - cfg->line_out_type == AUTO_PIN_HP_OUT ? PIN_HP : PIN_OUT); + if (spec->prefer_hp_amp || cfg->line_out_type == AUTO_PIN_HP_OUT) + val = PIN_HP; + else + val = PIN_OUT; + set_pin_targets(codec, cfg->line_outs, cfg->line_out_pins, val); if (cfg->line_out_type != AUTO_PIN_HP_OUT) set_pin_targets(codec, cfg->hp_outs, cfg->hp_pins, PIN_HP); - if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) + if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) { + val = spec->prefer_hp_amp ? PIN_HP : PIN_OUT; set_pin_targets(codec, cfg->speaker_outs, - cfg->speaker_pins, PIN_OUT); + cfg->speaker_pins, val); + } return badness; } diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h index 1ceaacd1b775..6ba580540081 100644 --- a/sound/pci/hda/hda_generic.h +++ b/sound/pci/hda/hda_generic.h @@ -190,6 +190,7 @@ struct hda_gen_spec { unsigned int vmaster_mute_enum:1; /* add vmaster mute mode enum */ unsigned int indep_hp:1; /* independent HP supported */ unsigned int indep_hp_enabled:1; /* independent HP enabled */ + unsigned int prefer_hp_amp:1; /* enable HP amp for speaker if any */ unsigned int add_stereo_mix_input:1; /* add aamix as a capture src */ unsigned int add_out_jack_modes:1; /* add output jack mode enum ctls */ diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index fab31d29fa23..71a8894438ab 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -1506,8 +1506,6 @@ static void alc260_fixup_fsc_s7020(struct hda_codec *codec, if (action == HDA_FIXUP_ACT_PRE_PROBE) spec->gen.add_out_jack_modes = 1; - else if (action == HDA_FIXUP_ACT_PROBE) - snd_hda_set_pin_ctl_cache(codec, 0x10, PIN_HP); } static const struct hda_fixup alc260_fixups[] = { @@ -1597,6 +1595,11 @@ static int patch_alc260(struct hda_codec *codec) return err; spec = codec->spec; + /* as quite a few machines require HP amp for speaker outputs, + * it's easier to enable it unconditionally; even if it's unneeded, + * it's almost harmless. + */ + spec->gen.prefer_hp_amp = 1; snd_hda_pick_fixup(codec, NULL, alc260_fixup_tbl, alc260_fixups); snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); -- cgit v1.2.3 From ccd7bd3d07bd763f0e7397e6cef16aaec0489fdc Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 16 Jan 2013 09:32:54 +0800 Subject: ALSA: hda/ca0132 - Make some symbols static sound/pci/hda/patch_ca0132.c:387:19: sparse: symbol 'ca0132_voicefx' was not declared. Should it be static? Reported-by: Fengguang Wu Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_ca0132.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index daf5ee381e5c..483850f64150 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -387,14 +387,14 @@ struct ct_voicefx_preset { unsigned int vals[VOICEFX_MAX_PARAM_COUNT]; }; -struct ct_voicefx ca0132_voicefx = { +static struct ct_voicefx ca0132_voicefx = { .name = "VoiceFX Capture Switch", .nid = VOICEFX, .mid = 0x95, .reqs = {10, 11, 12, 13, 14, 15, 16, 17, 18} }; -struct ct_voicefx_preset ca0132_voicefx_presets[] = { +static struct ct_voicefx_preset ca0132_voicefx_presets[] = { { .name = "Neutral", .vals = { 0x00000000, 0x43C80000, 0x44AF0000, 0x44FA0000, 0x3F800000, 0x3F800000, -- cgit v1.2.3 From b56fa1ed09615f148271045d220b1c55580bdfc9 Mon Sep 17 00:00:00 2001 From: David Henningsson Date: Wed, 16 Jan 2013 11:45:35 +0100 Subject: ALSA: hda - Check array bounds in get_input_path This gives us some additional safety. Signed-off-by: David Henningsson Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index e878a9effc96..1fa71acc348d 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -2403,8 +2403,16 @@ static int create_input_ctls(struct hda_codec *codec) static struct nid_path *get_input_path(struct hda_codec *codec, int adc_idx, int imux_idx) { struct hda_gen_spec *spec = codec->spec; + if (imux_idx < 0 || imux_idx >= HDA_MAX_NUM_INPUTS) { + snd_BUG(); + return NULL; + } if (spec->dyn_adc_switch) adc_idx = spec->dyn_adc_idx[imux_idx]; + if (adc_idx < 0 || adc_idx >= AUTO_CFG_MAX_OUTS) { + snd_BUG(); + return NULL; + } return snd_hda_get_path_from_idx(codec, spec->input_paths[imux_idx][adc_idx]); } -- cgit v1.2.3 From a053d1e3c43e39109e640d1516669aeb8ce0b60b Mon Sep 17 00:00:00 2001 From: David Henningsson Date: Wed, 16 Jan 2013 11:45:36 +0100 Subject: ALSA: hda - fix wrong adc_idx in generic parser We use knew->index for adc_idx when we create "Capture Volume" and "Capture Switch", so use the same to retrieve adc_idx. Signed-off-by: David Henningsson Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 1fa71acc348d..68947fa23400 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -2432,7 +2432,7 @@ static int mux_enum_get(struct snd_kcontrol *kcontrol, { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct hda_gen_spec *spec = codec->spec; - unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); + unsigned int adc_idx = kcontrol->id.index; ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx]; return 0; @@ -2442,7 +2442,7 @@ static int mux_enum_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); + unsigned int adc_idx = kcontrol->id.index; return mux_select(codec, adc_idx, ucontrol->value.enumerated.item[0]); } @@ -2474,7 +2474,7 @@ static int cap_put_caller(struct snd_kcontrol *kcontrol, int i, adc_idx, err = 0; imux = &spec->input_mux; - adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); + adc_idx = kcontrol->id.index; mutex_lock(&codec->control_mutex); /* we use the cache-only update at first since multiple input paths * may shared the same amp; by updating only caches, the redundant -- cgit v1.2.3 From c0f3b21643487e2bbf8af534a33ffd2857e18fa1 Mon Sep 17 00:00:00 2001 From: David Henningsson Date: Wed, 16 Jan 2013 11:45:37 +0100 Subject: ALSA: hda - initialize channel counts correctly Even a single DAC can output two channels, so the channel count is twice the number of DACs. Signed-off-by: David Henningsson Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 68947fa23400..c33e019617cd 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -1400,7 +1400,7 @@ static int fill_and_eval_dacs(struct hda_codec *codec, } spec->ext_channel_count = spec->min_channel_count = - spec->multiout.num_dacs; + spec->multiout.num_dacs * 2; if (spec->multi_ios == 2) { for (i = 0; i < 2; i++) -- cgit v1.2.3 From 02aba550537a666b8d09346f39d6372c78b115a5 Mon Sep 17 00:00:00 2001 From: David Henningsson Date: Wed, 16 Jan 2013 15:58:43 +0100 Subject: ALSA: hda - do not add non-existing Mic boost controls If the input node does not have any volume capable input amp, don't add such a control. Signed-off-by: David Henningsson Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index c33e019617cd..05dfeb7bfc97 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -2826,6 +2826,9 @@ static int parse_mic_boost(struct hda_codec *codec) struct nid_path *path; unsigned int val; + if (!nid_has_volume(codec, nid, HDA_INPUT)) + continue; + label = hda_get_autocfg_input_label(codec, cfg, i); if (prev_label && !strcmp(label, prev_label)) type_idx++; -- cgit v1.2.3 From 99a5592d6a897eed447df1fac6b591c06c891858 Mon Sep 17 00:00:00 2001 From: David Henningsson Date: Wed, 16 Jan 2013 15:58:44 +0100 Subject: ALSA: hda - force different capture controls if amp caps differ Otherwise setting the capture volume for amps will be weird and inconsistent (it will try to set values outside the range of the second amp based on capabilities of the first amp). Signed-off-by: David Henningsson Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 05dfeb7bfc97..171364a9dda4 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -484,6 +484,15 @@ static bool check_amp_caps(struct hda_codec *codec, hda_nid_t nid, return false; } +static bool same_amp_caps(struct hda_codec *codec, hda_nid_t nid1, + hda_nid_t nid2, int dir) +{ + if (!(get_wcaps(codec, nid1) & (1 << (dir + 1)))) + return !(get_wcaps(codec, nid2) & (1 << (dir + 1))); + return (query_amp_caps(codec, nid1, dir) == + query_amp_caps(codec, nid2, dir)); +} + #define nid_has_mute(codec, nid, dir) \ check_amp_caps(codec, nid, dir, AC_AMPCAP_MUTE) #define nid_has_volume(codec, nid, dir) \ @@ -2768,6 +2777,7 @@ static int create_capture_mixers(struct hda_codec *codec) for (n = 0; n < nums; n++) { bool multi = false; + bool multi_cap_vol = spec->multi_cap_vol; bool inv_dmic = false; int vol, sw; @@ -2780,12 +2790,20 @@ static int create_capture_mixers(struct hda_codec *codec) parse_capvol_in_path(codec, path); if (!vol) vol = path->ctls[NID_PATH_VOL_CTL]; - else if (vol != path->ctls[NID_PATH_VOL_CTL]) + else if (vol != path->ctls[NID_PATH_VOL_CTL]) { multi = true; + if (!same_amp_caps(codec, vol, + path->ctls[NID_PATH_VOL_CTL], HDA_INPUT)) + multi_cap_vol = true; + } if (!sw) sw = path->ctls[NID_PATH_MUTE_CTL]; - else if (sw != path->ctls[NID_PATH_MUTE_CTL]) + else if (sw != path->ctls[NID_PATH_MUTE_CTL]) { multi = true; + if (!same_amp_caps(codec, sw, + path->ctls[NID_PATH_MUTE_CTL], HDA_INPUT)) + multi_cap_vol = true; + } if (is_inv_dmic_pin(codec, spec->imux_pins[i])) inv_dmic = true; } @@ -2793,7 +2811,7 @@ static int create_capture_mixers(struct hda_codec *codec) if (!multi) err = create_single_cap_vol_ctl(codec, n, vol, sw, inv_dmic); - else if (!spec->multi_cap_vol) + else if (!multi_cap_vol) err = create_bind_cap_vol_ctl(codec, n, vol, sw); else err = create_multi_cap_vol_ctl(codec); -- cgit v1.2.3 From 6fc4cb97cbf5bf1ccfac821072e3927d21b3e0e9 Mon Sep 17 00:00:00 2001 From: David Henningsson Date: Wed, 16 Jan 2013 15:58:45 +0100 Subject: ALSA: hda - Make sure fill_all_dac_nids is called for digital only codecs Otherwise no PCM will be built for codecs without analog I/O. Signed-off-by: David Henningsson Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 171364a9dda4..73900d935681 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -1516,8 +1516,6 @@ static int parse_output_paths(struct hda_codec *codec) bool best_wired = true, best_mio = true; bool hp_spk_swapped = false; - fill_all_dac_nids(codec); - best_cfg = kmalloc(sizeof(*best_cfg), GFP_KERNEL); if (!best_cfg) return -ENOMEM; @@ -3428,6 +3426,8 @@ int snd_hda_gen_parse_auto_config(struct hda_codec *codec, cfg = &spec->autocfg; } + fill_all_dac_nids(codec); + if (!cfg->line_outs) { if (cfg->dig_outs || cfg->dig_in_pin) { spec->multiout.max_channels = 2; -- cgit v1.2.3 From a5cc25091c61bb8b4a4bb98207eeb7d0cf312e1c Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 16 Jan 2013 18:08:55 +0100 Subject: ALSA: hda - Properly call automute/switch hooks at init ... and a little bit of code refactoring. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 73900d935681..0d6c4f7c1c85 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -1826,6 +1826,8 @@ get_multiio_path(struct hda_codec *codec, int idx) spec->out_paths[spec->autocfg.line_outs + idx]); } +static void update_automute_all(struct hda_codec *codec); + static int set_multi_io(struct hda_codec *codec, int idx, bool output) { struct hda_gen_spec *spec = codec->spec; @@ -1850,9 +1852,7 @@ static int set_multi_io(struct hda_codec *codec, int idx, bool output) } /* update jack retasking in case it modifies any of them */ - snd_hda_gen_hp_automute(codec, NULL); - snd_hda_gen_line_automute(codec, NULL); - snd_hda_gen_mic_autoswitch(codec, NULL); + update_automute_all(codec); return 0; } @@ -3131,6 +3131,25 @@ void snd_hda_gen_mic_autoswitch(struct hda_codec *codec, struct hda_jack_tbl *ja } EXPORT_SYMBOL_HDA(snd_hda_gen_mic_autoswitch); +/* update jack retasking */ +static void update_automute_all(struct hda_codec *codec) +{ + struct hda_gen_spec *spec = codec->spec; + + if (spec->hp_automute_hook) + spec->hp_automute_hook(codec, NULL); + else + snd_hda_gen_hp_automute(codec, NULL); + if (spec->line_automute_hook) + spec->line_automute_hook(codec, NULL); + else + snd_hda_gen_line_automute(codec, NULL); + if (spec->mic_autoswitch_hook) + spec->mic_autoswitch_hook(codec, NULL); + else + snd_hda_gen_mic_autoswitch(codec, NULL); +} + /* * Auto-Mute mode mixer enum support */ @@ -4281,9 +4300,7 @@ int snd_hda_gen_init(struct hda_codec *codec) clear_unsol_on_unused_pins(codec); /* call init functions of standard auto-mute helpers */ - snd_hda_gen_hp_automute(codec, NULL); - snd_hda_gen_line_automute(codec, NULL); - snd_hda_gen_mic_autoswitch(codec, NULL); + update_automute_all(codec); snd_hda_codec_flush_amp_cache(codec); snd_hda_codec_flush_cmd_cache(codec); -- cgit v1.2.3 From 47b9ddb83b719d35ca0a723410734951b04cf403 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 16 Jan 2013 18:18:00 +0100 Subject: ALSA: hda - Record the current speaker / LO mute status in hda_gen_spec ... to be referred by the codec driver. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 2 ++ sound/pci/hda/hda_generic.h | 2 ++ 2 files changed, 4 insertions(+) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 0d6c4f7c1c85..186a5546fcb8 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -3047,6 +3047,7 @@ void snd_hda_gen_update_outputs(struct hda_codec *codec) else on = spec->hp_jack_present | spec->line_jack_present; on |= spec->master_mute; + spec->speaker_muted = on; do_automute(codec, ARRAY_SIZE(spec->autocfg.speaker_pins), spec->autocfg.speaker_pins, on); @@ -3060,6 +3061,7 @@ void snd_hda_gen_update_outputs(struct hda_codec *codec) else on = spec->hp_jack_present; on |= spec->master_mute; + spec->line_out_muted = on; do_automute(codec, ARRAY_SIZE(spec->autocfg.line_out_pins), spec->autocfg.line_out_pins, on); } diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h index 6ba580540081..d4abf87c969f 100644 --- a/sound/pci/hda/hda_generic.h +++ b/sound/pci/hda/hda_generic.h @@ -170,6 +170,8 @@ struct hda_gen_spec { unsigned int auto_mic:1; unsigned int automute_speaker:1; /* automute speaker outputs */ unsigned int automute_lo:1; /* automute LO outputs */ + unsigned int speaker_muted:1; /* current status of speaker mute */ + unsigned int line_out_muted:1; /* current status of LO mute */ unsigned int detect_hp:1; /* Headphone detection enabled */ unsigned int detect_lo:1; /* Line-out detection enabled */ unsigned int automute_speaker_possible:1; /* there are speakers and either LO or HP */ -- cgit v1.2.3 From f72706be354b35fa3ccdfd64fe609bde4435e12b Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 16 Jan 2013 18:20:07 +0100 Subject: ALSA: hda - Add suppress_auto_mute flag to hda_gen_spec A new flag to skip the auto-mute handling in the generic parser, just like suppress_auto_mic flag. It has to be set before calling snd_hda_gen_parse_auto_config(). Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 6 ++++++ sound/pci/hda/hda_generic.h | 1 + 2 files changed, 7 insertions(+) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 186a5546fcb8..1ea9c2dad950 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -119,6 +119,9 @@ static void parse_user_hints(struct hda_codec *codec) if (val >= 0) codec->single_adc_amp = !!val; + val = snd_hda_get_bool_hint(codec, "auto_mute"); + if (val >= 0) + spec->suppress_auto_mute = !val; val = snd_hda_get_bool_hint(codec, "auto_mic"); if (val >= 0) spec->suppress_auto_mic = !val; @@ -3253,6 +3256,9 @@ static int check_auto_mute_availability(struct hda_codec *codec) int present = 0; int i, err; + if (spec->suppress_auto_mute) + return 0; + if (cfg->hp_pins[0]) present++; if (cfg->line_out_pins[0]) diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h index d4abf87c969f..59d08c60ac47 100644 --- a/sound/pci/hda/hda_generic.h +++ b/sound/pci/hda/hda_generic.h @@ -177,6 +177,7 @@ struct hda_gen_spec { unsigned int automute_speaker_possible:1; /* there are speakers and either LO or HP */ unsigned int automute_lo_possible:1; /* there are line outs and HP */ unsigned int keep_vref_in_automute:1; /* Don't clear VREF in automute */ + unsigned int suppress_auto_mute:1; /* suppress input jack auto mute */ unsigned int suppress_auto_mic:1; /* suppress input jack auto switch */ unsigned int line_in_auto_switch:1; /* allow line-in auto switch */ -- cgit v1.2.3 From acc47aafcfcf16cebcab12ab65efdc0ff3af181c Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 16 Jan 2013 18:28:38 +0100 Subject: ALSA: hda - Give more comments to hda_gen_spec flags Since we have many bit flags in hda_gen_spec, rearrange in sections and give more comments there. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.h | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h index 59d08c60ac47..dd0818b8963b 100644 --- a/sound/pci/hda/hda_generic.h +++ b/sound/pci/hda/hda_generic.h @@ -164,27 +164,34 @@ struct hda_gen_spec { struct automic_entry am_entry[MAX_AUTO_MIC_PINS]; /* for pin sensing */ + /* current status; set in hda_geneic.c */ unsigned int hp_jack_present:1; unsigned int line_jack_present:1; - unsigned int master_mute:1; + unsigned int speaker_muted:1; /* current status of speaker mute */ + unsigned int line_out_muted:1; /* current status of LO mute */ + + /* internal states of automute / autoswitch behavior */ unsigned int auto_mic:1; unsigned int automute_speaker:1; /* automute speaker outputs */ unsigned int automute_lo:1; /* automute LO outputs */ - unsigned int speaker_muted:1; /* current status of speaker mute */ - unsigned int line_out_muted:1; /* current status of LO mute */ + + /* capabilities detected by parser */ unsigned int detect_hp:1; /* Headphone detection enabled */ unsigned int detect_lo:1; /* Line-out detection enabled */ unsigned int automute_speaker_possible:1; /* there are speakers and either LO or HP */ unsigned int automute_lo_possible:1; /* there are line outs and HP */ + + /* additional parameters set by codec drivers */ + unsigned int master_mute:1; /* master mute over all */ unsigned int keep_vref_in_automute:1; /* Don't clear VREF in automute */ + unsigned int line_in_auto_switch:1; /* allow line-in auto switch */ + + /* parser behavior flags; set before snd_hda_gen_parse_auto_config() */ unsigned int suppress_auto_mute:1; /* suppress input jack auto mute */ unsigned int suppress_auto_mic:1; /* suppress input jack auto switch */ - unsigned int line_in_auto_switch:1; /* allow line-in auto switch */ - /* other flags */ + /* other parse behavior flags */ unsigned int need_dac_fix:1; /* need to limit DACs for multi channels */ - unsigned int no_analog:1; /* digital I/O only */ - unsigned int dyn_adc_switch:1; /* switch ADCs (for ALC275) */ unsigned int shared_mic_hp:1; /* HP/Mic-in sharing */ unsigned int no_primary_hp:1; /* Don't prefer HP pins to speaker pins */ unsigned int multi_cap_vol:1; /* allow multiple capture xxx volumes */ @@ -192,11 +199,15 @@ struct hda_gen_spec { unsigned int own_eapd_ctl:1; /* set EAPD by own function */ unsigned int vmaster_mute_enum:1; /* add vmaster mute mode enum */ unsigned int indep_hp:1; /* independent HP supported */ - unsigned int indep_hp_enabled:1; /* independent HP enabled */ unsigned int prefer_hp_amp:1; /* enable HP amp for speaker if any */ unsigned int add_stereo_mix_input:1; /* add aamix as a capture src */ unsigned int add_out_jack_modes:1; /* add output jack mode enum ctls */ + /* other internal flags */ + unsigned int no_analog:1; /* digital I/O only */ + unsigned int dyn_adc_switch:1; /* switch ADCs (for ALC275) */ + unsigned int indep_hp_enabled:1; /* independent HP enabled */ + /* loopback mixing mode */ bool aamix_mode; -- cgit v1.2.3 From f6655d52a36ccb27e1cf918dfa7221923964ab21 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 17 Jan 2013 08:49:01 +0100 Subject: ALSA: hda - Minor cleanup/fixes for patch_sigmatel.c fixup transition - spec->hp_detect has to be overridden in HDA_FIXUP_ACT_PARSE, not in PRE_PARSE. - Remove err == 0 check but return directly -EINVAL from stac92xx_parse_auto_config() - Set spec->default_polarity for 92HD71bxx - Some code shuffles Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_sigmatel.c | 40 +++++++++++++--------------------------- 1 file changed, 13 insertions(+), 27 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index a7eed73ab34e..f9757356a1fe 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -1941,7 +1941,7 @@ static void stac92hd73xx_fixup_no_jd(struct hda_codec *codec, { struct sigmatel_spec *spec = codec->spec; - if (action != HDA_FIXUP_ACT_PRE_PROBE) + if (action != HDA_FIXUP_ACT_PROBE) return; spec->hp_detect = 0; } @@ -2502,14 +2502,14 @@ static void stac92hd71bxx_fixup_hp_dv5(struct hda_codec *codec, case HDA_FIXUP_ACT_PRE_PROBE: snd_hda_codec_set_pincfg(codec, 0x0d, 0x90170010); stac92xx_auto_set_pinctl(codec, 0x0d, AC_PINCTL_OUT_EN); + break; + + case HDA_FIXUP_ACT_PROBE: /* HP dv6 gives the headphone pin as a line-out. Thus we * need to set hp_detect flag here to force to enable HP * detection. */ spec->hp_detect = 1; - break; - - case HDA_FIXUP_ACT_PROBE: /* enable bass on HP dv7 */ cap = snd_hda_param_read(codec, 0x1, AC_PAR_GPIO_CAP); cap &= AC_GPIO_IO_COUNT; @@ -2559,7 +2559,7 @@ static void stac92hd71bxx_fixup_hp(struct hda_codec *codec, } } - if (find_mute_led_cfg(codec, 1)) + if (find_mute_led_cfg(codec, spec->default_polarity)) snd_printd("mute LED gpio %d polarity %d\n", spec->gpio_led, spec->gpio_led_polarity); @@ -5159,7 +5159,7 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec) spec->dmic_nids)) < 0) return err; if (! spec->autocfg.line_outs) - return 0; /* can't find valid pin config */ + return -EINVAL; /* can't find valid pin config */ /* If we have no real line-out pin and multiple hp-outs, HPs should * be set up as multi-channel outputs. @@ -5362,7 +5362,7 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec) spec->dinput_mux = &spec->private_dimux; spec->sinput_mux = &spec->private_smux; spec->mono_mux = &spec->private_mono_mux; - return 1; + return 0; } /* add playback controls for HP output */ @@ -5468,7 +5468,7 @@ static int stac9200_parse_auto_config(struct hda_codec *codec) spec->input_mux = &spec->private_imux; spec->dinput_mux = &spec->private_dimux; - return 1; + return 0; } /* @@ -6531,8 +6531,6 @@ static int patch_stac925x(struct hda_codec *codec) snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); err = stac92xx_parse_auto_config(codec); - if (!err) - err = -EINVAL; if (err < 0) { stac92xx_free(codec); return err; @@ -6621,9 +6619,6 @@ static int patch_stac92hd73xx(struct hda_codec *codec) snd_hda_add_verbs(codec, stac92hd73xx_core_init); err = stac92xx_parse_auto_config(codec); - - if (!err) - err = -EINVAL; if (err < 0) { stac92xx_free(codec); return err; @@ -6833,8 +6828,6 @@ static int patch_stac92hd83xxx(struct hda_codec *codec) stac_setup_gpio(codec); err = stac92xx_parse_auto_config(codec); - if (!err) - err = -EINVAL; if (err < 0) { stac92xx_free(codec); return err; @@ -7024,15 +7017,14 @@ static int patch_stac92hd71bxx(struct hda_codec *codec) spec->num_dmuxes = ARRAY_SIZE(stac92hd71bxx_dmux_nids); spec->num_smuxes = stac92hd71bxx_connected_smuxes(codec, 0x1e); + spec->multiout.dac_nids = spec->dac_nids; + spec->default_polarity = 1; + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); stac_setup_gpio(codec); - spec->multiout.dac_nids = spec->dac_nids; - err = stac92xx_parse_auto_config(codec); - if (!err) - err = -EINVAL; if (err < 0) { stac92xx_free(codec); return err; @@ -7079,8 +7071,6 @@ static int patch_stac922x(struct hda_codec *codec) snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); err = stac92xx_parse_auto_config(codec); - if (!err) - err = -EINVAL; if (err < 0) { stac92xx_free(codec); return err; @@ -7144,14 +7134,12 @@ static int patch_stac927x(struct hda_codec *codec) spec->aloopback_shift = 0; spec->eapd_switch = 1; + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); + if (!spec->volknob_init) snd_hda_add_verbs(codec, stac927x_core_init); - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); - err = stac92xx_parse_auto_config(codec); - if (!err) - err = -EINVAL; if (err < 0) { stac92xx_free(codec); return err; @@ -7228,8 +7216,6 @@ static int patch_stac9205(struct hda_codec *codec) snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); err = stac92xx_parse_auto_config(codec); - if (!err) - err = -EINVAL; if (err < 0) { stac92xx_free(codec); return err; -- cgit v1.2.3 From 29476558deb3017993366a3b0b45dff2acad495b Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 17 Jan 2013 09:52:11 +0100 Subject: ALSA: hda - Add input jack mode enum controls to generic parser Just like the jack mode enum ctls for output jacks, add the support for similar enum ctls for input pins to control the bias Vref. The new controls will be added when spec->add_in_jack_modes is set either by the codec driver or by a hint string. Note that ground and 100% vrefs are excluded from the list for simplicity, currently. We may add a new flag to allow them, too. But I guess it's easier to put a value override in the pinfix in such a case. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 140 ++++++++++++++++++++++++++++++++++++++++++++ sound/pci/hda/hda_generic.h | 1 + 2 files changed, 141 insertions(+) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 1ea9c2dad950..878556b8ea32 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include "hda_codec.h" @@ -149,6 +150,9 @@ static void parse_user_hints(struct hda_codec *codec) val = snd_hda_get_bool_hint(codec, "add_out_jack_modes"); if (val >= 0) spec->add_out_jack_modes = !!val; + val = snd_hda_get_bool_hint(codec, "add_in_jack_modes"); + if (val >= 0) + spec->add_in_jack_modes = !!val; if (!snd_hda_get_int_hint(codec, "mixer_nid", &val)) spec->mixer_nid = val; @@ -2138,6 +2142,136 @@ static int create_out_jack_modes(struct hda_codec *codec, int num_pins, return 0; } +/* + * input jack mode + */ + +/* from AC_PINCTL_VREF_HIZ to AC_PINCTL_VREF_100 */ +#define NUM_VREFS 6 + +static const char * const vref_texts[NUM_VREFS] = { + "Line In", "Mic 50pc Bias", "Mic 0V Bias", + "", "Mic 80pc Bias", "Mic 100pc Bias" +}; + +static unsigned int get_vref_caps(struct hda_codec *codec, hda_nid_t pin) +{ + unsigned int pincap; + + pincap = snd_hda_query_pin_caps(codec, pin); + pincap = (pincap & AC_PINCAP_VREF) >> AC_PINCAP_VREF_SHIFT; + /* filter out unusual vrefs */ + pincap &= ~(AC_PINCAP_VREF_GRD | AC_PINCAP_VREF_100); + return pincap; +} + +/* convert from the enum item index to the vref ctl index (0=HIZ, 1=50%...) */ +static int get_vref_idx(unsigned int vref_caps, unsigned int item_idx) +{ + unsigned int i, n = 0; + + for (i = 0; i < NUM_VREFS; i++) { + if (vref_caps & (1 << i)) { + if (n == item_idx) + return i; + n++; + } + } + return 0; +} + +/* convert back from the vref ctl index to the enum item index */ +static int cvt_from_vref_idx(unsigned int vref_caps, unsigned int idx) +{ + unsigned int i, n = 0; + + for (i = 0; i < NUM_VREFS; i++) { + if (i == idx) + return n; + if (vref_caps & (1 << i)) + n++; + } + return 0; +} + +static int in_jack_mode_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + hda_nid_t nid = kcontrol->private_value; + unsigned int vref_caps = get_vref_caps(codec, nid); + + snd_hda_enum_helper_info(kcontrol, uinfo, hweight32(vref_caps), + vref_texts); + /* set the right text */ + strcpy(uinfo->value.enumerated.name, + vref_texts[get_vref_idx(vref_caps, uinfo->value.enumerated.item)]); + return 0; +} + +static int in_jack_mode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + hda_nid_t nid = kcontrol->private_value; + unsigned int vref_caps = get_vref_caps(codec, nid); + unsigned int idx; + + idx = snd_hda_codec_get_pin_target(codec, nid) & AC_PINCTL_VREFEN; + ucontrol->value.enumerated.item[0] = cvt_from_vref_idx(vref_caps, idx); + return 0; +} + +static int in_jack_mode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + hda_nid_t nid = kcontrol->private_value; + unsigned int vref_caps = get_vref_caps(codec, nid); + unsigned int val, idx; + + val = snd_hda_codec_get_pin_target(codec, nid); + idx = cvt_from_vref_idx(vref_caps, val & AC_PINCTL_VREFEN); + if (idx == ucontrol->value.enumerated.item[0]) + return 0; + + val &= ~AC_PINCTL_VREFEN; + val |= get_vref_idx(vref_caps, ucontrol->value.enumerated.item[0]); + snd_hda_set_pin_ctl_cache(codec, nid, val); + return 1; +} + +static const struct snd_kcontrol_new in_jack_mode_enum = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .info = in_jack_mode_info, + .get = in_jack_mode_get, + .put = in_jack_mode_put, +}; + +static int create_in_jack_mode(struct hda_codec *codec, hda_nid_t pin) +{ + struct hda_gen_spec *spec = codec->spec; + unsigned int defcfg; + struct snd_kcontrol_new *knew; + char name[44]; + + /* no jack mode for fixed pins */ + defcfg = snd_hda_codec_get_pincfg(codec, pin); + if (snd_hda_get_input_pin_attr(defcfg) == INPUT_PIN_ATTR_INT) + return 0; + + /* no multiple vref caps? */ + if (hweight32(get_vref_caps(codec, pin)) <= 1) + return 0; + + get_jack_mode_name(codec, pin, name, sizeof(name)); + knew = snd_hda_gen_add_kctl(spec, name, &in_jack_mode_enum); + if (!knew) + return -ENOMEM; + knew->private_value = pin; + return 0; +} + /* * Parse input paths @@ -2392,6 +2526,12 @@ static int create_input_ctls(struct hda_codec *codec) err = parse_capture_source(codec, pin, num_adcs, label, -mixer); if (err < 0) return err; + + if (spec->add_in_jack_modes) { + err = create_in_jack_mode(codec, pin); + if (err < 0) + return err; + } } if (mixer && spec->add_stereo_mix_input) { diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h index dd0818b8963b..142a571ecd5b 100644 --- a/sound/pci/hda/hda_generic.h +++ b/sound/pci/hda/hda_generic.h @@ -202,6 +202,7 @@ struct hda_gen_spec { unsigned int prefer_hp_amp:1; /* enable HP amp for speaker if any */ unsigned int add_stereo_mix_input:1; /* add aamix as a capture src */ unsigned int add_out_jack_modes:1; /* add output jack mode enum ctls */ + unsigned int add_in_jack_modes:1; /* add input jack mode enum ctls */ /* other internal flags */ unsigned int no_analog:1; /* digital I/O only */ -- cgit v1.2.3 From 7a71bbf310cda13a713aab0c1dcf888707a54286 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 17 Jan 2013 10:25:15 +0100 Subject: ALSA: hda - Move vmaster TLV parsing to snd_hda_gen_parse_auto_config() Add vmaster_tlv[] to hda_gen_spec and store the suggested TLV data in snd_hda_gen_parse_auto_config(). This allows the codec driver to correct the TLV data (e.g. mute capability) before actually creating vmaster instance. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 8 ++++---- sound/pci/hda/hda_generic.h | 1 + 2 files changed, 5 insertions(+), 4 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 878556b8ea32..9c06749738b1 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -1601,6 +1601,9 @@ static int parse_output_paths(struct hda_codec *codec) path = snd_hda_get_path_from_idx(codec, spec->out_paths[0]); if (path) spec->vmaster_nid = look_for_out_vol_nid(codec, path); + if (spec->vmaster_nid) + snd_hda_set_vmaster_tlv(codec, spec->vmaster_nid, + HDA_OUTPUT, spec->vmaster_tlv); } kfree(best_cfg); @@ -3752,11 +3755,8 @@ int snd_hda_gen_build_controls(struct hda_codec *codec) /* if we have no master control, let's create it */ if (!spec->no_analog && !snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) { - unsigned int vmaster_tlv[4]; - snd_hda_set_vmaster_tlv(codec, spec->vmaster_nid, - HDA_OUTPUT, vmaster_tlv); err = snd_hda_add_vmaster(codec, "Master Playback Volume", - vmaster_tlv, slave_pfxs, + spec->vmaster_tlv, slave_pfxs, "Playback Volume"); if (err < 0) return err; diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h index 142a571ecd5b..43a8482ed2b3 100644 --- a/sound/pci/hda/hda_generic.h +++ b/sound/pci/hda/hda_generic.h @@ -214,6 +214,7 @@ struct hda_gen_spec { /* for virtual master */ hda_nid_t vmaster_nid; + unsigned int vmaster_tlv[4]; struct hda_vmaster_mute_hook vmaster_mute; #ifdef CONFIG_PM struct hda_loopback_check loopback; -- cgit v1.2.3 From 0ffd534eb164fbc87e91fc54a217247ea0cfbabc Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 17 Jan 2013 15:53:29 +0100 Subject: ALSA: hda - Record all detected ADCs in hda_gen_spec Since the generic parser reduces the ADC list, copy the list of the all detected ADCs and keep it. This list can be later referred by the codec driver for finer power controls. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 5 +++++ sound/pci/hda/hda_generic.h | 4 +++- 2 files changed, 8 insertions(+), 1 deletion(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 9c06749738b1..fc3f24113ce3 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -2368,6 +2368,11 @@ static int fill_adc_nids(struct hda_codec *codec) break; } spec->num_adc_nids = nums; + + /* copy the detected ADCs to all_adcs[] */ + spec->num_all_adcs = nums; + memcpy(spec->all_adcs, spec->adc_nids, nums * sizeof(hda_nid_t)); + return nums; } diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h index 43a8482ed2b3..a0486b1b4da4 100644 --- a/sound/pci/hda/hda_generic.h +++ b/sound/pci/hda/hda_generic.h @@ -142,9 +142,11 @@ struct hda_gen_spec { unsigned int dyn_adc_idx[HDA_MAX_NUM_INPUTS]; hda_nid_t shared_mic_vref_pin; - /* DAC list */ + /* DAC/ADC lists */ int num_all_dacs; hda_nid_t all_dacs[16]; + int num_all_adcs; + hda_nid_t all_adcs[AUTO_CFG_MAX_OUTS]; /* path list */ struct snd_array paths; -- cgit v1.2.3 From ac2e87366c18d49c6dc12e89ae4f4512f126eeb4 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 17 Jan 2013 15:57:10 +0100 Subject: ALSA: hda - Add PCM capture hook to hda_gen_spec Not only PCM playback, a hook for PCM capture would be required for power controls in codec drivers. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 63 +++++++++++++++++++++++++++++++++++++++++++++ sound/pci/hda/hda_generic.h | 6 ++++- 2 files changed, 68 insertions(+), 1 deletion(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index fc3f24113ce3..f946714c7d3c 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -3815,6 +3815,16 @@ static void call_pcm_playback_hook(struct hda_pcm_stream *hinfo, spec->pcm_playback_hook(hinfo, codec, substream, action); } +static void call_pcm_capture_hook(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream, + int action) +{ + struct hda_gen_spec *spec = codec->spec; + if (spec->pcm_capture_hook) + spec->pcm_capture_hook(hinfo, codec, substream, action); +} + /* * Analog playback callbacks */ @@ -3882,6 +3892,44 @@ static int playback_pcm_close(struct hda_pcm_stream *hinfo, return 0; } +static int capture_pcm_open(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + call_pcm_capture_hook(hinfo, codec, substream, HDA_GEN_PCM_ACT_OPEN); + return 0; +} + +static int capture_pcm_prepare(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + unsigned int stream_tag, + unsigned int format, + struct snd_pcm_substream *substream) +{ + snd_hda_codec_setup_stream(codec, hinfo->nid, stream_tag, 0, format); + call_pcm_capture_hook(hinfo, codec, substream, + HDA_GEN_PCM_ACT_PREPARE); + return 0; +} + +static int capture_pcm_cleanup(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + snd_hda_codec_cleanup_stream(codec, hinfo->nid); + call_pcm_capture_hook(hinfo, codec, substream, + HDA_GEN_PCM_ACT_CLEANUP); + return 0; +} + +static int capture_pcm_close(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + call_pcm_capture_hook(hinfo, codec, substream, HDA_GEN_PCM_ACT_CLOSE); + return 0; +} + static int alt_playback_pcm_open(struct hda_pcm_stream *hinfo, struct hda_codec *codec, struct snd_pcm_substream *substream) @@ -3976,6 +4024,9 @@ static int dig_playback_pcm_close(struct hda_pcm_stream *hinfo, /* * Analog capture */ +#define alt_capture_pcm_open capture_pcm_open +#define alt_capture_pcm_close capture_pcm_close + static int alt_capture_pcm_prepare(struct hda_pcm_stream *hinfo, struct hda_codec *codec, unsigned int stream_tag, @@ -3986,6 +4037,8 @@ static int alt_capture_pcm_prepare(struct hda_pcm_stream *hinfo, snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number + 1], stream_tag, 0, format); + call_pcm_capture_hook(hinfo, codec, substream, + HDA_GEN_PCM_ACT_PREPARE); return 0; } @@ -3997,6 +4050,8 @@ static int alt_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number + 1]); + call_pcm_capture_hook(hinfo, codec, substream, + HDA_GEN_PCM_ACT_CLEANUP); return 0; } @@ -4020,6 +4075,12 @@ static const struct hda_pcm_stream pcm_analog_capture = { .channels_min = 2, .channels_max = 2, /* NID is set in build_pcms */ + .ops = { + .open = capture_pcm_open, + .close = capture_pcm_close, + .prepare = capture_pcm_prepare, + .cleanup = capture_pcm_cleanup + }, }; static const struct hda_pcm_stream pcm_analog_alt_playback = { @@ -4041,6 +4102,8 @@ static const struct hda_pcm_stream pcm_analog_alt_capture = { .channels_max = 2, /* NID is set in build_pcms */ .ops = { + .open = alt_capture_pcm_open, + .close = alt_capture_pcm_close, .prepare = alt_capture_pcm_prepare, .cleanup = alt_capture_pcm_cleanup }, diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h index a0486b1b4da4..7b14e9ce7486 100644 --- a/sound/pci/hda/hda_generic.h +++ b/sound/pci/hda/hda_generic.h @@ -233,11 +233,15 @@ struct hda_gen_spec { void (*automute_hook)(struct hda_codec *codec); void (*cap_sync_hook)(struct hda_codec *codec); - /* PCM playback hook */ + /* PCM hooks */ void (*pcm_playback_hook)(struct hda_pcm_stream *hinfo, struct hda_codec *codec, struct snd_pcm_substream *substream, int action); + void (*pcm_capture_hook)(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream, + int action); /* automute / autoswitch hooks */ void (*hp_automute_hook)(struct hda_codec *codec, -- cgit v1.2.3 From 247d85ee068610c50d66ee0cd3130e02c69f5f2e Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 17 Jan 2013 16:18:11 +0100 Subject: ALSA: hda - Improve naming rule for primary output When the volume or mute control of the primary output is shared with other (headphone or speaker) outputs, we shouldn't name it as a specific output type but rather name it with the channel name or a generic name like "PCM". Also, this check should be performed individually for the volume and the mute controls because some codecs may have shared volumes but separate mute controls. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 59 ++++++++++++++++++++++++++++++++------------- 1 file changed, 42 insertions(+), 17 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index f946714c7d3c..ef4c04adbc21 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -825,19 +825,27 @@ static int add_stereo_sw(struct hda_codec *codec, const char *pfx, return add_sw_ctl(codec, pfx, cidx, chs, path); } +/* any ctl assigned to the path with the given index? */ +static bool path_has_mixer(struct hda_codec *codec, int path_idx, int ctl_type) +{ + struct nid_path *path = snd_hda_get_path_from_idx(codec, path_idx); + return path && path->ctls[ctl_type]; +} + static const char * const channel_name[4] = { "Front", "Surround", "CLFE", "Side" }; /* give some appropriate ctl name prefix for the given line out channel */ -static const char *get_line_out_pfx(struct hda_gen_spec *spec, int ch, - bool can_be_master, int *index) +static const char *get_line_out_pfx(struct hda_codec *codec, int ch, + int *index, int ctl_type) { + struct hda_gen_spec *spec = codec->spec; struct auto_pin_cfg *cfg = &spec->autocfg; *index = 0; if (cfg->line_outs == 1 && !spec->multi_ios && - !cfg->hp_outs && !cfg->speaker_outs && can_be_master) + !cfg->hp_outs && !cfg->speaker_outs) return spec->vmaster_mute.hook ? "PCM" : "Master"; /* if there is really a single DAC used in the whole output paths, @@ -847,24 +855,41 @@ static const char *get_line_out_pfx(struct hda_gen_spec *spec, int ch, !spec->multiout.hp_out_nid[0] && !spec->multiout.extra_out_nid[0]) return spec->vmaster_mute.hook ? "PCM" : "Master"; + /* multi-io channels */ + if (ch >= cfg->line_outs) + return channel_name[ch]; + switch (cfg->line_out_type) { case AUTO_PIN_SPEAKER_OUT: + /* if the primary channel vol/mute is shared with HP volume, + * don't name it as Speaker + */ + if (!ch && cfg->hp_outs && + !path_has_mixer(codec, spec->hp_paths[0], ctl_type)) + break; if (cfg->line_outs == 1) return "Speaker"; if (cfg->line_outs == 2) return ch ? "Bass Speaker" : "Speaker"; break; case AUTO_PIN_HP_OUT: + /* if the primary channel vol/mute is shared with spk volume, + * don't name it as Headphone + */ + if (!ch && cfg->speaker_outs && + !path_has_mixer(codec, spec->speaker_paths[0], ctl_type)) + break; /* for multi-io case, only the primary out */ if (ch && spec->multi_ios) break; *index = ch; return "Headphone"; - default: - if (cfg->line_outs == 1 && !spec->multi_ios) - return "PCM"; - break; } + + /* for a single channel output, we don't have to name the channel */ + if (cfg->line_outs == 1 && !spec->multi_ios) + return "PCM"; + if (ch >= ARRAY_SIZE(channel_name)) { snd_BUG(); return "PCM"; @@ -1626,16 +1651,11 @@ static int create_multi_out_ctls(struct hda_codec *codec, int index; struct nid_path *path; - if (i >= cfg->line_outs) { - index = 0; - name = channel_name[i]; - } else { - name = get_line_out_pfx(spec, i, true, &index); - } - path = snd_hda_get_path_from_idx(codec, spec->out_paths[i]); if (!path) continue; + + name = get_line_out_pfx(codec, i, &index, NID_PATH_VOL_CTL); if (!name || !strcmp(name, "CLFE")) { /* Center/LFE */ err = add_vol_ctl(codec, "Center", 0, 1, path); @@ -1644,6 +1664,14 @@ static int create_multi_out_ctls(struct hda_codec *codec, err = add_vol_ctl(codec, "LFE", 0, 2, path); if (err < 0) return err; + } else { + err = add_stereo_vol(codec, name, index, path); + if (err < 0) + return err; + } + + name = get_line_out_pfx(codec, i, &index, NID_PATH_MUTE_CTL); + if (!name || !strcmp(name, "CLFE")) { err = add_sw_ctl(codec, "Center", 0, 1, path); if (err < 0) return err; @@ -1651,9 +1679,6 @@ static int create_multi_out_ctls(struct hda_codec *codec, if (err < 0) return err; } else { - err = add_stereo_vol(codec, name, index, path); - if (err < 0) - return err; err = add_stereo_sw(codec, name, index, path); if (err < 0) return err; -- cgit v1.2.3 From 36c9db7a1aca3396b26004a3c41f27e2b735536f Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 17 Jan 2013 17:07:59 +0100 Subject: ALSA: hda - Use generic parser for STAC/IDT codec driver Finally we reached here. All codecs driver (except for CA0132, which has really device-specific requirements) have been converted to use the generic parser. This patch appears bigger than others since it also involves with the code shuffling, but mostly the cut-off of parser codes and adapt to the generic parser flags. Most of fixup codecs haven't been changed but just removed a few unnecessary codes. The only missing stuff is the SPDIF mux control. It'll be added again later. Signed-off-by: Takashi Iwai --- sound/pci/hda/Kconfig | 1 + sound/pci/hda/patch_sigmatel.c | 5219 ++++++++-------------------------------- 2 files changed, 1019 insertions(+), 4201 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig index 4004d405e082..4466bf63d6bf 100644 --- a/sound/pci/hda/Kconfig +++ b/sound/pci/hda/Kconfig @@ -112,6 +112,7 @@ config SND_HDA_CODEC_ANALOG config SND_HDA_CODEC_SIGMATEL bool "Build IDT/Sigmatel HD-audio codec support" default y + select SND_HDA_GENERIC help Say Y here to include IDT (Sigmatel) HD-audio codec support in snd-hda-intel driver, such as STAC9200. diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index f9757356a1fe..9d2dfad7f0bc 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -39,14 +39,11 @@ #include "hda_auto_parser.h" #include "hda_beep.h" #include "hda_jack.h" +#include "hda_generic.h" enum { - STAC_VREF_EVENT = 1, - STAC_INSERT_EVENT, + STAC_VREF_EVENT = 8, STAC_PWR_EVENT, - STAC_HP_EVENT, - STAC_LO_EVENT, - STAC_MIC_EVENT, }; enum { @@ -115,7 +112,6 @@ enum { STAC_HP_DV4, STAC_HP_DV5, STAC_HP_HDX, - STAC_HP_DV4_1222NR, STAC_92HD71BXX_HP, STAC_92HD71BXX_NO_DMIC, STAC_92HD71BXX_NO_SMUX, @@ -173,30 +169,14 @@ enum { STAC_9872_MODELS }; -struct sigmatel_mic_route { - hda_nid_t pin; - signed char mux_idx; - signed char dmux_idx; -}; - -#define MAX_PINS_NUM 16 -#define MAX_ADCS_NUM 4 -#define MAX_DMICS_NUM 4 - struct sigmatel_spec { - struct snd_kcontrol_new *mixers[4]; - unsigned int num_mixers; + struct hda_gen_spec gen; unsigned int eapd_switch: 1; - unsigned int surr_switch: 1; - unsigned int alt_switch: 1; - unsigned int hp_detect: 1; - unsigned int spdif_mute: 1; - unsigned int check_volume_offset:1; - unsigned int auto_mic:1; unsigned int linear_tone_beep:1; unsigned int headset_jack:1; /* 4-pin headset jack (hp + mono mic) */ unsigned int volknob_init:1; /* special volume-knob initialization */ + unsigned int powerdown_adcs:1; /* gpio lines */ unsigned int eapd_mask; @@ -218,6 +198,7 @@ struct sigmatel_spec { /* analog loopback */ const struct snd_kcontrol_new *aloopback_ctl; + unsigned int aloopback; unsigned char aloopback_mask; unsigned char aloopback_shift; @@ -225,458 +206,130 @@ struct sigmatel_spec { unsigned int power_map_bits; unsigned int num_pwrs; const hda_nid_t *pwr_nids; - const hda_nid_t *dac_list; - - /* playback */ - struct hda_input_mux *mono_mux; - unsigned int cur_mmux; - struct hda_multi_out multiout; - hda_nid_t dac_nids[5]; - hda_nid_t hp_dacs[5]; - hda_nid_t speaker_dacs[5]; - - int volume_offset; - - /* capture */ - const hda_nid_t *adc_nids; - unsigned int num_adcs; - const hda_nid_t *mux_nids; - unsigned int num_muxes; - const hda_nid_t *dmic_nids; - unsigned int num_dmics; - const hda_nid_t *dmux_nids; - unsigned int num_dmuxes; - const hda_nid_t *smux_nids; - unsigned int num_smuxes; - unsigned int num_analog_muxes; - - const unsigned long *capvols; /* amp-volume attr: HDA_COMPOSE_AMP_VAL() */ - const unsigned long *capsws; /* amp-mute attr: HDA_COMPOSE_AMP_VAL() */ - unsigned int num_caps; /* number of capture volume/switch elements */ - - struct sigmatel_mic_route ext_mic; - struct sigmatel_mic_route int_mic; - struct sigmatel_mic_route dock_mic; - - const char * const *spdif_labels; - - hda_nid_t dig_in_nid; - hda_nid_t mono_nid; + unsigned int active_adcs; + + /* beep widgets */ hda_nid_t anabeep_nid; hda_nid_t digbeep_nid; - - /* pin widgets */ - const hda_nid_t *pin_nids; - unsigned int num_pins; - - /* codec specific stuff */ - const struct hda_verb *init; - const struct snd_kcontrol_new *mixer; - - /* capture source */ - struct hda_input_mux *dinput_mux; - unsigned int cur_dmux[2]; - struct hda_input_mux *input_mux; - unsigned int cur_mux[3]; - struct hda_input_mux *sinput_mux; - unsigned int cur_smux[2]; - unsigned int cur_amux; - hda_nid_t *amp_nids; - unsigned int powerdown_adcs; - - /* i/o switches */ - unsigned int io_switch[2]; - unsigned int clfe_swap; - hda_nid_t line_switch; /* shared line-in for input and output */ - hda_nid_t mic_switch; /* shared mic-in for input and output */ - hda_nid_t hp_switch; /* NID of HP as line-out */ - unsigned int aloopback; - - struct hda_pcm pcm_rec[2]; /* PCM information */ - - /* dynamic controls and input_mux */ - struct auto_pin_cfg autocfg; - struct snd_array kctls; - struct hda_input_mux private_dimux; - struct hda_input_mux private_imux; - struct hda_input_mux private_smux; - struct hda_input_mux private_mono_mux; - - /* auto spec */ - unsigned auto_pin_cnt; - hda_nid_t auto_pin_nids[MAX_PINS_NUM]; - unsigned auto_adc_cnt; - hda_nid_t auto_adc_nids[MAX_ADCS_NUM]; - hda_nid_t auto_mux_nids[MAX_ADCS_NUM]; - hda_nid_t auto_dmux_nids[MAX_ADCS_NUM]; - unsigned long auto_capvols[MAX_ADCS_NUM]; - unsigned auto_dmic_cnt; - hda_nid_t auto_dmic_nids[MAX_DMICS_NUM]; - - struct hda_vmaster_mute_hook vmaster_mute; }; #define AC_VERB_IDT_SET_POWER_MAP 0x7ec #define AC_VERB_IDT_GET_POWER_MAP 0xfec -static const hda_nid_t stac9200_adc_nids[1] = { - 0x03, -}; - -static const hda_nid_t stac9200_mux_nids[1] = { - 0x0c, -}; - -static const hda_nid_t stac9200_dac_nids[1] = { - 0x02, -}; - static const hda_nid_t stac92hd73xx_pwr_nids[8] = { 0x0a, 0x0b, 0x0c, 0xd, 0x0e, 0x0f, 0x10, 0x11 }; -static const hda_nid_t stac92hd73xx_slave_dig_outs[2] = { - 0x26, 0, -}; - -static const hda_nid_t stac92hd73xx_adc_nids[2] = { - 0x1a, 0x1b -}; - -#define STAC92HD73XX_NUM_DMICS 2 -static const hda_nid_t stac92hd73xx_dmic_nids[STAC92HD73XX_NUM_DMICS + 1] = { - 0x13, 0x14, 0 -}; - -#define STAC92HD73_DAC_COUNT 5 - -static const hda_nid_t stac92hd73xx_mux_nids[2] = { - 0x20, 0x21, -}; - -static const hda_nid_t stac92hd73xx_dmux_nids[2] = { - 0x20, 0x21, -}; - -static const hda_nid_t stac92hd73xx_smux_nids[2] = { - 0x22, 0x23, -}; - -#define STAC92HD73XX_NUM_CAPS 2 -static const unsigned long stac92hd73xx_capvols[] = { - HDA_COMPOSE_AMP_VAL(0x20, 3, 0, HDA_OUTPUT), - HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT), -}; -#define stac92hd73xx_capsws stac92hd73xx_capvols - -#define STAC92HD83_DAC_COUNT 3 - static const hda_nid_t stac92hd83xxx_pwr_nids[7] = { 0x0a, 0x0b, 0x0c, 0xd, 0x0e, 0x0f, 0x10 }; -static const hda_nid_t stac92hd83xxx_slave_dig_outs[2] = { - 0x1e, 0, -}; - -static const hda_nid_t stac92hd83xxx_dmic_nids[] = { - 0x11, 0x20, -}; - static const hda_nid_t stac92hd71bxx_pwr_nids[3] = { 0x0a, 0x0d, 0x0f }; -static const hda_nid_t stac92hd71bxx_adc_nids[2] = { - 0x12, 0x13, -}; - -static const hda_nid_t stac92hd71bxx_mux_nids[2] = { - 0x1a, 0x1b -}; - -static const hda_nid_t stac92hd71bxx_dmux_nids[2] = { - 0x1c, 0x1d, -}; - -static const hda_nid_t stac92hd71bxx_smux_nids[2] = { - 0x24, 0x25, -}; - -#define STAC92HD71BXX_NUM_DMICS 2 -static const hda_nid_t stac92hd71bxx_dmic_nids[STAC92HD71BXX_NUM_DMICS + 1] = { - 0x18, 0x19, 0 -}; - -static const hda_nid_t stac92hd71bxx_dmic_5port_nids[STAC92HD71BXX_NUM_DMICS] = { - 0x18, 0 -}; - -static const hda_nid_t stac92hd71bxx_slave_dig_outs[2] = { - 0x22, 0 -}; - -#define STAC92HD71BXX_NUM_CAPS 2 -static const unsigned long stac92hd71bxx_capvols[] = { - HDA_COMPOSE_AMP_VAL(0x1c, 3, 0, HDA_OUTPUT), - HDA_COMPOSE_AMP_VAL(0x1d, 3, 0, HDA_OUTPUT), -}; -#define stac92hd71bxx_capsws stac92hd71bxx_capvols - -static const hda_nid_t stac925x_adc_nids[1] = { - 0x03, -}; - -static const hda_nid_t stac925x_mux_nids[1] = { - 0x0f, -}; - -static const hda_nid_t stac925x_dac_nids[1] = { - 0x02, -}; - -#define STAC925X_NUM_DMICS 1 -static const hda_nid_t stac925x_dmic_nids[STAC925X_NUM_DMICS + 1] = { - 0x15, 0 -}; - -static const hda_nid_t stac925x_dmux_nids[1] = { - 0x14, -}; - -static const unsigned long stac925x_capvols[] = { - HDA_COMPOSE_AMP_VAL(0x09, 3, 0, HDA_OUTPUT), -}; -static const unsigned long stac925x_capsws[] = { - HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT), -}; - -static const hda_nid_t stac922x_adc_nids[2] = { - 0x06, 0x07, -}; - -static const hda_nid_t stac922x_mux_nids[2] = { - 0x12, 0x13, -}; - -#define STAC922X_NUM_CAPS 2 -static const unsigned long stac922x_capvols[] = { - HDA_COMPOSE_AMP_VAL(0x17, 3, 0, HDA_INPUT), - HDA_COMPOSE_AMP_VAL(0x18, 3, 0, HDA_INPUT), -}; -#define stac922x_capsws stac922x_capvols - -static const hda_nid_t stac927x_slave_dig_outs[2] = { - 0x1f, 0, -}; - -static const hda_nid_t stac927x_adc_nids[3] = { - 0x07, 0x08, 0x09 -}; - -static const hda_nid_t stac927x_mux_nids[3] = { - 0x15, 0x16, 0x17 -}; - -static const hda_nid_t stac927x_smux_nids[1] = { - 0x21, -}; - -static const hda_nid_t stac927x_dac_nids[6] = { - 0x02, 0x03, 0x04, 0x05, 0x06, 0 -}; - -static const hda_nid_t stac927x_dmux_nids[1] = { - 0x1b, -}; - -#define STAC927X_NUM_DMICS 2 -static const hda_nid_t stac927x_dmic_nids[STAC927X_NUM_DMICS + 1] = { - 0x13, 0x14, 0 -}; - -#define STAC927X_NUM_CAPS 3 -static const unsigned long stac927x_capvols[] = { - HDA_COMPOSE_AMP_VAL(0x18, 3, 0, HDA_INPUT), - HDA_COMPOSE_AMP_VAL(0x19, 3, 0, HDA_INPUT), - HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_INPUT), -}; -static const unsigned long stac927x_capsws[] = { - HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_OUTPUT), - HDA_COMPOSE_AMP_VAL(0x1c, 3, 0, HDA_OUTPUT), - HDA_COMPOSE_AMP_VAL(0x1d, 3, 0, HDA_OUTPUT), -}; - -static const char * const stac927x_spdif_labels[5] = { - "Digital Playback", "ADAT", "Analog Mux 1", - "Analog Mux 2", "Analog Mux 3" -}; - -static const hda_nid_t stac9205_adc_nids[2] = { - 0x12, 0x13 -}; - -static const hda_nid_t stac9205_mux_nids[2] = { - 0x19, 0x1a -}; - -static const hda_nid_t stac9205_dmux_nids[1] = { - 0x1d, -}; - -static const hda_nid_t stac9205_smux_nids[1] = { - 0x21, -}; - -#define STAC9205_NUM_DMICS 2 -static const hda_nid_t stac9205_dmic_nids[STAC9205_NUM_DMICS + 1] = { - 0x17, 0x18, 0 -}; - -#define STAC9205_NUM_CAPS 2 -static const unsigned long stac9205_capvols[] = { - HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_INPUT), - HDA_COMPOSE_AMP_VAL(0x1c, 3, 0, HDA_INPUT), -}; -static const unsigned long stac9205_capsws[] = { - HDA_COMPOSE_AMP_VAL(0x1d, 3, 0, HDA_OUTPUT), - HDA_COMPOSE_AMP_VAL(0x1e, 3, 0, HDA_OUTPUT), -}; - -static const hda_nid_t stac9200_pin_nids[8] = { - 0x08, 0x09, 0x0d, 0x0e, - 0x0f, 0x10, 0x11, 0x12, -}; - -static const hda_nid_t stac925x_pin_nids[8] = { - 0x07, 0x08, 0x0a, 0x0b, - 0x0c, 0x0d, 0x10, 0x11, -}; - -static const hda_nid_t stac922x_pin_nids[10] = { - 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, - 0x0f, 0x10, 0x11, 0x15, 0x1b, -}; - -static const hda_nid_t stac92hd73xx_pin_nids[13] = { - 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, - 0x0f, 0x10, 0x11, 0x12, 0x13, - 0x14, 0x22, 0x23 -}; - -#define STAC92HD71BXX_NUM_PINS 13 -static const hda_nid_t stac92hd71bxx_pin_nids_4port[STAC92HD71BXX_NUM_PINS] = { - 0x0a, 0x0b, 0x0c, 0x0d, 0x00, - 0x00, 0x14, 0x18, 0x19, 0x1e, - 0x1f, 0x20, 0x27 -}; -static const hda_nid_t stac92hd71bxx_pin_nids_6port[STAC92HD71BXX_NUM_PINS] = { - 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, - 0x0f, 0x14, 0x18, 0x19, 0x1e, - 0x1f, 0x20, 0x27 -}; - -static const hda_nid_t stac927x_pin_nids[14] = { - 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, - 0x0f, 0x10, 0x11, 0x12, 0x13, - 0x14, 0x21, 0x22, 0x23, -}; - -static const hda_nid_t stac9205_pin_nids[12] = { - 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, - 0x0f, 0x14, 0x16, 0x17, 0x18, - 0x21, 0x22, -}; -static int stac_add_event(struct hda_codec *codec, hda_nid_t nid, - unsigned char type, int data); -static int stac_add_hp_bass_switch(struct hda_codec *codec); -static void stac92xx_auto_set_pinctl(struct hda_codec *codec, - hda_nid_t nid, int pin_type); -static int hp_bnb2011_with_dock(struct hda_codec *codec); -static int hp_blike_system(u32 subsystem_id); -static int find_mute_led_cfg(struct hda_codec *codec, int default_polarity); - -static int stac92xx_dmux_enum_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) +/* + * PCM hooks + */ +static void stac_playback_pcm_hook(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream, + int action) { - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct sigmatel_spec *spec = codec->spec; - return snd_hda_input_mux_info(spec->dinput_mux, uinfo); + if (action == HDA_GEN_PCM_ACT_OPEN && spec->stream_delay) + msleep(spec->stream_delay); } -static int stac92xx_dmux_enum_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +static void stac_capture_pcm_hook(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream, + int action) { - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct sigmatel_spec *spec = codec->spec; - unsigned int dmux_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); + int i, idx = 0; - ucontrol->value.enumerated.item[0] = spec->cur_dmux[dmux_idx]; - return 0; -} + if (!spec->powerdown_adcs) + return; -static int stac92xx_dmux_enum_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct sigmatel_spec *spec = codec->spec; - unsigned int dmux_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); + for (i = 0; i < spec->gen.num_all_adcs; i++) { + if (spec->gen.all_adcs[i] == hinfo->nid) { + idx = i; + break; + } + } - return snd_hda_input_mux_put(codec, spec->dinput_mux, ucontrol, - spec->dmux_nids[dmux_idx], &spec->cur_dmux[dmux_idx]); + switch (action) { + case HDA_GEN_PCM_ACT_OPEN: + msleep(40); + snd_hda_codec_write(codec, hinfo->nid, 0, + AC_VERB_SET_POWER_STATE, AC_PWRST_D0); + spec->active_adcs |= (1 << idx); + break; + case HDA_GEN_PCM_ACT_CLOSE: + snd_hda_codec_write(codec, hinfo->nid, 0, + AC_VERB_SET_POWER_STATE, AC_PWRST_D3); + spec->active_adcs &= ~(1 << idx); + break; + } } -static int stac92xx_smux_enum_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct sigmatel_spec *spec = codec->spec; - return snd_hda_input_mux_info(spec->sinput_mux, uinfo); -} +/* + * Early 2006 Intel Macintoshes with STAC9220X5 codecs seem to have a + * funky external mute control using GPIO pins. + */ -static int stac92xx_smux_enum_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +static void stac_gpio_set(struct hda_codec *codec, unsigned int mask, + unsigned int dir_mask, unsigned int data) { - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct sigmatel_spec *spec = codec->spec; - unsigned int smux_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); + unsigned int gpiostate, gpiomask, gpiodir; - ucontrol->value.enumerated.item[0] = spec->cur_smux[smux_idx]; - return 0; + snd_printdd("%s msk %x dir %x gpio %x\n", __func__, mask, dir_mask, data); + + gpiostate = snd_hda_codec_read(codec, codec->afg, 0, + AC_VERB_GET_GPIO_DATA, 0); + gpiostate = (gpiostate & ~dir_mask) | (data & dir_mask); + + gpiomask = snd_hda_codec_read(codec, codec->afg, 0, + AC_VERB_GET_GPIO_MASK, 0); + gpiomask |= mask; + + gpiodir = snd_hda_codec_read(codec, codec->afg, 0, + AC_VERB_GET_GPIO_DIRECTION, 0); + gpiodir |= dir_mask; + + /* Configure GPIOx as CMOS */ + snd_hda_codec_write(codec, codec->afg, 0, 0x7e7, 0); + + snd_hda_codec_write(codec, codec->afg, 0, + AC_VERB_SET_GPIO_MASK, gpiomask); + snd_hda_codec_read(codec, codec->afg, 0, + AC_VERB_SET_GPIO_DIRECTION, gpiodir); /* sync */ + + msleep(1); + + snd_hda_codec_read(codec, codec->afg, 0, + AC_VERB_SET_GPIO_DATA, gpiostate); /* sync */ } -static int stac92xx_smux_enum_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +/* hook for controlling mic-mute LED GPIO */ +static void stac_capture_led_hook(struct hda_codec *codec, bool enable) { - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct sigmatel_spec *spec = codec->spec; - struct hda_input_mux *smux = &spec->private_smux; - unsigned int smux_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); - int err, val; - hda_nid_t nid; - - err = snd_hda_input_mux_put(codec, spec->sinput_mux, ucontrol, - spec->smux_nids[smux_idx], &spec->cur_smux[smux_idx]); - if (err < 0) - return err; + bool mute = !enable; - if (spec->spdif_mute) { - if (smux_idx == 0) - nid = spec->multiout.dig_out_nid; - else - nid = codec->slave_dig_outs[smux_idx - 1]; - if (spec->cur_smux[smux_idx] == smux->num_items - 1) - val = HDA_AMP_MUTE; + if (spec->mic_mute_led_on != mute) { + spec->mic_mute_led_on = mute; + if (mute) + spec->gpio_data |= spec->mic_mute_led_gpio; else - val = 0; - /* un/mute SPDIF out */ - snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0, - HDA_AMP_MUTE, val); + spec->gpio_data &= ~spec->mic_mute_led_gpio; + stac_gpio_set(codec, spec->gpio_mask, + spec->gpio_dir, spec->gpio_data); } - return 0; } static int stac_vrefout_set(struct hda_codec *codec, @@ -702,141 +355,233 @@ static int stac_vrefout_set(struct hda_codec *codec, return 1; } -static unsigned int stac92xx_vref_set(struct hda_codec *codec, - hda_nid_t nid, unsigned int new_vref) -{ - int error; - unsigned int pincfg; - pincfg = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_PIN_WIDGET_CONTROL, 0); - - pincfg &= 0xff; - pincfg &= ~(AC_PINCTL_VREFEN | AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN); - pincfg |= new_vref; - - if (new_vref == AC_PINCTL_VREF_HIZ) - pincfg |= AC_PINCTL_OUT_EN; - else - pincfg |= AC_PINCTL_IN_EN; - - error = snd_hda_set_pin_ctl_cache(codec, nid, pincfg); - if (error < 0) - return error; - else - return 1; -} - -static unsigned int stac92xx_vref_get(struct hda_codec *codec, hda_nid_t nid) -{ - unsigned int vref; - vref = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_PIN_WIDGET_CONTROL, 0); - vref &= AC_PINCTL_VREFEN; - return vref; -} - -static int stac92xx_mux_enum_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) +/* update mute-LED accoring to the master switch */ +static void stac_update_led_status(struct hda_codec *codec, int enabled) { - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct sigmatel_spec *spec = codec->spec; - return snd_hda_input_mux_info(spec->input_mux, uinfo); -} + int muted = !enabled; -static int stac92xx_mux_enum_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct sigmatel_spec *spec = codec->spec; - unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); + if (!spec->gpio_led) + return; - ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx]; - return 0; -} + /* LED state is inverted on these systems */ + if (spec->gpio_led_polarity) + muted = !muted; -static int stac92xx_mux_enum_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct sigmatel_spec *spec = codec->spec; - unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); - const struct hda_input_mux *imux = spec->input_mux; - unsigned int idx, prev_idx, didx; - - idx = ucontrol->value.enumerated.item[0]; - if (idx >= imux->num_items) - idx = imux->num_items - 1; - prev_idx = spec->cur_mux[adc_idx]; - if (prev_idx == idx) - return 0; - if (idx < spec->num_analog_muxes) { - snd_hda_codec_write_cache(codec, spec->mux_nids[adc_idx], 0, - AC_VERB_SET_CONNECT_SEL, - imux->items[idx].index); - if (prev_idx >= spec->num_analog_muxes && - spec->mux_nids[adc_idx] != spec->dmux_nids[adc_idx]) { - imux = spec->dinput_mux; - /* 0 = analog */ - snd_hda_codec_write_cache(codec, - spec->dmux_nids[adc_idx], 0, - AC_VERB_SET_CONNECT_SEL, - imux->items[0].index); - } + if (!spec->vref_mute_led_nid) { + if (muted) + spec->gpio_data |= spec->gpio_led; + else + spec->gpio_data &= ~spec->gpio_led; + stac_gpio_set(codec, spec->gpio_mask, + spec->gpio_dir, spec->gpio_data); } else { - imux = spec->dinput_mux; - /* first dimux item is hardcoded to select analog imux, - * so lets skip it - */ - didx = idx - spec->num_analog_muxes + 1; - snd_hda_codec_write_cache(codec, spec->dmux_nids[adc_idx], 0, - AC_VERB_SET_CONNECT_SEL, - imux->items[didx].index); + spec->vref_led = muted ? AC_PINCTL_VREF_50 : AC_PINCTL_VREF_GRD; + stac_vrefout_set(codec, spec->vref_mute_led_nid, + spec->vref_led); } - spec->cur_mux[adc_idx] = idx; - return 1; } -static int stac92xx_mono_mux_enum_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) +/* vmaster hook to update mute LED */ +static void stac_vmaster_hook(void *private_data, int val) { - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct sigmatel_spec *spec = codec->spec; - return snd_hda_input_mux_info(spec->mono_mux, uinfo); + stac_update_led_status(private_data, val); } -static int stac92xx_mono_mux_enum_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +/* automute hook to handle GPIO mute and EAPD updates */ +static void stac_update_outputs(struct hda_codec *codec) { - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct sigmatel_spec *spec = codec->spec; - ucontrol->value.enumerated.item[0] = spec->cur_mmux; - return 0; -} + if (spec->gpio_mute) + spec->gen.master_mute = + !(snd_hda_codec_read(codec, codec->afg, 0, + AC_VERB_GET_GPIO_DATA, 0) & spec->gpio_mute); -static int stac92xx_mono_mux_enum_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct sigmatel_spec *spec = codec->spec; + snd_hda_gen_update_outputs(codec); - return snd_hda_input_mux_put(codec, spec->mono_mux, ucontrol, - spec->mono_nid, &spec->cur_mmux); + if (spec->eapd_mask && spec->eapd_switch) { + unsigned int val = spec->gpio_data; + if (spec->gen.speaker_muted) + val &= ~spec->eapd_mask; + else + val |= spec->eapd_mask; + if (spec->gpio_data != val) + stac_gpio_set(codec, spec->gpio_mask, spec->gpio_dir, + val); + } } -#define stac92xx_aloopback_info snd_ctl_boolean_mono_info - -static int stac92xx_aloopback_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +static void stac_toggle_power_map(struct hda_codec *codec, hda_nid_t nid, + bool enable, bool do_write) { - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); struct sigmatel_spec *spec = codec->spec; + unsigned int idx, val; - ucontrol->value.integer.value[0] = !!(spec->aloopback & - (spec->aloopback_mask << idx)); - return 0; + for (idx = 0; idx < spec->num_pwrs; idx++) { + if (spec->pwr_nids[idx] == nid) + break; + } + if (idx >= spec->num_pwrs) + return; + + idx = 1 << idx; + + val = spec->power_map_bits; + if (enable) + val &= ~idx; + else + val |= idx; + + /* power down unused output ports */ + if (val != spec->power_map_bits) { + spec->power_map_bits = val; + if (do_write) + snd_hda_codec_write(codec, codec->afg, 0, + AC_VERB_IDT_SET_POWER_MAP, val); + } +} + +/* update power bit per jack plug/unplug */ +static void jack_update_power(struct hda_codec *codec, + struct hda_jack_tbl *jack) +{ + struct sigmatel_spec *spec = codec->spec; + int i; + + if (!spec->num_pwrs) + return; + + if (jack && jack->nid) { + stac_toggle_power_map(codec, jack->nid, + snd_hda_jack_detect(codec, jack->nid), + true); + return; + } + + /* update all jacks */ + for (i = 0; i < spec->num_pwrs; i++) { + hda_nid_t nid = spec->pwr_nids[i]; + jack = snd_hda_jack_tbl_get(codec, nid); + if (!jack || !jack->action) + continue; + if (jack->action == STAC_PWR_EVENT || + jack->action <= HDA_GEN_LAST_EVENT) + stac_toggle_power_map(codec, nid, + snd_hda_jack_detect(codec, nid), + false); + } + + snd_hda_codec_write(codec, codec->afg, 0, AC_VERB_IDT_SET_POWER_MAP, + spec->power_map_bits); +} + +static void stac_hp_automute(struct hda_codec *codec, + struct hda_jack_tbl *jack) +{ + snd_hda_gen_hp_automute(codec, jack); + jack_update_power(codec, jack); +} + +static void stac_line_automute(struct hda_codec *codec, + struct hda_jack_tbl *jack) +{ + snd_hda_gen_line_automute(codec, jack); + jack_update_power(codec, jack); +} + +static void stac_vref_event(struct hda_codec *codec, struct hda_jack_tbl *event) +{ + unsigned int data; + + data = snd_hda_codec_read(codec, codec->afg, 0, + AC_VERB_GET_GPIO_DATA, 0); + /* toggle VREF state based on GPIOx status */ + snd_hda_codec_write(codec, codec->afg, 0, 0x7e0, + !!(data & (1 << event->private_data))); +} + +/* initialize the power map and enable the power event to jacks that + * haven't been assigned to automute + */ +static void stac_init_power_map(struct hda_codec *codec) +{ + struct sigmatel_spec *spec = codec->spec; + int i; + + for (i = 0; i < spec->num_pwrs; i++) { + hda_nid_t nid = spec->pwr_nids[i]; + unsigned int def_conf = snd_hda_codec_get_pincfg(codec, nid); + def_conf = get_defcfg_connect(def_conf); + if (snd_hda_jack_tbl_get(codec, nid)) + continue; + if (def_conf == AC_JACK_PORT_COMPLEX && + !(spec->vref_mute_led_nid == nid || + is_jack_detectable(codec, nid))) { + snd_hda_jack_detect_enable_callback(codec, nid, + STAC_PWR_EVENT, + jack_update_power); + } else { + if (def_conf == AC_JACK_PORT_NONE) + stac_toggle_power_map(codec, nid, false, false); + else + stac_toggle_power_map(codec, nid, true, false); + } + } +} + +/* + */ + +static inline bool get_int_hint(struct hda_codec *codec, const char *key, + int *valp) +{ + return !snd_hda_get_int_hint(codec, key, valp); +} + +/* override some hints from the hwdep entry */ +static void stac_store_hints(struct hda_codec *codec) +{ + struct sigmatel_spec *spec = codec->spec; + int val; + + if (get_int_hint(codec, "gpio_mask", &spec->gpio_mask)) { + spec->eapd_mask = spec->gpio_dir = spec->gpio_data = + spec->gpio_mask; + } + if (get_int_hint(codec, "gpio_dir", &spec->gpio_dir)) + spec->gpio_mask &= spec->gpio_mask; + if (get_int_hint(codec, "gpio_data", &spec->gpio_data)) + spec->gpio_dir &= spec->gpio_mask; + if (get_int_hint(codec, "eapd_mask", &spec->eapd_mask)) + spec->eapd_mask &= spec->gpio_mask; + if (get_int_hint(codec, "gpio_mute", &spec->gpio_mute)) + spec->gpio_mute &= spec->gpio_mask; + val = snd_hda_get_bool_hint(codec, "eapd_switch"); + if (val >= 0) + spec->eapd_switch = val; +} + +/* + * loopback controls + */ + +#define stac_aloopback_info snd_ctl_boolean_mono_info + +static int stac_aloopback_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); + struct sigmatel_spec *spec = codec->spec; + + ucontrol->value.integer.value[0] = !!(spec->aloopback & + (spec->aloopback_mask << idx)); + return 0; } -static int stac92xx_aloopback_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +static int stac_aloopback_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct sigmatel_spec *spec = codec->spec; @@ -875,399 +620,466 @@ static int stac92xx_aloopback_put(struct snd_kcontrol *kcontrol, return 1; } -static const struct hda_verb stac9200_core_init[] = { - /* set dac0mux for dac converter */ - { 0x07, AC_VERB_SET_CONNECT_SEL, 0x00}, - {} -}; - -static const struct hda_verb stac9200_eapd_init[] = { - /* set dac0mux for dac converter */ - {0x07, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x08, AC_VERB_SET_EAPD_BTLENABLE, 0x02}, - {} -}; - -static const struct hda_verb dell_eq_core_init[] = { - /* set master volume to max value without distortion - * and direct control */ - { 0x1f, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xec}, - {} -}; - -static const struct hda_verb stac92hd73xx_core_init[] = { - /* set master volume and direct control */ - { 0x1f, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff}, - {} -}; - -static const struct hda_verb stac92hd83xxx_core_init[] = { - /* power state controls amps */ - { 0x01, AC_VERB_SET_EAPD, 1 << 2}, - {} -}; - -static const struct hda_verb stac92hd83xxx_hp_zephyr_init[] = { - { 0x22, 0x785, 0x43 }, - { 0x22, 0x782, 0xe0 }, - { 0x22, 0x795, 0x00 }, - {} -}; - -static const struct hda_verb stac92hd71bxx_core_init[] = { - /* set master volume and direct control */ - { 0x28, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff}, - {} -}; - -static const struct hda_verb stac92hd71bxx_unmute_core_init[] = { - /* unmute right and left channels for nodes 0x0f, 0xa, 0x0d */ - { 0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - { 0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - { 0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {} -}; - -static const struct hda_verb stac925x_core_init[] = { - /* set dac0mux for dac converter */ - { 0x06, AC_VERB_SET_CONNECT_SEL, 0x00}, - /* mute the master volume */ - { 0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, - {} -}; - -static const struct hda_verb stac922x_core_init[] = { - /* set master volume and direct control */ - { 0x16, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff}, - {} -}; - -static const struct hda_verb d965_core_init[] = { - /* unmute node 0x1b */ - { 0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000}, - /* select node 0x03 as DAC */ - { 0x0b, AC_VERB_SET_CONNECT_SEL, 0x01}, - {} -}; - -static const struct hda_verb dell_3st_core_init[] = { - /* don't set delta bit */ - {0x24, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0x7f}, - /* unmute node 0x1b */ - {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000}, - /* select node 0x03 as DAC */ - {0x0b, AC_VERB_SET_CONNECT_SEL, 0x01}, - {} -}; - -static const struct hda_verb stac927x_core_init[] = { - /* set master volume and direct control */ - { 0x24, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff}, - /* enable analog pc beep path */ - { 0x01, AC_VERB_SET_DIGI_CONVERT_2, 1 << 5}, - {} -}; - -static const struct hda_verb stac927x_volknob_core_init[] = { - /* don't set delta bit */ - {0x24, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0x7f}, - /* enable analog pc beep path */ - {0x01, AC_VERB_SET_DIGI_CONVERT_2, 1 << 5}, - {} -}; - -static const struct hda_verb stac9205_core_init[] = { - /* set master volume and direct control */ - { 0x24, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff}, - /* enable analog pc beep path */ - { 0x01, AC_VERB_SET_DIGI_CONVERT_2, 1 << 5}, - {} -}; - -#define STAC_MONO_MUX \ - { \ - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ - .name = "Mono Mux", \ - .count = 1, \ - .info = stac92xx_mono_mux_enum_info, \ - .get = stac92xx_mono_mux_enum_get, \ - .put = stac92xx_mono_mux_enum_put, \ - } - #define STAC_ANALOG_LOOPBACK(verb_read, verb_write, cnt) \ { \ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ .name = "Analog Loopback", \ .count = cnt, \ - .info = stac92xx_aloopback_info, \ - .get = stac92xx_aloopback_get, \ - .put = stac92xx_aloopback_put, \ + .info = stac_aloopback_info, \ + .get = stac_aloopback_get, \ + .put = stac_aloopback_put, \ .private_value = verb_read | (verb_write << 16), \ } -#define DC_BIAS(xname, idx, nid) \ - { \ - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ - .name = xname, \ - .index = idx, \ - .info = stac92xx_dc_bias_info, \ - .get = stac92xx_dc_bias_get, \ - .put = stac92xx_dc_bias_put, \ - .private_value = nid, \ - } +/* + * Mute LED handling on HP laptops + */ -static const struct snd_kcontrol_new stac9200_mixer[] = { - HDA_CODEC_VOLUME_MIN_MUTE("PCM Playback Volume", 0xb, 0, HDA_OUTPUT), - HDA_CODEC_MUTE("PCM Playback Switch", 0xb, 0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Capture Volume", 0x0a, 0, HDA_OUTPUT), - HDA_CODEC_MUTE("Capture Switch", 0x0a, 0, HDA_OUTPUT), - { } /* end */ -}; +/* check whether it's a HP laptop with a docking port */ +static bool hp_bnb2011_with_dock(struct hda_codec *codec) +{ + if (codec->vendor_id != 0x111d7605 && + codec->vendor_id != 0x111d76d1) + return false; -static const struct snd_kcontrol_new stac92hd73xx_6ch_loopback[] = { - STAC_ANALOG_LOOPBACK(0xFA0, 0x7A1, 3), - {} -}; + switch (codec->subsystem_id) { + case 0x103c1618: + case 0x103c1619: + case 0x103c161a: + case 0x103c161b: + case 0x103c161c: + case 0x103c161d: + case 0x103c161e: + case 0x103c161f: -static const struct snd_kcontrol_new stac92hd73xx_8ch_loopback[] = { - STAC_ANALOG_LOOPBACK(0xFA0, 0x7A1, 4), - {} -}; + case 0x103c162a: + case 0x103c162b: -static const struct snd_kcontrol_new stac92hd73xx_10ch_loopback[] = { - STAC_ANALOG_LOOPBACK(0xFA0, 0x7A1, 5), - {} -}; + case 0x103c1630: + case 0x103c1631: + case 0x103c1633: + case 0x103c1634: + case 0x103c1635: -static const struct snd_kcontrol_new stac92hd71bxx_loopback[] = { - STAC_ANALOG_LOOPBACK(0xFA0, 0x7A0, 2) -}; + case 0x103c3587: + case 0x103c3588: + case 0x103c3589: + case 0x103c358a: -static const struct snd_kcontrol_new stac925x_mixer[] = { - HDA_CODEC_VOLUME_MIN_MUTE("PCM Playback Volume", 0xe, 0, HDA_OUTPUT), - HDA_CODEC_MUTE("PCM Playback Switch", 0x0e, 0, HDA_OUTPUT), - { } /* end */ -}; + case 0x103c3667: + case 0x103c3668: + case 0x103c3669: -static const struct snd_kcontrol_new stac9205_loopback[] = { - STAC_ANALOG_LOOPBACK(0xFE0, 0x7E0, 1), - {} -}; - -static const struct snd_kcontrol_new stac927x_loopback[] = { - STAC_ANALOG_LOOPBACK(0xFEB, 0x7EB, 1), - {} -}; - -static struct snd_kcontrol_new stac_dmux_mixer = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Digital Input Source", - /* count set later */ - .info = stac92xx_dmux_enum_info, - .get = stac92xx_dmux_enum_get, - .put = stac92xx_dmux_enum_put, -}; + return true; + } + return false; +} -static struct snd_kcontrol_new stac_smux_mixer = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "IEC958 Playback Source", - /* count set later */ - .info = stac92xx_smux_enum_info, - .get = stac92xx_smux_enum_get, - .put = stac92xx_smux_enum_put, -}; +static bool hp_blike_system(u32 subsystem_id) +{ + switch (subsystem_id) { + case 0x103c1520: + case 0x103c1521: + case 0x103c1523: + case 0x103c1524: + case 0x103c1525: + case 0x103c1722: + case 0x103c1723: + case 0x103c1724: + case 0x103c1725: + case 0x103c1726: + case 0x103c1727: + case 0x103c1728: + case 0x103c1729: + case 0x103c172a: + case 0x103c172b: + case 0x103c307e: + case 0x103c307f: + case 0x103c3080: + case 0x103c3081: + case 0x103c7007: + case 0x103c7008: + return true; + } + return false; +} -static const char * const slave_pfxs[] = { - "Front", "Surround", "Center", "LFE", "Side", - "Headphone", "Speaker", "Bass Speaker", "IEC958", "PCM", - NULL -}; +static void set_hp_led_gpio(struct hda_codec *codec) +{ + struct sigmatel_spec *spec = codec->spec; + unsigned int gpio; -static void stac92xx_update_led_status(struct hda_codec *codec, int enabled); + if (spec->gpio_led) + return; -static void stac92xx_vmaster_hook(void *private_data, int val) -{ - stac92xx_update_led_status(private_data, val); + gpio = snd_hda_param_read(codec, codec->afg, AC_PAR_GPIO_CAP); + gpio &= AC_GPIO_IO_COUNT; + if (gpio > 3) + spec->gpio_led = 0x08; /* GPIO 3 */ + else + spec->gpio_led = 0x01; /* GPIO 0 */ } -static void stac92xx_free_kctls(struct hda_codec *codec); - -static int stac92xx_build_controls(struct hda_codec *codec) +/* + * This method searches for the mute LED GPIO configuration + * provided as OEM string in SMBIOS. The format of that string + * is HP_Mute_LED_P_G or HP_Mute_LED_P + * where P can be 0 or 1 and defines mute LED GPIO control state (low/high) + * that corresponds to the NOT muted state of the master volume + * and G is the index of the GPIO to use as the mute LED control (0..9) + * If _G portion is missing it is assigned based on the codec ID + * + * So, HP B-series like systems may have HP_Mute_LED_0 (current models) + * or HP_Mute_LED_0_3 (future models) OEM SMBIOS strings + * + * + * The dv-series laptops don't seem to have the HP_Mute_LED* strings in + * SMBIOS - at least the ones I have seen do not have them - which include + * my own system (HP Pavilion dv6-1110ax) and my cousin's + * HP Pavilion dv9500t CTO. + * Need more information on whether it is true across the entire series. + * -- kunal + */ +static int find_mute_led_cfg(struct hda_codec *codec, int default_polarity) { struct sigmatel_spec *spec = codec->spec; - unsigned int vmaster_tlv[4]; - int err; - int i; + const struct dmi_device *dev = NULL; - if (spec->mixer) { - err = snd_hda_add_new_ctls(codec, spec->mixer); - if (err < 0) - return err; + if (get_int_hint(codec, "gpio_led", &spec->gpio_led)) { + get_int_hint(codec, "gpio_led_polarity", + &spec->gpio_led_polarity); + return 1; } - for (i = 0; i < spec->num_mixers; i++) { - err = snd_hda_add_new_ctls(codec, spec->mixers[i]); - if (err < 0) - return err; - } - if (!spec->auto_mic && spec->num_dmuxes > 0 && - snd_hda_get_bool_hint(codec, "separate_dmux") == 1) { - stac_dmux_mixer.count = spec->num_dmuxes; - err = snd_hda_ctl_add(codec, 0, - snd_ctl_new1(&stac_dmux_mixer, codec)); - if (err < 0) - return err; - } - if (spec->num_smuxes > 0) { - int wcaps = get_wcaps(codec, spec->multiout.dig_out_nid); - struct hda_input_mux *smux = &spec->private_smux; - /* check for mute support on SPDIF out */ - if (wcaps & AC_WCAP_OUT_AMP) { - snd_hda_add_imux_item(smux, "Off", 0, NULL); - spec->spdif_mute = 1; + while ((dev = dmi_find_device(DMI_DEV_TYPE_OEM_STRING, NULL, dev))) { + if (sscanf(dev->name, "HP_Mute_LED_%d_%x", + &spec->gpio_led_polarity, + &spec->gpio_led) == 2) { + unsigned int max_gpio; + max_gpio = snd_hda_param_read(codec, codec->afg, + AC_PAR_GPIO_CAP); + max_gpio &= AC_GPIO_IO_COUNT; + if (spec->gpio_led < max_gpio) + spec->gpio_led = 1 << spec->gpio_led; + else + spec->vref_mute_led_nid = spec->gpio_led; + return 1; + } + if (sscanf(dev->name, "HP_Mute_LED_%d", + &spec->gpio_led_polarity) == 1) { + set_hp_led_gpio(codec); + return 1; + } + /* BIOS bug: unfilled OEM string */ + if (strstr(dev->name, "HP_Mute_LED_P_G")) { + set_hp_led_gpio(codec); + if (default_polarity >= 0) + spec->gpio_led_polarity = default_polarity; + else + spec->gpio_led_polarity = 1; + return 1; } - stac_smux_mixer.count = spec->num_smuxes; - err = snd_hda_ctl_add(codec, 0, - snd_ctl_new1(&stac_smux_mixer, codec)); - if (err < 0) - return err; } - if (spec->multiout.dig_out_nid) { - err = snd_hda_create_dig_out_ctls(codec, - spec->multiout.dig_out_nid, - spec->multiout.dig_out_nid, - spec->autocfg.dig_out_type[0]); - if (err < 0) - return err; - err = snd_hda_create_spdif_share_sw(codec, - &spec->multiout); - if (err < 0) - return err; - spec->multiout.share_spdif = 1; - } - if (spec->dig_in_nid && !(spec->gpio_dir & 0x01)) { - err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid); - if (err < 0) - return err; + /* + * Fallback case - if we don't find the DMI strings, + * we statically set the GPIO - if not a B-series system + * and default polarity is provided + */ + if (!hp_blike_system(codec->subsystem_id) && + (default_polarity == 0 || default_polarity == 1)) { + set_hp_led_gpio(codec); + spec->gpio_led_polarity = default_polarity; + return 1; } + return 0; +} - /* if we have no master control, let's create it */ - snd_hda_set_vmaster_tlv(codec, spec->multiout.dac_nids[0], - HDA_OUTPUT, vmaster_tlv); - /* correct volume offset */ - vmaster_tlv[2] += vmaster_tlv[3] * spec->volume_offset; - /* minimum value is actually mute */ - vmaster_tlv[3] |= TLV_DB_SCALE_MUTE; - err = snd_hda_add_vmaster(codec, "Master Playback Volume", - vmaster_tlv, slave_pfxs, - "Playback Volume"); - if (err < 0) - return err; +/* + * PC beep controls + */ - err = __snd_hda_add_vmaster(codec, "Master Playback Switch", - NULL, slave_pfxs, - "Playback Switch", true, - &spec->vmaster_mute.sw_kctl); - if (err < 0) - return err; +/* create PC beep volume controls */ +static int stac_auto_create_beep_ctls(struct hda_codec *codec, + hda_nid_t nid) +{ + struct sigmatel_spec *spec = codec->spec; + u32 caps = query_amp_caps(codec, nid, HDA_OUTPUT); + struct snd_kcontrol_new *knew; + static struct snd_kcontrol_new abeep_mute_ctl = + HDA_CODEC_MUTE(NULL, 0, 0, 0); + static struct snd_kcontrol_new dbeep_mute_ctl = + HDA_CODEC_MUTE_BEEP(NULL, 0, 0, 0); + static struct snd_kcontrol_new beep_vol_ctl = + HDA_CODEC_VOLUME(NULL, 0, 0, 0); - if (spec->gpio_led) { - spec->vmaster_mute.hook = stac92xx_vmaster_hook; - err = snd_hda_add_vmaster_hook(codec, &spec->vmaster_mute, true); - if (err < 0) - return err; + /* check for mute support for the the amp */ + if ((caps & AC_AMPCAP_MUTE) >> AC_AMPCAP_MUTE_SHIFT) { + const struct snd_kcontrol_new *temp; + if (spec->anabeep_nid == nid) + temp = &abeep_mute_ctl; + else + temp = &dbeep_mute_ctl; + knew = snd_hda_gen_add_kctl(&spec->gen, + "Beep Playback Switch", temp); + if (!knew) + return -ENOMEM; + knew->private_value = + HDA_COMPOSE_AMP_VAL(nid, 1, 0, HDA_OUTPUT); } - if (spec->aloopback_ctl && - snd_hda_get_bool_hint(codec, "loopback") == 1) { - err = snd_hda_add_new_ctls(codec, spec->aloopback_ctl); - if (err < 0) - return err; + /* check to see if there is volume support for the amp */ + if ((caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT) { + knew = snd_hda_gen_add_kctl(&spec->gen, + "Beep Playback Volume", + &beep_vol_ctl); + if (!knew) + return -ENOMEM; + knew->private_value = + HDA_COMPOSE_AMP_VAL(nid, 1, 0, HDA_OUTPUT); } + return 0; +} - stac92xx_free_kctls(codec); /* no longer needed */ +#ifdef CONFIG_SND_HDA_INPUT_BEEP +#define stac_dig_beep_switch_info snd_ctl_boolean_mono_info - err = snd_hda_jack_add_kctls(codec, &spec->autocfg); - if (err < 0) - return err; +static int stac_dig_beep_switch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + ucontrol->value.integer.value[0] = codec->beep->enabled; + return 0; +} - return 0; +static int stac_dig_beep_switch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + return snd_hda_enable_beep_device(codec, ucontrol->value.integer.value[0]); } -static const struct hda_pintbl ref9200_pin_configs[] = { - { 0x08, 0x01c47010 }, - { 0x09, 0x01447010 }, - { 0x0d, 0x0221401f }, - { 0x0e, 0x01114010 }, - { 0x0f, 0x02a19020 }, - { 0x10, 0x01a19021 }, - { 0x11, 0x90100140 }, - { 0x12, 0x01813122 }, - {} +static const struct snd_kcontrol_new stac_dig_beep_ctrl = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Beep Playback Switch", + .info = stac_dig_beep_switch_info, + .get = stac_dig_beep_switch_get, + .put = stac_dig_beep_switch_put, }; -static const struct hda_pintbl gateway9200_m4_pin_configs[] = { - { 0x08, 0x400000fe }, - { 0x09, 0x404500f4 }, - { 0x0d, 0x400100f0 }, - { 0x0e, 0x90110010 }, - { 0x0f, 0x400100f1 }, - { 0x10, 0x02a1902e }, - { 0x11, 0x500000f2 }, - { 0x12, 0x500000f3 }, +static int stac_beep_switch_ctl(struct hda_codec *codec) +{ + struct sigmatel_spec *spec = codec->spec; + + if (!snd_hda_gen_add_kctl(&spec->gen, NULL, &stac_dig_beep_ctrl)) + return -ENOMEM; + return 0; +} +#endif + +/* + */ + +static const struct hda_verb stac9200_core_init[] = { + /* set dac0mux for dac converter */ + { 0x07, AC_VERB_SET_CONNECT_SEL, 0x00}, {} }; -static const struct hda_pintbl gateway9200_m4_2_pin_configs[] = { - { 0x08, 0x400000fe }, - { 0x09, 0x404500f4 }, - { 0x0d, 0x400100f0 }, - { 0x0e, 0x90110010 }, - { 0x0f, 0x400100f1 }, - { 0x10, 0x02a1902e }, - { 0x11, 0x500000f2 }, - { 0x12, 0x500000f3 }, +static const struct hda_verb stac9200_eapd_init[] = { + /* set dac0mux for dac converter */ + {0x07, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x08, AC_VERB_SET_EAPD_BTLENABLE, 0x02}, {} }; -/* - STAC 9200 pin configs for - 102801A8 - 102801DE - 102801E8 -*/ -static const struct hda_pintbl dell9200_d21_pin_configs[] = { - { 0x08, 0x400001f0 }, - { 0x09, 0x400001f1 }, - { 0x0d, 0x02214030 }, - { 0x0e, 0x01014010 }, - { 0x0f, 0x02a19020 }, - { 0x10, 0x01a19021 }, - { 0x11, 0x90100140 }, - { 0x12, 0x01813122 }, +static const struct hda_verb dell_eq_core_init[] = { + /* set master volume to max value without distortion + * and direct control */ + { 0x1f, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xec}, {} }; -/* - STAC 9200 pin configs for - 102801C0 - 102801C1 -*/ -static const struct hda_pintbl dell9200_d22_pin_configs[] = { - { 0x08, 0x400001f0 }, - { 0x09, 0x400001f1 }, - { 0x0d, 0x0221401f }, - { 0x0e, 0x01014010 }, - { 0x0f, 0x01813020 }, - { 0x10, 0x02a19021 }, - { 0x11, 0x90100140 }, - { 0x12, 0x400001f2 }, +static const struct hda_verb stac92hd73xx_core_init[] = { + /* set master volume and direct control */ + { 0x1f, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff}, {} }; -/* +static const struct hda_verb stac92hd83xxx_core_init[] = { + /* power state controls amps */ + { 0x01, AC_VERB_SET_EAPD, 1 << 2}, + {} +}; + +static const struct hda_verb stac92hd83xxx_hp_zephyr_init[] = { + { 0x22, 0x785, 0x43 }, + { 0x22, 0x782, 0xe0 }, + { 0x22, 0x795, 0x00 }, + {} +}; + +static const struct hda_verb stac92hd71bxx_core_init[] = { + /* set master volume and direct control */ + { 0x28, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff}, + {} +}; + +static const struct hda_verb stac92hd71bxx_unmute_core_init[] = { + /* unmute right and left channels for nodes 0x0f, 0xa, 0x0d */ + { 0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + { 0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + { 0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {} +}; + +static const struct hda_verb stac925x_core_init[] = { + /* set dac0mux for dac converter */ + { 0x06, AC_VERB_SET_CONNECT_SEL, 0x00}, + /* mute the master volume */ + { 0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, + {} +}; + +static const struct hda_verb stac922x_core_init[] = { + /* set master volume and direct control */ + { 0x16, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff}, + {} +}; + +static const struct hda_verb d965_core_init[] = { + /* unmute node 0x1b */ + { 0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000}, + /* select node 0x03 as DAC */ + { 0x0b, AC_VERB_SET_CONNECT_SEL, 0x01}, + {} +}; + +static const struct hda_verb dell_3st_core_init[] = { + /* don't set delta bit */ + {0x24, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0x7f}, + /* unmute node 0x1b */ + {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000}, + /* select node 0x03 as DAC */ + {0x0b, AC_VERB_SET_CONNECT_SEL, 0x01}, + {} +}; + +static const struct hda_verb stac927x_core_init[] = { + /* set master volume and direct control */ + { 0x24, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff}, + /* enable analog pc beep path */ + { 0x01, AC_VERB_SET_DIGI_CONVERT_2, 1 << 5}, + {} +}; + +static const struct hda_verb stac927x_volknob_core_init[] = { + /* don't set delta bit */ + {0x24, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0x7f}, + /* enable analog pc beep path */ + {0x01, AC_VERB_SET_DIGI_CONVERT_2, 1 << 5}, + {} +}; + +static const struct hda_verb stac9205_core_init[] = { + /* set master volume and direct control */ + { 0x24, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff}, + /* enable analog pc beep path */ + { 0x01, AC_VERB_SET_DIGI_CONVERT_2, 1 << 5}, + {} +}; + +static const struct snd_kcontrol_new stac92hd73xx_6ch_loopback = + STAC_ANALOG_LOOPBACK(0xFA0, 0x7A1, 3); + +static const struct snd_kcontrol_new stac92hd73xx_8ch_loopback = + STAC_ANALOG_LOOPBACK(0xFA0, 0x7A1, 4); + +static const struct snd_kcontrol_new stac92hd73xx_10ch_loopback = + STAC_ANALOG_LOOPBACK(0xFA0, 0x7A1, 5); + +static const struct snd_kcontrol_new stac92hd71bxx_loopback = + STAC_ANALOG_LOOPBACK(0xFA0, 0x7A0, 2); + +static const struct snd_kcontrol_new stac9205_loopback = + STAC_ANALOG_LOOPBACK(0xFE0, 0x7E0, 1); + +static const struct snd_kcontrol_new stac927x_loopback = + STAC_ANALOG_LOOPBACK(0xFEB, 0x7EB, 1); + +static const struct hda_pintbl ref9200_pin_configs[] = { + { 0x08, 0x01c47010 }, + { 0x09, 0x01447010 }, + { 0x0d, 0x0221401f }, + { 0x0e, 0x01114010 }, + { 0x0f, 0x02a19020 }, + { 0x10, 0x01a19021 }, + { 0x11, 0x90100140 }, + { 0x12, 0x01813122 }, + {} +}; + +static const struct hda_pintbl gateway9200_m4_pin_configs[] = { + { 0x08, 0x400000fe }, + { 0x09, 0x404500f4 }, + { 0x0d, 0x400100f0 }, + { 0x0e, 0x90110010 }, + { 0x0f, 0x400100f1 }, + { 0x10, 0x02a1902e }, + { 0x11, 0x500000f2 }, + { 0x12, 0x500000f3 }, + {} +}; + +static const struct hda_pintbl gateway9200_m4_2_pin_configs[] = { + { 0x08, 0x400000fe }, + { 0x09, 0x404500f4 }, + { 0x0d, 0x400100f0 }, + { 0x0e, 0x90110010 }, + { 0x0f, 0x400100f1 }, + { 0x10, 0x02a1902e }, + { 0x11, 0x500000f2 }, + { 0x12, 0x500000f3 }, + {} +}; + +/* + STAC 9200 pin configs for + 102801A8 + 102801DE + 102801E8 +*/ +static const struct hda_pintbl dell9200_d21_pin_configs[] = { + { 0x08, 0x400001f0 }, + { 0x09, 0x400001f1 }, + { 0x0d, 0x02214030 }, + { 0x0e, 0x01014010 }, + { 0x0f, 0x02a19020 }, + { 0x10, 0x01a19021 }, + { 0x11, 0x90100140 }, + { 0x12, 0x01813122 }, + {} +}; + +/* + STAC 9200 pin configs for + 102801C0 + 102801C1 +*/ +static const struct hda_pintbl dell9200_d22_pin_configs[] = { + { 0x08, 0x400001f0 }, + { 0x09, 0x400001f1 }, + { 0x0d, 0x0221401f }, + { 0x0e, 0x01014010 }, + { 0x0f, 0x01813020 }, + { 0x10, 0x02a19021 }, + { 0x11, 0x90100140 }, + { 0x12, 0x400001f2 }, + {} +}; + +/* STAC 9200 pin configs for 102801C4 (Dell Dimension E310) 102801C5 @@ -1431,17 +1243,13 @@ static void stac9200_fixup_panasonic(struct hda_codec *codec, { struct sigmatel_spec *spec = codec->spec; - switch (action) { - case HDA_FIXUP_ACT_PRE_PROBE: + if (action == HDA_FIXUP_ACT_PRE_PROBE) { spec->gpio_mask = spec->gpio_dir = 0x09; spec->gpio_data = 0x00; - break; - case HDA_FIXUP_ACT_PROBE: /* CF-74 has no headphone detection, and the driver should *NOT* * do detection and HP/speaker toggle because the hardware does it. */ - spec->hp_detect = 0; - break; + spec->gen.suppress_auto_mute = 1; } } @@ -1862,7 +1670,6 @@ static void stac92hd73xx_fixup_dell(struct hda_codec *codec) struct sigmatel_spec *spec = codec->spec; snd_hda_apply_pincfgs(codec, dell_m6_pin_configs); - spec->num_smuxes = 0; spec->eapd_switch = 0; } @@ -1883,43 +1690,34 @@ static void stac92hd73xx_fixup_dell_eq(struct hda_codec *codec, static void stac92hd73xx_fixup_dell_m6_amic(struct hda_codec *codec, const struct hda_fixup *fix, int action) { - struct sigmatel_spec *spec = codec->spec; - if (action != HDA_FIXUP_ACT_PRE_PROBE) return; stac92hd73xx_fixup_dell(codec); snd_hda_codec_set_pincfg(codec, 0x0b, 0x90A70170); - spec->num_dmics = 0; } /* Digital Mics */ static void stac92hd73xx_fixup_dell_m6_dmic(struct hda_codec *codec, const struct hda_fixup *fix, int action) { - struct sigmatel_spec *spec = codec->spec; - if (action != HDA_FIXUP_ACT_PRE_PROBE) return; stac92hd73xx_fixup_dell(codec); snd_hda_codec_set_pincfg(codec, 0x13, 0x90A60160); - spec->num_dmics = 1; } /* Both */ static void stac92hd73xx_fixup_dell_m6_both(struct hda_codec *codec, const struct hda_fixup *fix, int action) { - struct sigmatel_spec *spec = codec->spec; - if (action != HDA_FIXUP_ACT_PRE_PROBE) return; stac92hd73xx_fixup_dell(codec); snd_hda_codec_set_pincfg(codec, 0x0b, 0x90A70170); snd_hda_codec_set_pincfg(codec, 0x13, 0x90A60160); - spec->num_dmics = 1; } static void stac92hd73xx_fixup_alienware_m17x(struct hda_codec *codec, @@ -1931,19 +1729,14 @@ static void stac92hd73xx_fixup_alienware_m17x(struct hda_codec *codec, return; snd_hda_apply_pincfgs(codec, alienware_m17x_pin_configs); - spec->num_dmics = STAC92HD73XX_NUM_DMICS; - spec->num_smuxes = ARRAY_SIZE(stac92hd73xx_smux_nids); spec->eapd_switch = 0; } static void stac92hd73xx_fixup_no_jd(struct hda_codec *codec, const struct hda_fixup *fix, int action) { - struct sigmatel_spec *spec = codec->spec; - - if (action != HDA_FIXUP_ACT_PROBE) - return; - spec->hp_detect = 0; + if (action == HDA_FIXUP_ACT_PRE_PROBE) + codec->no_jack_detect = 1; } static const struct hda_fixup stac92hd73xx_fixups[] = { @@ -2348,6 +2141,54 @@ static const struct snd_pci_quirk stac92hd83xxx_fixup_tbl[] = { {} /* terminator */ }; +/* HP dv7 bass switch - GPIO5 */ +#define stac_hp_bass_gpio_info snd_ctl_boolean_mono_info +static int stac_hp_bass_gpio_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct sigmatel_spec *spec = codec->spec; + ucontrol->value.integer.value[0] = !!(spec->gpio_data & 0x20); + return 0; +} + +static int stac_hp_bass_gpio_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct sigmatel_spec *spec = codec->spec; + unsigned int gpio_data; + + gpio_data = (spec->gpio_data & ~0x20) | + (ucontrol->value.integer.value[0] ? 0x20 : 0); + if (gpio_data == spec->gpio_data) + return 0; + spec->gpio_data = gpio_data; + stac_gpio_set(codec, spec->gpio_mask, spec->gpio_dir, spec->gpio_data); + return 1; +} + +static const struct snd_kcontrol_new stac_hp_bass_sw_ctrl = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .info = stac_hp_bass_gpio_info, + .get = stac_hp_bass_gpio_get, + .put = stac_hp_bass_gpio_put, +}; + +static int stac_add_hp_bass_switch(struct hda_codec *codec) +{ + struct sigmatel_spec *spec = codec->spec; + + if (!snd_hda_gen_add_kctl(&spec->gen, "Bass Speaker Playback Switch", + &stac_hp_bass_sw_ctrl)) + return -ENOMEM; + + spec->gpio_mask |= 0x20; + spec->gpio_dir |= 0x20; + spec->gpio_data |= 0x20; + return 0; +} + static const struct hda_pintbl ref92hd71bxx_pin_configs[] = { { 0x0a, 0x02214030 }, { 0x0b, 0x02a19040 }, @@ -2420,66 +2261,29 @@ static void stac92hd71bxx_fixup_ref(struct hda_codec *codec, spec->gpio_mask = spec->gpio_dir = spec->gpio_data = 0; } -static void stac92hd71bxx_fixup_no_dmic(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct sigmatel_spec *spec = codec->spec; - - if (action != HDA_FIXUP_ACT_PRE_PROBE) - return; - spec->num_dmics = 0; - spec->num_smuxes = 0; - spec->num_dmuxes = 0; -} - -static void stac92hd71bxx_fixup_no_smux(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct sigmatel_spec *spec = codec->spec; - - if (action != HDA_FIXUP_ACT_PRE_PROBE) - return; - spec->num_dmics = 1; - spec->num_smuxes = 0; - spec->num_dmuxes = 1; -} - static void stac92hd71bxx_fixup_hp_m4(struct hda_codec *codec, const struct hda_fixup *fix, int action) { struct sigmatel_spec *spec = codec->spec; + struct hda_jack_tbl *jack; if (action != HDA_FIXUP_ACT_PRE_PROBE) return; /* Enable VREF power saving on GPIO1 detect */ - stac_add_event(codec, codec->afg, STAC_VREF_EVENT, 0x02); snd_hda_codec_write_cache(codec, codec->afg, 0, AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK, 0x02); - snd_hda_jack_detect_enable(codec, codec->afg, 0); + snd_hda_jack_detect_enable_callback(codec, codec->afg, + STAC_VREF_EVENT, + stac_vref_event); + jack = snd_hda_jack_tbl_get(codec, codec->afg); + if (jack) + jack->private_data = 0x02; + spec->gpio_mask |= 0x02; /* enable internal microphone */ snd_hda_codec_set_pincfg(codec, 0x0e, 0x01813040); - stac92xx_auto_set_pinctl(codec, 0x0e, - AC_PINCTL_IN_EN | AC_PINCTL_VREF_80); - - stac92hd71bxx_fixup_no_dmic(codec, fix, action); -} - -static void stac92hd71bxx_fixup_hp_dv4_1222nr(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct sigmatel_spec *spec = codec->spec; - - if (action != HDA_FIXUP_ACT_PRE_PROBE) - return; - spec->num_dmics = 1; - /* I don't know if it needs 1 or 2 smuxes - will wait for - * bug reports to fix if needed - */ - spec->num_smuxes = 1; - spec->num_dmuxes = 1; } static void stac92hd71bxx_fixup_hp_dv4(struct hda_codec *codec, @@ -2495,21 +2299,14 @@ static void stac92hd71bxx_fixup_hp_dv4(struct hda_codec *codec, static void stac92hd71bxx_fixup_hp_dv5(struct hda_codec *codec, const struct hda_fixup *fix, int action) { - struct sigmatel_spec *spec = codec->spec; unsigned int cap; switch (action) { case HDA_FIXUP_ACT_PRE_PROBE: snd_hda_codec_set_pincfg(codec, 0x0d, 0x90170010); - stac92xx_auto_set_pinctl(codec, 0x0d, AC_PINCTL_OUT_EN); break; case HDA_FIXUP_ACT_PROBE: - /* HP dv6 gives the headphone pin as a line-out. Thus we - * need to set hp_detect flag here to force to enable HP - * detection. - */ - spec->hp_detect = 1; /* enable bass on HP dv7 */ cap = snd_hda_param_read(codec, 0x1, AC_PAR_GPIO_CAP); cap &= AC_GPIO_IO_COUNT; @@ -2527,9 +2324,6 @@ static void stac92hd71bxx_fixup_hp_hdx(struct hda_codec *codec, if (action != HDA_FIXUP_ACT_PRE_PROBE) return; spec->gpio_led = 0x08; - spec->num_dmics = 1; - spec->num_smuxes = 1; - spec->num_dmuxes = 1; } @@ -2559,7 +2353,7 @@ static void stac92hd71bxx_fixup_hp(struct hda_codec *codec, } } - if (find_mute_led_cfg(codec, spec->default_polarity)) + if (find_mute_led_cfg(codec, 1)) snd_printd("mute LED gpio %d polarity %d\n", spec->gpio_led, spec->gpio_led_polarity); @@ -2574,20 +2368,14 @@ static const struct hda_fixup stac92hd71bxx_fixups[] = { [STAC_DELL_M4_1] = { .type = HDA_FIXUP_PINS, .v.pins = dell_m4_1_pin_configs, - .chained = true, - .chain_id = STAC_92HD71BXX_NO_SMUX, }, [STAC_DELL_M4_2] = { .type = HDA_FIXUP_PINS, .v.pins = dell_m4_2_pin_configs, - .chained = true, - .chain_id = STAC_92HD71BXX_NO_DMIC, }, [STAC_DELL_M4_3] = { .type = HDA_FIXUP_PINS, .v.pins = dell_m4_3_pin_configs, - .chained = true, - .chain_id = STAC_92HD71BXX_NO_SMUX, }, [STAC_HP_M4] = { .type = HDA_FIXUP_FUNC, @@ -2613,23 +2401,9 @@ static const struct hda_fixup stac92hd71bxx_fixups[] = { .chained = true, .chain_id = STAC_92HD71BXX_HP, }, - [STAC_HP_DV4_1222NR] = { + [STAC_92HD71BXX_HP] = { .type = HDA_FIXUP_FUNC, - .v.func = stac92hd71bxx_fixup_hp_dv4_1222nr, - .chained = true, - .chain_id = STAC_HP_DV4, - }, - [STAC_92HD71BXX_NO_DMIC] = { - .type = HDA_FIXUP_FUNC, - .v.func = stac92hd71bxx_fixup_no_dmic, - }, - [STAC_92HD71BXX_NO_SMUX] = { - .type = HDA_FIXUP_FUNC, - .v.func = stac92hd71bxx_fixup_no_smux, - }, - [STAC_92HD71BXX_HP] = { - .type = HDA_FIXUP_FUNC, - .v.func = stac92hd71bxx_fixup_hp, + .v.func = stac92hd71bxx_fixup_hp, }, }; @@ -2642,7 +2416,7 @@ static const struct hda_model_fixup stac92hd71bxx_models[] = { { .id = STAC_HP_DV4, .name = "hp-dv4" }, { .id = STAC_HP_DV5, .name = "hp-dv5" }, { .id = STAC_HP_HDX, .name = "hp-hdx" }, - { .id = STAC_HP_DV4_1222NR, .name = "hp-dv4-1222nr" }, + { .id = STAC_HP_DV4, .name = "hp-dv4-1222nr" }, {} }; @@ -2652,8 +2426,6 @@ static const struct snd_pci_quirk stac92hd71bxx_fixup_tbl[] = { "DFI LanParty", STAC_92HD71BXX_REF), SND_PCI_QUIRK(PCI_VENDOR_ID_DFI, 0x3101, "DFI LanParty", STAC_92HD71BXX_REF), - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x30fb, - "HP dv4-1222nr", STAC_HP_DV4_1222NR), SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xfff0, 0x1720, "HP", STAC_HP_DV5), SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xfff0, 0x3080, @@ -3234,11 +3006,9 @@ static const struct hda_pintbl dell_3st_pin_configs[] = { static void stac927x_fixup_ref_no_jd(struct hda_codec *codec, const struct hda_fixup *fix, int action) { - struct sigmatel_spec *spec = codec->spec; - /* no jack detecion for ref-no-jd model */ - if (action == HDA_FIXUP_ACT_PROBE) - spec->hp_detect = 0; + if (action == HDA_FIXUP_ACT_PRE_PROBE) + codec->no_jack_detect = 1; } static void stac927x_fixup_ref(struct hda_codec *codec, @@ -3266,13 +3036,9 @@ static void stac927x_fixup_dell_dmic(struct hda_codec *codec, spec->eapd_mask = spec->gpio_mask = 0x04; spec->gpio_dir = spec->gpio_data = 0x04; } - spec->dmic_nids = stac927x_dmic_nids; - spec->num_dmics = STAC927X_NUM_DMICS; snd_hda_add_verbs(codec, dell_3st_core_init); spec->volknob_init = 1; - spec->dmux_nids = stac927x_dmux_nids; - spec->num_dmuxes = ARRAY_SIZE(stac927x_dmux_nids); } static void stac927x_fixup_volknob(struct hda_codec *codec, @@ -3511,18 +3277,20 @@ static void stac9205_fixup_dell_m43(struct hda_codec *codec, const struct hda_fixup *fix, int action) { struct sigmatel_spec *spec = codec->spec; - int err; + struct hda_jack_tbl *jack; if (action == HDA_FIXUP_ACT_PRE_PROBE) { snd_hda_apply_pincfgs(codec, dell_9205_m43_pin_configs); /* Enable unsol response for GPIO4/Dock HP connection */ - err = stac_add_event(codec, codec->afg, STAC_VREF_EVENT, 0x01); - if (err < 0) - return; snd_hda_codec_write_cache(codec, codec->afg, 0, AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK, 0x10); - snd_hda_jack_detect_enable(codec, codec->afg, 0); + snd_hda_jack_detect_enable_callback(codec, codec->afg, + STAC_VREF_EVENT, + stac_vref_event); + jack = snd_hda_jack_tbl_get(codec, codec->afg); + if (jack) + jack->private_data = 0x01; spec->gpio_dir = 0x0b; spec->eapd_mask = 0x01; @@ -3610,2687 +3378,163 @@ static const struct snd_pci_quirk stac9205_fixup_tbl[] = { "Dell Precision", STAC_9205_DELL_M43), SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x021b, "Dell Precision", STAC_9205_DELL_M43), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x021c, - "Dell Precision", STAC_9205_DELL_M43), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x021f, - "Dell Inspiron", STAC_9205_DELL_M44), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0228, - "Dell Vostro 1500", STAC_9205_DELL_M42), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0229, - "Dell Vostro 1700", STAC_9205_DELL_M42), - /* Gateway */ - SND_PCI_QUIRK(0x107b, 0x0560, "Gateway T6834c", STAC_9205_EAPD), - SND_PCI_QUIRK(0x107b, 0x0565, "Gateway T1616", STAC_9205_EAPD), - {} /* terminator */ -}; - -/* - * Analog playback callbacks - */ -static int stac92xx_playback_pcm_open(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct sigmatel_spec *spec = codec->spec; - if (spec->stream_delay) - msleep(spec->stream_delay); - return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream, - hinfo); -} - -static int stac92xx_playback_pcm_prepare(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - unsigned int stream_tag, - unsigned int format, - struct snd_pcm_substream *substream) -{ - struct sigmatel_spec *spec = codec->spec; - return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag, format, substream); -} - -static int stac92xx_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct sigmatel_spec *spec = codec->spec; - return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout); -} - -/* - * Digital playback callbacks - */ -static int stac92xx_dig_playback_pcm_open(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct sigmatel_spec *spec = codec->spec; - return snd_hda_multi_out_dig_open(codec, &spec->multiout); -} - -static int stac92xx_dig_playback_pcm_close(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct sigmatel_spec *spec = codec->spec; - return snd_hda_multi_out_dig_close(codec, &spec->multiout); -} - -static int stac92xx_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - unsigned int stream_tag, - unsigned int format, - struct snd_pcm_substream *substream) -{ - struct sigmatel_spec *spec = codec->spec; - return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, - stream_tag, format, substream); -} - -static int stac92xx_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct sigmatel_spec *spec = codec->spec; - return snd_hda_multi_out_dig_cleanup(codec, &spec->multiout); -} - - -/* - * Analog capture callbacks - */ -static int stac92xx_capture_pcm_prepare(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - unsigned int stream_tag, - unsigned int format, - struct snd_pcm_substream *substream) -{ - struct sigmatel_spec *spec = codec->spec; - hda_nid_t nid = spec->adc_nids[substream->number]; - - if (spec->powerdown_adcs) { - msleep(40); - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_POWER_STATE, AC_PWRST_D0); - } - snd_hda_codec_setup_stream(codec, nid, stream_tag, 0, format); - return 0; -} - -static int stac92xx_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct sigmatel_spec *spec = codec->spec; - hda_nid_t nid = spec->adc_nids[substream->number]; - - snd_hda_codec_cleanup_stream(codec, nid); - if (spec->powerdown_adcs) - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_POWER_STATE, AC_PWRST_D3); - return 0; -} - -static const struct hda_pcm_stream stac92xx_pcm_digital_playback = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, - /* NID is set in stac92xx_build_pcms */ - .ops = { - .open = stac92xx_dig_playback_pcm_open, - .close = stac92xx_dig_playback_pcm_close, - .prepare = stac92xx_dig_playback_pcm_prepare, - .cleanup = stac92xx_dig_playback_pcm_cleanup - }, -}; - -static const struct hda_pcm_stream stac92xx_pcm_digital_capture = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, - /* NID is set in stac92xx_build_pcms */ -}; - -static const struct hda_pcm_stream stac92xx_pcm_analog_playback = { - .substreams = 1, - .channels_min = 2, - .channels_max = 8, - .nid = 0x02, /* NID to query formats and rates */ - .ops = { - .open = stac92xx_playback_pcm_open, - .prepare = stac92xx_playback_pcm_prepare, - .cleanup = stac92xx_playback_pcm_cleanup - }, -}; - -static const struct hda_pcm_stream stac92xx_pcm_analog_alt_playback = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, - .nid = 0x06, /* NID to query formats and rates */ - .ops = { - .open = stac92xx_playback_pcm_open, - .prepare = stac92xx_playback_pcm_prepare, - .cleanup = stac92xx_playback_pcm_cleanup - }, -}; - -static const struct hda_pcm_stream stac92xx_pcm_analog_capture = { - .channels_min = 2, - .channels_max = 2, - /* NID + .substreams is set in stac92xx_build_pcms */ - .ops = { - .prepare = stac92xx_capture_pcm_prepare, - .cleanup = stac92xx_capture_pcm_cleanup - }, -}; - -static int stac92xx_build_pcms(struct hda_codec *codec) -{ - struct sigmatel_spec *spec = codec->spec; - struct hda_pcm *info = spec->pcm_rec; - - codec->num_pcms = 1; - codec->pcm_info = info; - - info->name = "STAC92xx Analog"; - info->stream[SNDRV_PCM_STREAM_PLAYBACK] = stac92xx_pcm_analog_playback; - info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = - spec->multiout.dac_nids[0]; - if (spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT && - spec->autocfg.line_outs == 2) - info->stream[SNDRV_PCM_STREAM_PLAYBACK].chmap = - snd_pcm_2_1_chmaps; - - info->stream[SNDRV_PCM_STREAM_CAPTURE] = stac92xx_pcm_analog_capture; - info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0]; - info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = spec->num_adcs; - - if (spec->alt_switch) { - codec->num_pcms++; - info++; - info->name = "STAC92xx Analog Alt"; - info->stream[SNDRV_PCM_STREAM_PLAYBACK] = stac92xx_pcm_analog_alt_playback; - } - - if (spec->multiout.dig_out_nid || spec->dig_in_nid) { - codec->num_pcms++; - info++; - info->name = "STAC92xx Digital"; - info->pcm_type = spec->autocfg.dig_out_type[0]; - if (spec->multiout.dig_out_nid) { - info->stream[SNDRV_PCM_STREAM_PLAYBACK] = stac92xx_pcm_digital_playback; - info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dig_out_nid; - } - if (spec->dig_in_nid) { - info->stream[SNDRV_PCM_STREAM_CAPTURE] = stac92xx_pcm_digital_capture; - info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in_nid; - } - } - - return 0; -} - -static void stac92xx_auto_set_pinctl(struct hda_codec *codec, hda_nid_t nid, int pin_type) - -{ - snd_hda_set_pin_ctl_cache(codec, nid, pin_type); -} - -#define stac92xx_hp_switch_info snd_ctl_boolean_mono_info - -static int stac92xx_hp_switch_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct sigmatel_spec *spec = codec->spec; - - ucontrol->value.integer.value[0] = !!spec->hp_switch; - return 0; -} - -static void stac_issue_unsol_event(struct hda_codec *codec, hda_nid_t nid); - -static int stac92xx_hp_switch_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct sigmatel_spec *spec = codec->spec; - int nid = kcontrol->private_value; - - spec->hp_switch = ucontrol->value.integer.value[0] ? nid : 0; - - /* check to be sure that the ports are up to date with - * switch changes - */ - stac_issue_unsol_event(codec, nid); - - return 1; -} - -static int stac92xx_dc_bias_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - int i; - static const char * const texts[] = { - "Mic In", "Line In", "Line Out" - }; - - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct sigmatel_spec *spec = codec->spec; - hda_nid_t nid = kcontrol->private_value; - - if (nid == spec->mic_switch || nid == spec->line_switch) - i = 3; - else - i = 2; - - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->value.enumerated.items = i; - uinfo->count = 1; - if (uinfo->value.enumerated.item >= i) - uinfo->value.enumerated.item = i-1; - strcpy(uinfo->value.enumerated.name, - texts[uinfo->value.enumerated.item]); - - return 0; -} - -static int stac92xx_dc_bias_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - hda_nid_t nid = kcontrol->private_value; - unsigned int vref = stac92xx_vref_get(codec, nid); - - if (vref == snd_hda_get_default_vref(codec, nid)) - ucontrol->value.enumerated.item[0] = 0; - else if (vref == AC_PINCTL_VREF_GRD) - ucontrol->value.enumerated.item[0] = 1; - else if (vref == AC_PINCTL_VREF_HIZ) - ucontrol->value.enumerated.item[0] = 2; - - return 0; -} - -static int stac92xx_dc_bias_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - unsigned int new_vref = 0; - int error; - hda_nid_t nid = kcontrol->private_value; - - if (ucontrol->value.enumerated.item[0] == 0) - new_vref = snd_hda_get_default_vref(codec, nid); - else if (ucontrol->value.enumerated.item[0] == 1) - new_vref = AC_PINCTL_VREF_GRD; - else if (ucontrol->value.enumerated.item[0] == 2) - new_vref = AC_PINCTL_VREF_HIZ; - else - return 0; - - if (new_vref != stac92xx_vref_get(codec, nid)) { - error = stac92xx_vref_set(codec, nid, new_vref); - return error; - } - - return 0; -} - -static int stac92xx_io_switch_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - char *texts[2]; - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct sigmatel_spec *spec = codec->spec; - - if (kcontrol->private_value == spec->line_switch) - texts[0] = "Line In"; - else - texts[0] = "Mic In"; - texts[1] = "Line Out"; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->value.enumerated.items = 2; - uinfo->count = 1; - - if (uinfo->value.enumerated.item >= 2) - uinfo->value.enumerated.item = 1; - strcpy(uinfo->value.enumerated.name, - texts[uinfo->value.enumerated.item]); - - return 0; -} - -static int stac92xx_io_switch_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct sigmatel_spec *spec = codec->spec; - hda_nid_t nid = kcontrol->private_value; - int io_idx = (nid == spec->mic_switch) ? 1 : 0; - - ucontrol->value.enumerated.item[0] = spec->io_switch[io_idx]; - return 0; -} - -static int stac92xx_io_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct sigmatel_spec *spec = codec->spec; - hda_nid_t nid = kcontrol->private_value; - int io_idx = (nid == spec->mic_switch) ? 1 : 0; - unsigned short val = !!ucontrol->value.enumerated.item[0]; - - spec->io_switch[io_idx] = val; - - if (val) - stac92xx_auto_set_pinctl(codec, nid, AC_PINCTL_OUT_EN); - else { - unsigned int pinctl = AC_PINCTL_IN_EN; - if (io_idx) /* set VREF for mic */ - pinctl |= snd_hda_get_default_vref(codec, nid); - stac92xx_auto_set_pinctl(codec, nid, pinctl); - } - - /* check the auto-mute again: we need to mute/unmute the speaker - * appropriately according to the pin direction - */ - if (spec->hp_detect) - stac_issue_unsol_event(codec, nid); - - return 1; -} - -#define stac92xx_clfe_switch_info snd_ctl_boolean_mono_info - -static int stac92xx_clfe_switch_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct sigmatel_spec *spec = codec->spec; - - ucontrol->value.integer.value[0] = spec->clfe_swap; - return 0; -} - -static int stac92xx_clfe_switch_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct sigmatel_spec *spec = codec->spec; - hda_nid_t nid = kcontrol->private_value & 0xff; - unsigned int val = !!ucontrol->value.integer.value[0]; - - if (spec->clfe_swap == val) - return 0; - - spec->clfe_swap = val; - - snd_hda_codec_write_cache(codec, nid, 0, AC_VERB_SET_EAPD_BTLENABLE, - spec->clfe_swap ? 0x4 : 0x0); - - return 1; -} - -#define STAC_CODEC_HP_SWITCH(xname) \ - { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ - .name = xname, \ - .index = 0, \ - .info = stac92xx_hp_switch_info, \ - .get = stac92xx_hp_switch_get, \ - .put = stac92xx_hp_switch_put, \ - } - -#define STAC_CODEC_IO_SWITCH(xname, xpval) \ - { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ - .name = xname, \ - .index = 0, \ - .info = stac92xx_io_switch_info, \ - .get = stac92xx_io_switch_get, \ - .put = stac92xx_io_switch_put, \ - .private_value = xpval, \ - } - -#define STAC_CODEC_CLFE_SWITCH(xname, xpval) \ - { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ - .name = xname, \ - .index = 0, \ - .info = stac92xx_clfe_switch_info, \ - .get = stac92xx_clfe_switch_get, \ - .put = stac92xx_clfe_switch_put, \ - .private_value = xpval, \ - } - -enum { - STAC_CTL_WIDGET_VOL, - STAC_CTL_WIDGET_MUTE, - STAC_CTL_WIDGET_MUTE_BEEP, - STAC_CTL_WIDGET_MONO_MUX, - STAC_CTL_WIDGET_HP_SWITCH, - STAC_CTL_WIDGET_IO_SWITCH, - STAC_CTL_WIDGET_CLFE_SWITCH, - STAC_CTL_WIDGET_DC_BIAS -}; - -static const struct snd_kcontrol_new stac92xx_control_templates[] = { - HDA_CODEC_VOLUME(NULL, 0, 0, 0), - HDA_CODEC_MUTE(NULL, 0, 0, 0), - HDA_CODEC_MUTE_BEEP(NULL, 0, 0, 0), - STAC_MONO_MUX, - STAC_CODEC_HP_SWITCH(NULL), - STAC_CODEC_IO_SWITCH(NULL, 0), - STAC_CODEC_CLFE_SWITCH(NULL, 0), - DC_BIAS(NULL, 0, 0), -}; - -/* add dynamic controls */ -static struct snd_kcontrol_new * -stac_control_new(struct sigmatel_spec *spec, - const struct snd_kcontrol_new *ktemp, - const char *name, - unsigned int subdev) -{ - struct snd_kcontrol_new *knew; - - knew = snd_array_new(&spec->kctls); - if (!knew) - return NULL; - *knew = *ktemp; - knew->name = kstrdup(name, GFP_KERNEL); - if (!knew->name) { - /* roolback */ - memset(knew, 0, sizeof(*knew)); - spec->kctls.alloced--; - return NULL; - } - knew->subdevice = subdev; - return knew; -} - -static struct snd_kcontrol_new * -add_control_temp(struct sigmatel_spec *spec, - const struct snd_kcontrol_new *ktemp, - int idx, const char *name, - unsigned long val) -{ - struct snd_kcontrol_new *knew = stac_control_new(spec, ktemp, name, - HDA_SUBDEV_AMP_FLAG); - if (!knew) - return NULL; - knew->index = idx; - knew->private_value = val; - return knew; -} - -static int stac92xx_add_control_temp(struct sigmatel_spec *spec, - const struct snd_kcontrol_new *ktemp, - int idx, const char *name, - unsigned long val) -{ - return add_control_temp(spec, ktemp, idx, name, val) ? 0 : -ENOMEM; -} - -static inline int stac92xx_add_control_idx(struct sigmatel_spec *spec, - int type, int idx, const char *name, - unsigned long val) -{ - return stac92xx_add_control_temp(spec, - &stac92xx_control_templates[type], - idx, name, val); -} - - -/* add dynamic controls */ -static inline int stac92xx_add_control(struct sigmatel_spec *spec, int type, - const char *name, unsigned long val) -{ - return stac92xx_add_control_idx(spec, type, 0, name, val); -} - -static const struct snd_kcontrol_new stac_input_src_temp = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Input Source", - .info = stac92xx_mux_enum_info, - .get = stac92xx_mux_enum_get, - .put = stac92xx_mux_enum_put, -}; - -static inline int stac92xx_add_jack_mode_control(struct hda_codec *codec, - hda_nid_t nid, int idx) -{ - int def_conf = snd_hda_codec_get_pincfg(codec, nid); - int control = 0; - struct sigmatel_spec *spec = codec->spec; - char name[22]; - - if (snd_hda_get_input_pin_attr(def_conf) != INPUT_PIN_ATTR_INT) { - if (spec->headset_jack && snd_hda_get_input_pin_attr(def_conf) - != INPUT_PIN_ATTR_DOCK) - return 0; - if (snd_hda_get_default_vref(codec, nid) == AC_PINCTL_VREF_GRD - && nid == spec->line_switch) - control = STAC_CTL_WIDGET_IO_SWITCH; - else if (snd_hda_query_pin_caps(codec, nid) - & (AC_PINCAP_VREF_GRD << AC_PINCAP_VREF_SHIFT)) - control = STAC_CTL_WIDGET_DC_BIAS; - else if (nid == spec->mic_switch) - control = STAC_CTL_WIDGET_IO_SWITCH; - } - - if (control) { - snd_hda_get_pin_label(codec, nid, &spec->autocfg, - name, sizeof(name), NULL); - return stac92xx_add_control(codec->spec, control, - strcat(name, " Jack Mode"), nid); - } - - return 0; -} - -static int stac92xx_add_input_source(struct sigmatel_spec *spec) -{ - struct snd_kcontrol_new *knew; - struct hda_input_mux *imux = &spec->private_imux; - - if (spec->auto_mic) - return 0; /* no need for input source */ - if (!spec->num_adcs || imux->num_items <= 1) - return 0; /* no need for input source control */ - knew = stac_control_new(spec, &stac_input_src_temp, - stac_input_src_temp.name, 0); - if (!knew) - return -ENOMEM; - knew->count = spec->num_adcs; - return 0; -} - -/* check whether the line-input can be used as line-out */ -static hda_nid_t check_line_out_switch(struct hda_codec *codec) -{ - struct sigmatel_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - hda_nid_t nid; - unsigned int pincap; - int i; - - if (cfg->line_out_type != AUTO_PIN_LINE_OUT) - return 0; - for (i = 0; i < cfg->num_inputs; i++) { - if (cfg->inputs[i].type == AUTO_PIN_LINE_IN) { - nid = cfg->inputs[i].pin; - pincap = snd_hda_query_pin_caps(codec, nid); - if (pincap & AC_PINCAP_OUT) - return nid; - } - } - return 0; -} - -static hda_nid_t get_unassigned_dac(struct hda_codec *codec, hda_nid_t nid); - -/* check whether the mic-input can be used as line-out */ -static hda_nid_t check_mic_out_switch(struct hda_codec *codec, hda_nid_t *dac) -{ - struct sigmatel_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - unsigned int def_conf, pincap; - int i; - - *dac = 0; - if (cfg->line_out_type != AUTO_PIN_LINE_OUT) - return 0; - for (i = 0; i < cfg->num_inputs; i++) { - hda_nid_t nid = cfg->inputs[i].pin; - if (cfg->inputs[i].type != AUTO_PIN_MIC) - continue; - def_conf = snd_hda_codec_get_pincfg(codec, nid); - /* some laptops have an internal analog microphone - * which can't be used as a output */ - if (snd_hda_get_input_pin_attr(def_conf) != INPUT_PIN_ATTR_INT) { - pincap = snd_hda_query_pin_caps(codec, nid); - if (pincap & AC_PINCAP_OUT) { - *dac = get_unassigned_dac(codec, nid); - if (*dac) - return nid; - } - } - } - return 0; -} - -static int is_in_dac_nids(struct sigmatel_spec *spec, hda_nid_t nid) -{ - int i; - - for (i = 0; i < spec->multiout.num_dacs; i++) { - if (spec->multiout.dac_nids[i] == nid) - return 1; - } - - return 0; -} - -static int check_all_dac_nids(struct sigmatel_spec *spec, hda_nid_t nid) -{ - int i; - if (is_in_dac_nids(spec, nid)) - return 1; - for (i = 0; i < spec->autocfg.hp_outs; i++) - if (spec->hp_dacs[i] == nid) - return 1; - for (i = 0; i < spec->autocfg.speaker_outs; i++) - if (spec->speaker_dacs[i] == nid) - return 1; - return 0; -} - -static hda_nid_t get_unassigned_dac(struct hda_codec *codec, hda_nid_t nid) -{ - struct sigmatel_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - int j, conn_len; - hda_nid_t conn[HDA_MAX_CONNECTIONS], fallback_dac; - unsigned int wcaps, wtype; - - conn_len = snd_hda_get_connections(codec, nid, conn, - HDA_MAX_CONNECTIONS); - /* 92HD88: trace back up the link of nids to find the DAC */ - while (conn_len == 1 && (get_wcaps_type(get_wcaps(codec, conn[0])) - != AC_WID_AUD_OUT)) { - nid = conn[0]; - conn_len = snd_hda_get_connections(codec, nid, conn, - HDA_MAX_CONNECTIONS); - } - for (j = 0; j < conn_len; j++) { - wcaps = get_wcaps(codec, conn[j]); - wtype = get_wcaps_type(wcaps); - /* we check only analog outputs */ - if (wtype != AC_WID_AUD_OUT || (wcaps & AC_WCAP_DIGITAL)) - continue; - /* if this route has a free DAC, assign it */ - if (!check_all_dac_nids(spec, conn[j])) { - if (conn_len > 1) { - /* select this DAC in the pin's input mux */ - snd_hda_codec_write_cache(codec, nid, 0, - AC_VERB_SET_CONNECT_SEL, j); - } - return conn[j]; - } - } - - /* if all DACs are already assigned, connect to the primary DAC, - unless we're assigning a secondary headphone */ - fallback_dac = spec->multiout.dac_nids[0]; - if (spec->multiout.hp_nid) { - for (j = 0; j < cfg->hp_outs; j++) - if (cfg->hp_pins[j] == nid) { - fallback_dac = spec->multiout.hp_nid; - break; - } - } - - if (conn_len > 1) { - for (j = 0; j < conn_len; j++) { - if (conn[j] == fallback_dac) { - snd_hda_codec_write_cache(codec, nid, 0, - AC_VERB_SET_CONNECT_SEL, j); - break; - } - } - } - return 0; -} - -static int add_spec_dacs(struct sigmatel_spec *spec, hda_nid_t nid); -static int add_spec_extra_dacs(struct sigmatel_spec *spec, hda_nid_t nid); - -/* - * Fill in the dac_nids table from the parsed pin configuration - * This function only works when every pin in line_out_pins[] - * contains atleast one DAC in its connection list. Some 92xx - * codecs are not connected directly to a DAC, such as the 9200 - * and 9202/925x. For those, dac_nids[] must be hard-coded. - */ -static int stac92xx_auto_fill_dac_nids(struct hda_codec *codec) -{ - struct sigmatel_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - int i; - hda_nid_t nid, dac; - - for (i = 0; i < cfg->line_outs; i++) { - nid = cfg->line_out_pins[i]; - dac = get_unassigned_dac(codec, nid); - if (!dac) { - if (spec->multiout.num_dacs > 0) { - /* we have already working output pins, - * so let's drop the broken ones again - */ - cfg->line_outs = spec->multiout.num_dacs; - break; - } - /* error out, no available DAC found */ - snd_printk(KERN_ERR - "%s: No available DAC for pin 0x%x\n", - __func__, nid); - return -ENODEV; - } - add_spec_dacs(spec, dac); - } - - for (i = 0; i < cfg->hp_outs; i++) { - nid = cfg->hp_pins[i]; - dac = get_unassigned_dac(codec, nid); - if (dac) { - if (!spec->multiout.hp_nid) - spec->multiout.hp_nid = dac; - else - add_spec_extra_dacs(spec, dac); - } - spec->hp_dacs[i] = dac; - } - - for (i = 0; i < cfg->speaker_outs; i++) { - nid = cfg->speaker_pins[i]; - dac = get_unassigned_dac(codec, nid); - if (dac) - add_spec_extra_dacs(spec, dac); - spec->speaker_dacs[i] = dac; - } - - /* add line-in as output */ - nid = check_line_out_switch(codec); - if (nid) { - dac = get_unassigned_dac(codec, nid); - if (dac) { - snd_printdd("STAC: Add line-in 0x%x as output %d\n", - nid, cfg->line_outs); - cfg->line_out_pins[cfg->line_outs] = nid; - cfg->line_outs++; - spec->line_switch = nid; - add_spec_dacs(spec, dac); - } - } - /* add mic as output */ - nid = check_mic_out_switch(codec, &dac); - if (nid && dac) { - snd_printdd("STAC: Add mic-in 0x%x as output %d\n", - nid, cfg->line_outs); - cfg->line_out_pins[cfg->line_outs] = nid; - cfg->line_outs++; - spec->mic_switch = nid; - add_spec_dacs(spec, dac); - } - - snd_printd("stac92xx: dac_nids=%d (0x%x/0x%x/0x%x/0x%x/0x%x)\n", - spec->multiout.num_dacs, - spec->multiout.dac_nids[0], - spec->multiout.dac_nids[1], - spec->multiout.dac_nids[2], - spec->multiout.dac_nids[3], - spec->multiout.dac_nids[4]); - - return 0; -} - -/* create volume control/switch for the given prefx type */ -static int create_controls_idx(struct hda_codec *codec, const char *pfx, - int idx, hda_nid_t nid, int chs) -{ - struct sigmatel_spec *spec = codec->spec; - char name[32]; - int err; - - if (!spec->check_volume_offset) { - unsigned int caps, step, nums, db_scale; - caps = query_amp_caps(codec, nid, HDA_OUTPUT); - step = (caps & AC_AMPCAP_STEP_SIZE) >> - AC_AMPCAP_STEP_SIZE_SHIFT; - step = (step + 1) * 25; /* in .01dB unit */ - nums = (caps & AC_AMPCAP_NUM_STEPS) >> - AC_AMPCAP_NUM_STEPS_SHIFT; - db_scale = nums * step; - /* if dB scale is over -64dB, and finer enough, - * let's reduce it to half - */ - if (db_scale > 6400 && nums >= 0x1f) - spec->volume_offset = nums / 2; - spec->check_volume_offset = 1; - } - - sprintf(name, "%s Playback Volume", pfx); - err = stac92xx_add_control_idx(spec, STAC_CTL_WIDGET_VOL, idx, name, - HDA_COMPOSE_AMP_VAL_OFS(nid, chs, 0, HDA_OUTPUT, - spec->volume_offset)); - if (err < 0) - return err; - sprintf(name, "%s Playback Switch", pfx); - err = stac92xx_add_control_idx(spec, STAC_CTL_WIDGET_MUTE, idx, name, - HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT)); - if (err < 0) - return err; - return 0; -} - -#define create_controls(codec, pfx, nid, chs) \ - create_controls_idx(codec, pfx, 0, nid, chs) - -static int add_spec_dacs(struct sigmatel_spec *spec, hda_nid_t nid) -{ - if (spec->multiout.num_dacs > 4) { - printk(KERN_WARNING "stac92xx: No space for DAC 0x%x\n", nid); - return 1; - } else { - snd_BUG_ON(spec->multiout.dac_nids != spec->dac_nids); - spec->dac_nids[spec->multiout.num_dacs] = nid; - spec->multiout.num_dacs++; - } - return 0; -} - -static int add_spec_extra_dacs(struct sigmatel_spec *spec, hda_nid_t nid) -{ - int i; - for (i = 0; i < ARRAY_SIZE(spec->multiout.extra_out_nid); i++) { - if (!spec->multiout.extra_out_nid[i]) { - spec->multiout.extra_out_nid[i] = nid; - return 0; - } - } - printk(KERN_WARNING "stac92xx: No space for extra DAC 0x%x\n", nid); - return 1; -} - -/* Create output controls - * The mixer elements are named depending on the given type (AUTO_PIN_XXX_OUT) - */ -static int create_multi_out_ctls(struct hda_codec *codec, int num_outs, - const hda_nid_t *pins, - const hda_nid_t *dac_nids, - int type) -{ - struct sigmatel_spec *spec = codec->spec; - static const char * const chname[4] = { - "Front", "Surround", NULL /*CLFE*/, "Side" - }; - hda_nid_t nid; - int i, err; - unsigned int wid_caps; - - for (i = 0; i < num_outs && i < ARRAY_SIZE(chname); i++) { - if (type == AUTO_PIN_HP_OUT && !spec->hp_detect) { - if (is_jack_detectable(codec, pins[i])) - spec->hp_detect = 1; - } - nid = dac_nids[i]; - if (!nid) - continue; - if (type != AUTO_PIN_HP_OUT && i == 2) { - /* Center/LFE */ - err = create_controls(codec, "Center", nid, 1); - if (err < 0) - return err; - err = create_controls(codec, "LFE", nid, 2); - if (err < 0) - return err; - - wid_caps = get_wcaps(codec, nid); - - if (wid_caps & AC_WCAP_LR_SWAP) { - err = stac92xx_add_control(spec, - STAC_CTL_WIDGET_CLFE_SWITCH, - "Swap Center/LFE Playback Switch", nid); - - if (err < 0) - return err; - } - - } else { - const char *name; - int idx; - switch (type) { - case AUTO_PIN_HP_OUT: - name = "Headphone"; - idx = i; - break; - case AUTO_PIN_SPEAKER_OUT: - if (num_outs <= 2) { - name = i ? "Bass Speaker" : "Speaker"; - idx = 0; - break; - } - /* Fall through in case of multi speaker outs */ - default: - name = chname[i]; - idx = 0; - break; - } - err = create_controls_idx(codec, name, idx, nid, 3); - if (err < 0) - return err; - } - } - return 0; -} - -static void stac_gpio_set(struct hda_codec *codec, unsigned int mask, - unsigned int dir_mask, unsigned int data); - -/* hook for controlling mic-mute LED GPIO */ -static int stac92xx_capture_sw_put_led(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct sigmatel_spec *spec = codec->spec; - int err; - bool mute; - - err = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol); - if (err <= 0) - return err; - mute = !(ucontrol->value.integer.value[0] && - ucontrol->value.integer.value[1]); - if (spec->mic_mute_led_on != mute) { - spec->mic_mute_led_on = mute; - if (mute) - spec->gpio_data |= spec->mic_mute_led_gpio; - else - spec->gpio_data &= ~spec->mic_mute_led_gpio; - stac_gpio_set(codec, spec->gpio_mask, - spec->gpio_dir, spec->gpio_data); - } - return err; -} - -static int stac92xx_add_capvol_ctls(struct hda_codec *codec, unsigned long vol, - unsigned long sw, int idx) -{ - struct sigmatel_spec *spec = codec->spec; - struct snd_kcontrol_new *knew; - int err; - - err = stac92xx_add_control_idx(codec->spec, STAC_CTL_WIDGET_VOL, idx, - "Capture Volume", vol); - if (err < 0) - return err; - - knew = add_control_temp(spec, - &stac92xx_control_templates[STAC_CTL_WIDGET_MUTE], - idx, "Capture Switch", sw); - if (!knew) - return -ENOMEM; - /* add a LED hook for some HP laptops */ - if (spec->mic_mute_led_gpio) - knew->put = stac92xx_capture_sw_put_led; - - return 0; -} - -/* add playback controls from the parsed DAC table */ -static int stac92xx_auto_create_multi_out_ctls(struct hda_codec *codec, - const struct auto_pin_cfg *cfg) -{ - struct sigmatel_spec *spec = codec->spec; - hda_nid_t nid; - int err; - int idx; - - err = create_multi_out_ctls(codec, cfg->line_outs, cfg->line_out_pins, - spec->multiout.dac_nids, - cfg->line_out_type); - if (err < 0) - return err; - - if (cfg->hp_outs > 1 && cfg->line_out_type == AUTO_PIN_LINE_OUT) { - err = stac92xx_add_control(spec, - STAC_CTL_WIDGET_HP_SWITCH, - "Headphone as Line Out Switch", - cfg->hp_pins[cfg->hp_outs - 1]); - if (err < 0) - return err; - } - - for (idx = 0; idx < cfg->num_inputs; idx++) { - if (cfg->inputs[idx].type > AUTO_PIN_LINE_IN) - break; - nid = cfg->inputs[idx].pin; - err = stac92xx_add_jack_mode_control(codec, nid, idx); - if (err < 0) - return err; - } - - return 0; -} - -/* add playback controls for Speaker and HP outputs */ -static int stac92xx_auto_create_hp_ctls(struct hda_codec *codec, - struct auto_pin_cfg *cfg) -{ - struct sigmatel_spec *spec = codec->spec; - int err; - - err = create_multi_out_ctls(codec, cfg->hp_outs, cfg->hp_pins, - spec->hp_dacs, AUTO_PIN_HP_OUT); - if (err < 0) - return err; - - err = create_multi_out_ctls(codec, cfg->speaker_outs, cfg->speaker_pins, - spec->speaker_dacs, AUTO_PIN_SPEAKER_OUT); - if (err < 0) - return err; - - return 0; -} - -/* labels for mono mux outputs */ -static const char * const stac92xx_mono_labels[4] = { - "DAC0", "DAC1", "Mixer", "DAC2" -}; - -/* create mono mux for mono out on capable codecs */ -static int stac92xx_auto_create_mono_output_ctls(struct hda_codec *codec) -{ - struct sigmatel_spec *spec = codec->spec; - struct hda_input_mux *mono_mux = &spec->private_mono_mux; - int i, num_cons; - hda_nid_t con_lst[ARRAY_SIZE(stac92xx_mono_labels)]; - - num_cons = snd_hda_get_connections(codec, - spec->mono_nid, - con_lst, - HDA_MAX_NUM_INPUTS); - if (num_cons <= 0 || num_cons > ARRAY_SIZE(stac92xx_mono_labels)) - return -EINVAL; - - for (i = 0; i < num_cons; i++) - snd_hda_add_imux_item(mono_mux, stac92xx_mono_labels[i], i, - NULL); - - return stac92xx_add_control(spec, STAC_CTL_WIDGET_MONO_MUX, - "Mono Mux", spec->mono_nid); -} - -/* create PC beep volume controls */ -static int stac92xx_auto_create_beep_ctls(struct hda_codec *codec, - hda_nid_t nid) -{ - struct sigmatel_spec *spec = codec->spec; - u32 caps = query_amp_caps(codec, nid, HDA_OUTPUT); - int err, type = STAC_CTL_WIDGET_MUTE_BEEP; - - if (spec->anabeep_nid == nid) - type = STAC_CTL_WIDGET_MUTE; - - /* check for mute support for the the amp */ - if ((caps & AC_AMPCAP_MUTE) >> AC_AMPCAP_MUTE_SHIFT) { - err = stac92xx_add_control(spec, type, - "Beep Playback Switch", - HDA_COMPOSE_AMP_VAL(nid, 1, 0, HDA_OUTPUT)); - if (err < 0) - return err; - } - - /* check to see if there is volume support for the amp */ - if ((caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT) { - err = stac92xx_add_control(spec, STAC_CTL_WIDGET_VOL, - "Beep Playback Volume", - HDA_COMPOSE_AMP_VAL(nid, 1, 0, HDA_OUTPUT)); - if (err < 0) - return err; - } - return 0; -} - -#ifdef CONFIG_SND_HDA_INPUT_BEEP -#define stac92xx_dig_beep_switch_info snd_ctl_boolean_mono_info - -static int stac92xx_dig_beep_switch_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - ucontrol->value.integer.value[0] = codec->beep->enabled; - return 0; -} - -static int stac92xx_dig_beep_switch_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - return snd_hda_enable_beep_device(codec, ucontrol->value.integer.value[0]); -} - -static const struct snd_kcontrol_new stac92xx_dig_beep_ctrl = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .info = stac92xx_dig_beep_switch_info, - .get = stac92xx_dig_beep_switch_get, - .put = stac92xx_dig_beep_switch_put, -}; - -static int stac92xx_beep_switch_ctl(struct hda_codec *codec) -{ - return stac92xx_add_control_temp(codec->spec, &stac92xx_dig_beep_ctrl, - 0, "Beep Playback Switch", 0); -} -#endif - -static int stac92xx_auto_create_mux_input_ctls(struct hda_codec *codec) -{ - struct sigmatel_spec *spec = codec->spec; - int i, j, err = 0; - - for (i = 0; i < spec->num_muxes; i++) { - hda_nid_t nid; - unsigned int wcaps; - unsigned long val; - - nid = spec->mux_nids[i]; - wcaps = get_wcaps(codec, nid); - if (!(wcaps & AC_WCAP_OUT_AMP)) - continue; - - /* check whether already the same control was created as - * normal Capture Volume. - */ - val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT); - for (j = 0; j < spec->num_caps; j++) { - if (spec->capvols[j] == val) - break; - } - if (j < spec->num_caps) - continue; - - err = stac92xx_add_control_idx(spec, STAC_CTL_WIDGET_VOL, i, - "Mux Capture Volume", val); - if (err < 0) - return err; - } - return 0; -}; - -static const char * const stac92xx_spdif_labels[3] = { - "Digital Playback", "Analog Mux 1", "Analog Mux 2", -}; - -static int stac92xx_auto_create_spdif_mux_ctls(struct hda_codec *codec) -{ - struct sigmatel_spec *spec = codec->spec; - struct hda_input_mux *spdif_mux = &spec->private_smux; - const char * const *labels = spec->spdif_labels; - int i, num_cons; - hda_nid_t con_lst[HDA_MAX_NUM_INPUTS]; - - num_cons = snd_hda_get_connections(codec, - spec->smux_nids[0], - con_lst, - HDA_MAX_NUM_INPUTS); - if (num_cons <= 0) - return -EINVAL; - - if (!labels) - labels = stac92xx_spdif_labels; - - for (i = 0; i < num_cons; i++) - snd_hda_add_imux_item(spdif_mux, labels[i], i, NULL); - - return 0; -} - -/* labels for dmic mux inputs */ -static const char * const stac92xx_dmic_labels[5] = { - "Analog Inputs", "Digital Mic 1", "Digital Mic 2", - "Digital Mic 3", "Digital Mic 4" -}; - -static hda_nid_t get_connected_node(struct hda_codec *codec, hda_nid_t mux, - int idx) -{ - hda_nid_t conn[HDA_MAX_NUM_INPUTS]; - int nums; - nums = snd_hda_get_connections(codec, mux, conn, ARRAY_SIZE(conn)); - if (idx >= 0 && idx < nums) - return conn[idx]; - return 0; -} - -/* look for NID recursively */ -#define get_connection_index(codec, mux, nid) \ - snd_hda_get_conn_index(codec, mux, nid, 1) - -/* create a volume assigned to the given pin (only if supported) */ -/* return 1 if the volume control is created */ -static int create_elem_capture_vol(struct hda_codec *codec, hda_nid_t nid, - const char *label, int idx, int direction) -{ - unsigned int caps, nums; - char name[32]; - int err; - - if (direction == HDA_OUTPUT) - caps = AC_WCAP_OUT_AMP; - else - caps = AC_WCAP_IN_AMP; - if (!(get_wcaps(codec, nid) & caps)) - return 0; - caps = query_amp_caps(codec, nid, direction); - nums = (caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT; - if (!nums) - return 0; - snprintf(name, sizeof(name), "%s Capture Volume", label); - err = stac92xx_add_control_idx(codec->spec, STAC_CTL_WIDGET_VOL, idx, name, - HDA_COMPOSE_AMP_VAL(nid, 3, 0, direction)); - if (err < 0) - return err; - return 1; -} - -/* create playback/capture controls for input pins on dmic capable codecs */ -static int stac92xx_auto_create_dmic_input_ctls(struct hda_codec *codec, - const struct auto_pin_cfg *cfg) -{ - struct sigmatel_spec *spec = codec->spec; - struct hda_input_mux *imux = &spec->private_imux; - struct hda_input_mux *dimux = &spec->private_dimux; - int err, i; - unsigned int def_conf; - - snd_hda_add_imux_item(dimux, stac92xx_dmic_labels[0], 0, NULL); - - for (i = 0; i < spec->num_dmics; i++) { - hda_nid_t nid; - int index, type_idx; - char label[32]; - - nid = spec->dmic_nids[i]; - if (get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_PIN) - continue; - def_conf = snd_hda_codec_get_pincfg(codec, nid); - if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE) - continue; - - index = get_connection_index(codec, spec->dmux_nids[0], nid); - if (index < 0) - continue; - - snd_hda_get_pin_label(codec, nid, &spec->autocfg, - label, sizeof(label), NULL); - snd_hda_add_imux_item(dimux, label, index, &type_idx); - if (snd_hda_get_bool_hint(codec, "separate_dmux") != 1) - snd_hda_add_imux_item(imux, label, index, &type_idx); - - err = create_elem_capture_vol(codec, nid, label, type_idx, - HDA_INPUT); - if (err < 0) - return err; - if (!err) { - err = create_elem_capture_vol(codec, nid, label, - type_idx, HDA_OUTPUT); - if (err < 0) - return err; - if (!err) { - nid = get_connected_node(codec, - spec->dmux_nids[0], index); - if (nid) - err = create_elem_capture_vol(codec, - nid, label, - type_idx, HDA_INPUT); - if (err < 0) - return err; - } - } - } - - return 0; -} - -static int check_mic_pin(struct hda_codec *codec, hda_nid_t nid, - hda_nid_t *fixed, hda_nid_t *ext, hda_nid_t *dock) -{ - unsigned int cfg; - unsigned int type; - - if (!nid) - return 0; - cfg = snd_hda_codec_get_pincfg(codec, nid); - type = get_defcfg_device(cfg); - switch (snd_hda_get_input_pin_attr(cfg)) { - case INPUT_PIN_ATTR_INT: - if (*fixed) - return 1; /* already occupied */ - if (type != AC_JACK_MIC_IN) - return 1; /* invalid type */ - *fixed = nid; - break; - case INPUT_PIN_ATTR_UNUSED: - break; - case INPUT_PIN_ATTR_DOCK: - if (*dock) - return 1; /* already occupied */ - if (type != AC_JACK_MIC_IN && type != AC_JACK_LINE_IN) - return 1; /* invalid type */ - *dock = nid; - break; - default: - if (*ext) - return 1; /* already occupied */ - if (type != AC_JACK_MIC_IN) - return 1; /* invalid type */ - *ext = nid; - break; - } - return 0; -} - -static int set_mic_route(struct hda_codec *codec, - struct sigmatel_mic_route *mic, - hda_nid_t pin) -{ - struct sigmatel_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - int i; - - mic->pin = pin; - if (pin == 0) - return 0; - for (i = 0; i < cfg->num_inputs; i++) { - if (pin == cfg->inputs[i].pin) - break; - } - if (i < cfg->num_inputs && cfg->inputs[i].type == AUTO_PIN_MIC) { - /* analog pin */ - i = get_connection_index(codec, spec->mux_nids[0], pin); - if (i < 0) - return -1; - mic->mux_idx = i; - mic->dmux_idx = -1; - if (spec->dmux_nids) - mic->dmux_idx = get_connection_index(codec, - spec->dmux_nids[0], - spec->mux_nids[0]); - } else if (spec->dmux_nids) { - /* digital pin */ - i = get_connection_index(codec, spec->dmux_nids[0], pin); - if (i < 0) - return -1; - mic->dmux_idx = i; - mic->mux_idx = -1; - if (spec->mux_nids) - mic->mux_idx = get_connection_index(codec, - spec->mux_nids[0], - spec->dmux_nids[0]); - } - return 0; -} - -/* return non-zero if the device is for automatic mic switch */ -static int stac_check_auto_mic(struct hda_codec *codec) -{ - struct sigmatel_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - hda_nid_t fixed, ext, dock; - int i; - - fixed = ext = dock = 0; - for (i = 0; i < cfg->num_inputs; i++) - if (check_mic_pin(codec, cfg->inputs[i].pin, - &fixed, &ext, &dock)) - return 0; - for (i = 0; i < spec->num_dmics; i++) - if (check_mic_pin(codec, spec->dmic_nids[i], - &fixed, &ext, &dock)) - return 0; - if (!fixed || (!ext && !dock)) - return 0; /* no input to switch */ - if (!is_jack_detectable(codec, ext)) - return 0; /* no unsol support */ - if (set_mic_route(codec, &spec->ext_mic, ext) || - set_mic_route(codec, &spec->int_mic, fixed) || - set_mic_route(codec, &spec->dock_mic, dock)) - return 0; /* something is wrong */ - return 1; -} - -/* create playback/capture controls for input pins */ -static int stac92xx_auto_create_analog_input_ctls(struct hda_codec *codec, const struct auto_pin_cfg *cfg) -{ - struct sigmatel_spec *spec = codec->spec; - struct hda_input_mux *imux = &spec->private_imux; - int i, j; - const char *label; - - for (i = 0; i < cfg->num_inputs; i++) { - hda_nid_t nid = cfg->inputs[i].pin; - int index, err, type_idx; - - index = -1; - for (j = 0; j < spec->num_muxes; j++) { - index = get_connection_index(codec, spec->mux_nids[j], - nid); - if (index >= 0) - break; - } - if (index < 0) - continue; - - label = hda_get_autocfg_input_label(codec, cfg, i); - snd_hda_add_imux_item(imux, label, index, &type_idx); - - err = create_elem_capture_vol(codec, nid, - label, type_idx, - HDA_INPUT); - if (err < 0) - return err; - } - spec->num_analog_muxes = imux->num_items; - - if (imux->num_items) { - /* - * Set the current input for the muxes. - * The STAC9221 has two input muxes with identical source - * NID lists. Hopefully this won't get confused. - */ - for (i = 0; i < spec->num_muxes; i++) { - snd_hda_codec_write_cache(codec, spec->mux_nids[i], 0, - AC_VERB_SET_CONNECT_SEL, - imux->items[0].index); - } - } - - return 0; -} - -static void stac92xx_auto_init_multi_out(struct hda_codec *codec) -{ - struct sigmatel_spec *spec = codec->spec; - int i; - - for (i = 0; i < spec->autocfg.line_outs; i++) { - hda_nid_t nid = spec->autocfg.line_out_pins[i]; - stac92xx_auto_set_pinctl(codec, nid, AC_PINCTL_OUT_EN); - } -} - -static void stac92xx_auto_init_hp_out(struct hda_codec *codec) -{ - struct sigmatel_spec *spec = codec->spec; - int i; - - for (i = 0; i < spec->autocfg.hp_outs; i++) { - hda_nid_t pin; - pin = spec->autocfg.hp_pins[i]; - if (pin) /* connect to front */ - stac92xx_auto_set_pinctl(codec, pin, AC_PINCTL_OUT_EN | AC_PINCTL_HP_EN); - } - for (i = 0; i < spec->autocfg.speaker_outs; i++) { - hda_nid_t pin; - pin = spec->autocfg.speaker_pins[i]; - if (pin) /* connect to front */ - stac92xx_auto_set_pinctl(codec, pin, AC_PINCTL_OUT_EN); - } -} - -static int is_dual_headphones(struct hda_codec *codec) -{ - struct sigmatel_spec *spec = codec->spec; - int i, valid_hps; - - if (spec->autocfg.line_out_type != AUTO_PIN_SPEAKER_OUT || - spec->autocfg.hp_outs <= 1) - return 0; - valid_hps = 0; - for (i = 0; i < spec->autocfg.hp_outs; i++) { - hda_nid_t nid = spec->autocfg.hp_pins[i]; - unsigned int cfg = snd_hda_codec_get_pincfg(codec, nid); - if (get_defcfg_location(cfg) & AC_JACK_LOC_SEPARATE) - continue; - valid_hps++; - } - return (valid_hps > 1); -} - - -static int stac92xx_parse_auto_config(struct hda_codec *codec) -{ - struct sigmatel_spec *spec = codec->spec; - hda_nid_t dig_out = 0, dig_in = 0; - int hp_swap = 0; - int i, err; - - if ((err = snd_hda_parse_pin_def_config(codec, - &spec->autocfg, - spec->dmic_nids)) < 0) - return err; - if (! spec->autocfg.line_outs) - return -EINVAL; /* can't find valid pin config */ - - /* If we have no real line-out pin and multiple hp-outs, HPs should - * be set up as multi-channel outputs. - */ - if (is_dual_headphones(codec)) { - /* Copy hp_outs to line_outs, backup line_outs in - * speaker_outs so that the following routines can handle - * HP pins as primary outputs. - */ - snd_printdd("stac92xx: Enabling multi-HPs workaround\n"); - memcpy(spec->autocfg.speaker_pins, spec->autocfg.line_out_pins, - sizeof(spec->autocfg.line_out_pins)); - spec->autocfg.speaker_outs = spec->autocfg.line_outs; - memcpy(spec->autocfg.line_out_pins, spec->autocfg.hp_pins, - sizeof(spec->autocfg.hp_pins)); - spec->autocfg.line_outs = spec->autocfg.hp_outs; - spec->autocfg.line_out_type = AUTO_PIN_HP_OUT; - spec->autocfg.hp_outs = 0; - hp_swap = 1; - } - if (spec->autocfg.mono_out_pin) { - int dir = get_wcaps(codec, spec->autocfg.mono_out_pin) & - (AC_WCAP_OUT_AMP | AC_WCAP_IN_AMP); - u32 caps = query_amp_caps(codec, - spec->autocfg.mono_out_pin, dir); - hda_nid_t conn_list[1]; - - /* get the mixer node and then the mono mux if it exists */ - if (snd_hda_get_connections(codec, - spec->autocfg.mono_out_pin, conn_list, 1) && - snd_hda_get_connections(codec, conn_list[0], - conn_list, 1) > 0) { - - int wcaps = get_wcaps(codec, conn_list[0]); - int wid_type = get_wcaps_type(wcaps); - /* LR swap check, some stac925x have a mux that - * changes the DACs output path instead of the - * mono-mux path. - */ - if (wid_type == AC_WID_AUD_SEL && - !(wcaps & AC_WCAP_LR_SWAP)) - spec->mono_nid = conn_list[0]; - } - if (dir) { - hda_nid_t nid = spec->autocfg.mono_out_pin; - - /* most mono outs have a least a mute/unmute switch */ - dir = (dir & AC_WCAP_OUT_AMP) ? HDA_OUTPUT : HDA_INPUT; - err = stac92xx_add_control(spec, STAC_CTL_WIDGET_MUTE, - "Mono Playback Switch", - HDA_COMPOSE_AMP_VAL(nid, 1, 0, dir)); - if (err < 0) - return err; - /* check for volume support for the amp */ - if ((caps & AC_AMPCAP_NUM_STEPS) - >> AC_AMPCAP_NUM_STEPS_SHIFT) { - err = stac92xx_add_control(spec, - STAC_CTL_WIDGET_VOL, - "Mono Playback Volume", - HDA_COMPOSE_AMP_VAL(nid, 1, 0, dir)); - if (err < 0) - return err; - } - } - - stac92xx_auto_set_pinctl(codec, spec->autocfg.mono_out_pin, - AC_PINCTL_OUT_EN); - } - - if (!spec->multiout.num_dacs) { - err = stac92xx_auto_fill_dac_nids(codec); - if (err < 0) - return err; - err = stac92xx_auto_create_multi_out_ctls(codec, - &spec->autocfg); - if (err < 0) - return err; - } - - /* setup analog beep controls */ - if (spec->anabeep_nid > 0) { - err = stac92xx_auto_create_beep_ctls(codec, - spec->anabeep_nid); - if (err < 0) - return err; - } - - /* setup digital beep controls and input device */ -#ifdef CONFIG_SND_HDA_INPUT_BEEP - if (spec->digbeep_nid > 0) { - hda_nid_t nid = spec->digbeep_nid; - unsigned int caps; - - err = stac92xx_auto_create_beep_ctls(codec, nid); - if (err < 0) - return err; - err = snd_hda_attach_beep_device(codec, nid); - if (err < 0) - return err; - if (codec->beep) { - /* IDT/STAC codecs have linear beep tone parameter */ - codec->beep->linear_tone = spec->linear_tone_beep; - /* if no beep switch is available, make its own one */ - caps = query_amp_caps(codec, nid, HDA_OUTPUT); - if (!(caps & AC_AMPCAP_MUTE)) { - err = stac92xx_beep_switch_ctl(codec); - if (err < 0) - return err; - } - } - } -#endif - - err = stac92xx_auto_create_hp_ctls(codec, &spec->autocfg); - if (err < 0) - return err; - - /* All output parsing done, now restore the swapped hp pins */ - if (hp_swap) { - memcpy(spec->autocfg.hp_pins, spec->autocfg.line_out_pins, - sizeof(spec->autocfg.hp_pins)); - spec->autocfg.hp_outs = spec->autocfg.line_outs; - spec->autocfg.line_out_type = AUTO_PIN_HP_OUT; - spec->autocfg.line_outs = 0; - } - - if (stac_check_auto_mic(codec)) { - spec->auto_mic = 1; - /* only one capture for auto-mic */ - spec->num_adcs = 1; - spec->num_caps = 1; - spec->num_muxes = 1; - } - - for (i = 0; i < spec->num_caps; i++) { - err = stac92xx_add_capvol_ctls(codec, spec->capvols[i], - spec->capsws[i], i); - if (err < 0) - return err; - } - - err = stac92xx_auto_create_analog_input_ctls(codec, &spec->autocfg); - if (err < 0) - return err; - - if (spec->mono_nid > 0) { - err = stac92xx_auto_create_mono_output_ctls(codec); - if (err < 0) - return err; - } - if (spec->num_dmics > 0 && !spec->dinput_mux) - if ((err = stac92xx_auto_create_dmic_input_ctls(codec, - &spec->autocfg)) < 0) - return err; - if (spec->num_muxes > 0) { - err = stac92xx_auto_create_mux_input_ctls(codec); - if (err < 0) - return err; - } - if (spec->num_smuxes > 0) { - err = stac92xx_auto_create_spdif_mux_ctls(codec); - if (err < 0) - return err; - } - - err = stac92xx_add_input_source(spec); - if (err < 0) - return err; - - spec->multiout.max_channels = spec->multiout.num_dacs * 2; - if (spec->multiout.max_channels > 2) - spec->surr_switch = 1; - - /* find digital out and in converters */ - for (i = codec->start_nid; i < codec->start_nid + codec->num_nodes; i++) { - unsigned int wid_caps = get_wcaps(codec, i); - if (wid_caps & AC_WCAP_DIGITAL) { - switch (get_wcaps_type(wid_caps)) { - case AC_WID_AUD_OUT: - if (!dig_out) - dig_out = i; - break; - case AC_WID_AUD_IN: - if (!dig_in) - dig_in = i; - break; - } - } - } - if (spec->autocfg.dig_outs) - spec->multiout.dig_out_nid = dig_out; - if (dig_in && spec->autocfg.dig_in_pin) - spec->dig_in_nid = dig_in; - - if (spec->kctls.list) - spec->mixers[spec->num_mixers++] = spec->kctls.list; - - spec->input_mux = &spec->private_imux; - if (!spec->dinput_mux) - spec->dinput_mux = &spec->private_dimux; - spec->sinput_mux = &spec->private_smux; - spec->mono_mux = &spec->private_mono_mux; - return 0; -} - -/* add playback controls for HP output */ -static int stac9200_auto_create_hp_ctls(struct hda_codec *codec, - struct auto_pin_cfg *cfg) -{ - struct sigmatel_spec *spec = codec->spec; - hda_nid_t pin = cfg->hp_pins[0]; - - if (! pin) - return 0; - - if (is_jack_detectable(codec, pin)) - spec->hp_detect = 1; - - return 0; -} - -/* add playback controls for LFE output */ -static int stac9200_auto_create_lfe_ctls(struct hda_codec *codec, - struct auto_pin_cfg *cfg) -{ - struct sigmatel_spec *spec = codec->spec; - int err; - hda_nid_t lfe_pin = 0x0; - int i; - - /* - * search speaker outs and line outs for a mono speaker pin - * with an amp. If one is found, add LFE controls - * for it. - */ - for (i = 0; i < spec->autocfg.speaker_outs && lfe_pin == 0x0; i++) { - hda_nid_t pin = spec->autocfg.speaker_pins[i]; - unsigned int wcaps = get_wcaps(codec, pin); - wcaps &= (AC_WCAP_STEREO | AC_WCAP_OUT_AMP); - if (wcaps == AC_WCAP_OUT_AMP) - /* found a mono speaker with an amp, must be lfe */ - lfe_pin = pin; - } - - /* if speaker_outs is 0, then speakers may be in line_outs */ - if (lfe_pin == 0 && spec->autocfg.speaker_outs == 0) { - for (i = 0; i < spec->autocfg.line_outs && lfe_pin == 0x0; i++) { - hda_nid_t pin = spec->autocfg.line_out_pins[i]; - unsigned int defcfg; - defcfg = snd_hda_codec_get_pincfg(codec, pin); - if (get_defcfg_device(defcfg) == AC_JACK_SPEAKER) { - unsigned int wcaps = get_wcaps(codec, pin); - wcaps &= (AC_WCAP_STEREO | AC_WCAP_OUT_AMP); - if (wcaps == AC_WCAP_OUT_AMP) - /* found a mono speaker with an amp, - must be lfe */ - lfe_pin = pin; - } - } - } - - if (lfe_pin) { - err = create_controls(codec, "LFE", lfe_pin, 1); - if (err < 0) - return err; - } - - return 0; -} - -static int stac9200_parse_auto_config(struct hda_codec *codec) -{ - struct sigmatel_spec *spec = codec->spec; - int err; - - if ((err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL)) < 0) - return err; - - if ((err = stac92xx_auto_create_analog_input_ctls(codec, &spec->autocfg)) < 0) - return err; - - if ((err = stac9200_auto_create_hp_ctls(codec, &spec->autocfg)) < 0) - return err; - - if ((err = stac9200_auto_create_lfe_ctls(codec, &spec->autocfg)) < 0) - return err; - - if (spec->num_muxes > 0) { - err = stac92xx_auto_create_mux_input_ctls(codec); - if (err < 0) - return err; - } - - err = stac92xx_add_input_source(spec); - if (err < 0) - return err; - - if (spec->autocfg.dig_outs) - spec->multiout.dig_out_nid = 0x05; - if (spec->autocfg.dig_in_pin) - spec->dig_in_nid = 0x04; - - if (spec->kctls.list) - spec->mixers[spec->num_mixers++] = spec->kctls.list; - - spec->input_mux = &spec->private_imux; - spec->dinput_mux = &spec->private_dimux; - - return 0; -} - -/* - * Early 2006 Intel Macintoshes with STAC9220X5 codecs seem to have a - * funky external mute control using GPIO pins. - */ - -static void stac_gpio_set(struct hda_codec *codec, unsigned int mask, - unsigned int dir_mask, unsigned int data) -{ - unsigned int gpiostate, gpiomask, gpiodir; - - snd_printdd("%s msk %x dir %x gpio %x\n", __func__, mask, dir_mask, data); - - gpiostate = snd_hda_codec_read(codec, codec->afg, 0, - AC_VERB_GET_GPIO_DATA, 0); - gpiostate = (gpiostate & ~dir_mask) | (data & dir_mask); - - gpiomask = snd_hda_codec_read(codec, codec->afg, 0, - AC_VERB_GET_GPIO_MASK, 0); - gpiomask |= mask; - - gpiodir = snd_hda_codec_read(codec, codec->afg, 0, - AC_VERB_GET_GPIO_DIRECTION, 0); - gpiodir |= dir_mask; - - /* Configure GPIOx as CMOS */ - snd_hda_codec_write(codec, codec->afg, 0, 0x7e7, 0); - - snd_hda_codec_write(codec, codec->afg, 0, - AC_VERB_SET_GPIO_MASK, gpiomask); - snd_hda_codec_read(codec, codec->afg, 0, - AC_VERB_SET_GPIO_DIRECTION, gpiodir); /* sync */ - - msleep(1); - - snd_hda_codec_read(codec, codec->afg, 0, - AC_VERB_SET_GPIO_DATA, gpiostate); /* sync */ -} - -static int stac_add_event(struct hda_codec *codec, hda_nid_t nid, - unsigned char type, int data) -{ - struct hda_jack_tbl *event; - - event = snd_hda_jack_tbl_new(codec, nid); - if (!event) - return -ENOMEM; - event->action = type; - event->private_data = data; - - return 0; -} - -static void handle_unsol_event(struct hda_codec *codec, - struct hda_jack_tbl *event); - -/* check if given nid is a valid pin and no other events are assigned - * to it. If OK, assign the event, set the unsol flag, and returns 1. - * Otherwise, returns zero. - */ -static int enable_pin_detect(struct hda_codec *codec, hda_nid_t nid, - unsigned int type) -{ - struct hda_jack_tbl *event; - - if (!is_jack_detectable(codec, nid)) - return 0; - event = snd_hda_jack_tbl_new(codec, nid); - if (!event) - return -ENOMEM; - if (event->action && event->action != type) - return 0; - event->action = type; - event->callback = handle_unsol_event; - snd_hda_jack_detect_enable(codec, nid, 0); - return 1; -} - -static int is_nid_out_jack_pin(struct auto_pin_cfg *cfg, hda_nid_t nid) -{ - int i; - for (i = 0; i < cfg->hp_outs; i++) - if (cfg->hp_pins[i] == nid) - return 1; /* nid is a HP-Out */ - for (i = 0; i < cfg->line_outs; i++) - if (cfg->line_out_pins[i] == nid) - return 1; /* nid is a line-Out */ - return 0; /* nid is not a HP-Out */ -}; - -static void stac92xx_power_down(struct hda_codec *codec) -{ - struct sigmatel_spec *spec = codec->spec; - - /* power down inactive DACs */ - const hda_nid_t *dac; - for (dac = spec->dac_list; *dac; dac++) - if (!check_all_dac_nids(spec, *dac)) - snd_hda_codec_write(codec, *dac, 0, - AC_VERB_SET_POWER_STATE, AC_PWRST_D3); -} - -static void stac_toggle_power_map(struct hda_codec *codec, hda_nid_t nid, - int enable); - -static inline bool get_int_hint(struct hda_codec *codec, const char *key, - int *valp) -{ - return !snd_hda_get_int_hint(codec, key, valp); -} - -/* override some hints from the hwdep entry */ -static void stac_store_hints(struct hda_codec *codec) -{ - struct sigmatel_spec *spec = codec->spec; - int val; - - val = snd_hda_get_bool_hint(codec, "hp_detect"); - if (val >= 0) - spec->hp_detect = val; - if (get_int_hint(codec, "gpio_mask", &spec->gpio_mask)) { - spec->eapd_mask = spec->gpio_dir = spec->gpio_data = - spec->gpio_mask; - } - if (get_int_hint(codec, "gpio_dir", &spec->gpio_dir)) - spec->gpio_mask &= spec->gpio_mask; - if (get_int_hint(codec, "gpio_data", &spec->gpio_data)) - spec->gpio_dir &= spec->gpio_mask; - if (get_int_hint(codec, "eapd_mask", &spec->eapd_mask)) - spec->eapd_mask &= spec->gpio_mask; - if (get_int_hint(codec, "gpio_mute", &spec->gpio_mute)) - spec->gpio_mute &= spec->gpio_mask; - val = snd_hda_get_bool_hint(codec, "eapd_switch"); - if (val >= 0) - spec->eapd_switch = val; -} - -static void stac_issue_unsol_events(struct hda_codec *codec, int num_pins, - const hda_nid_t *pins) -{ - while (num_pins--) - stac_issue_unsol_event(codec, *pins++); -} - -/* fake event to set up pins */ -static void stac_fake_hp_events(struct hda_codec *codec) -{ - struct sigmatel_spec *spec = codec->spec; - - if (spec->autocfg.hp_outs) - stac_issue_unsol_events(codec, spec->autocfg.hp_outs, - spec->autocfg.hp_pins); - if (spec->autocfg.line_outs && - spec->autocfg.line_out_pins[0] != spec->autocfg.hp_pins[0]) - stac_issue_unsol_events(codec, spec->autocfg.line_outs, - spec->autocfg.line_out_pins); -} - -static int stac92xx_init(struct hda_codec *codec) -{ - struct sigmatel_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - unsigned int gpio; - int i; - - if (spec->init) - snd_hda_sequence_write(codec, spec->init); - - snd_hda_apply_verbs(codec); - - /* power down adcs initially */ - if (spec->powerdown_adcs) - for (i = 0; i < spec->num_adcs; i++) - snd_hda_codec_write(codec, - spec->adc_nids[i], 0, - AC_VERB_SET_POWER_STATE, AC_PWRST_D3); - - /* override some hints */ - stac_store_hints(codec); - - /* set up GPIO */ - gpio = spec->gpio_data; - /* turn on EAPD statically when spec->eapd_switch isn't set. - * otherwise, unsol event will turn it on/off dynamically - */ - if (!spec->eapd_switch) - gpio |= spec->eapd_mask; - stac_gpio_set(codec, spec->gpio_mask, spec->gpio_dir, gpio); - - /* set up pins */ - if (spec->hp_detect) { - /* Enable unsolicited responses on the HP widget */ - for (i = 0; i < cfg->hp_outs; i++) { - hda_nid_t nid = cfg->hp_pins[i]; - enable_pin_detect(codec, nid, STAC_HP_EVENT); - } - if (cfg->line_out_type == AUTO_PIN_LINE_OUT && - cfg->speaker_outs > 0) { - /* enable pin-detect for line-outs as well */ - for (i = 0; i < cfg->line_outs; i++) { - hda_nid_t nid = cfg->line_out_pins[i]; - enable_pin_detect(codec, nid, STAC_LO_EVENT); - } - } - - /* force to enable the first line-out; the others are set up - * in unsol_event - */ - stac92xx_auto_set_pinctl(codec, spec->autocfg.line_out_pins[0], - AC_PINCTL_OUT_EN); - /* fake event to set up pins */ - stac_fake_hp_events(codec); - } else { - stac92xx_auto_init_multi_out(codec); - stac92xx_auto_init_hp_out(codec); - for (i = 0; i < cfg->hp_outs; i++) - stac_toggle_power_map(codec, cfg->hp_pins[i], 1); - } - if (spec->auto_mic) { - /* initialize connection to analog input */ - if (spec->dmux_nids) - snd_hda_codec_write_cache(codec, spec->dmux_nids[0], 0, - AC_VERB_SET_CONNECT_SEL, 0); - if (enable_pin_detect(codec, spec->ext_mic.pin, STAC_MIC_EVENT)) - stac_issue_unsol_event(codec, spec->ext_mic.pin); - if (enable_pin_detect(codec, spec->dock_mic.pin, - STAC_MIC_EVENT)) - stac_issue_unsol_event(codec, spec->dock_mic.pin); - } - for (i = 0; i < cfg->num_inputs; i++) { - hda_nid_t nid = cfg->inputs[i].pin; - int type = cfg->inputs[i].type; - unsigned int pinctl, conf; - if (type == AUTO_PIN_MIC) { - /* for mic pins, force to initialize */ - pinctl = snd_hda_get_default_vref(codec, nid); - pinctl |= AC_PINCTL_IN_EN; - stac92xx_auto_set_pinctl(codec, nid, pinctl); - } else { - pinctl = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_PIN_WIDGET_CONTROL, 0); - /* if PINCTL already set then skip */ - /* Also, if both INPUT and OUTPUT are set, - * it must be a BIOS bug; need to override, too - */ - if (!(pinctl & AC_PINCTL_IN_EN) || - (pinctl & AC_PINCTL_OUT_EN)) { - pinctl &= ~AC_PINCTL_OUT_EN; - pinctl |= AC_PINCTL_IN_EN; - stac92xx_auto_set_pinctl(codec, nid, pinctl); - } - } - conf = snd_hda_codec_get_pincfg(codec, nid); - if (get_defcfg_connect(conf) != AC_JACK_PORT_FIXED) { - if (enable_pin_detect(codec, nid, STAC_INSERT_EVENT)) - stac_issue_unsol_event(codec, nid); - } - } - for (i = 0; i < spec->num_dmics; i++) - stac92xx_auto_set_pinctl(codec, spec->dmic_nids[i], - AC_PINCTL_IN_EN); - if (cfg->dig_out_pins[0]) - stac92xx_auto_set_pinctl(codec, cfg->dig_out_pins[0], - AC_PINCTL_OUT_EN); - if (cfg->dig_in_pin) - stac92xx_auto_set_pinctl(codec, cfg->dig_in_pin, - AC_PINCTL_IN_EN); - for (i = 0; i < spec->num_pwrs; i++) { - hda_nid_t nid = spec->pwr_nids[i]; - unsigned int pinctl, def_conf; - - def_conf = snd_hda_codec_get_pincfg(codec, nid); - def_conf = get_defcfg_connect(def_conf); - if (def_conf == AC_JACK_PORT_NONE) { - /* power off unused ports */ - stac_toggle_power_map(codec, nid, 0); - continue; - } - if (def_conf == AC_JACK_PORT_FIXED) { - /* no need for jack detection for fixed pins */ - stac_toggle_power_map(codec, nid, 1); - continue; - } - /* power on when no jack detection is available */ - /* or when the VREF is used for controlling LED */ - if (!spec->hp_detect || - spec->vref_mute_led_nid == nid || - !is_jack_detectable(codec, nid)) { - stac_toggle_power_map(codec, nid, 1); - continue; - } - - if (is_nid_out_jack_pin(cfg, nid)) - continue; /* already has an unsol event */ - - pinctl = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_PIN_WIDGET_CONTROL, 0); - /* outputs are only ports capable of power management - * any attempts on powering down a input port cause the - * referenced VREF to act quirky. - */ - if (pinctl & AC_PINCTL_IN_EN) { - stac_toggle_power_map(codec, nid, 1); - continue; - } - if (enable_pin_detect(codec, nid, STAC_PWR_EVENT)) { - stac_issue_unsol_event(codec, nid); - continue; - } - /* none of the above, turn the port OFF */ - stac_toggle_power_map(codec, nid, 0); - } - - /* sync mute LED */ - if (spec->gpio_led) { - if (spec->vmaster_mute.hook) - snd_hda_sync_vmaster_hook(&spec->vmaster_mute); - else /* the very first init call doesn't have vmaster yet */ - stac92xx_update_led_status(codec, false); - } - - /* sync the power-map */ - if (spec->num_pwrs) - snd_hda_codec_write(codec, codec->afg, 0, - AC_VERB_IDT_SET_POWER_MAP, - spec->power_map_bits); - if (spec->dac_list) - stac92xx_power_down(codec); - return 0; -} - -static void stac92xx_free_kctls(struct hda_codec *codec) -{ - struct sigmatel_spec *spec = codec->spec; - - if (spec->kctls.list) { - struct snd_kcontrol_new *kctl = spec->kctls.list; - int i; - for (i = 0; i < spec->kctls.used; i++) - kfree(kctl[i].name); - } - snd_array_free(&spec->kctls); -} - -static void stac92xx_shutup_pins(struct hda_codec *codec) -{ - unsigned int i, def_conf; - - if (codec->bus->shutdown) - return; - for (i = 0; i < codec->init_pins.used; i++) { - struct hda_pincfg *pin = snd_array_elem(&codec->init_pins, i); - def_conf = snd_hda_codec_get_pincfg(codec, pin->nid); - if (get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE) - snd_hda_set_pin_ctl(codec, pin->nid, 0); - } -} - -static void stac92xx_shutup(struct hda_codec *codec) -{ - struct sigmatel_spec *spec = codec->spec; - - stac92xx_shutup_pins(codec); - - if (spec->eapd_mask) - stac_gpio_set(codec, spec->gpio_mask, - spec->gpio_dir, spec->gpio_data & - ~spec->eapd_mask); -} - -static void stac92xx_free(struct hda_codec *codec) -{ - struct sigmatel_spec *spec = codec->spec; - - if (! spec) - return; - - kfree(spec); - snd_hda_detach_beep_device(codec); -} - -static void stac92xx_set_pinctl(struct hda_codec *codec, hda_nid_t nid, - unsigned int flag) -{ - unsigned int old_ctl, pin_ctl; - - pin_ctl = snd_hda_codec_read(codec, nid, - 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00); - - if (pin_ctl & AC_PINCTL_IN_EN) { - /* - * we need to check the current set-up direction of - * shared input pins since they can be switched via - * "xxx as Output" mixer switch - */ - struct sigmatel_spec *spec = codec->spec; - if (nid == spec->line_switch || nid == spec->mic_switch) - return; - } - - old_ctl = pin_ctl; - /* if setting pin direction bits, clear the current - direction bits first */ - if (flag & (AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN)) - pin_ctl &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN); - - pin_ctl |= flag; - if (old_ctl != pin_ctl) - snd_hda_set_pin_ctl_cache(codec, nid, pin_ctl); -} - -static void stac92xx_reset_pinctl(struct hda_codec *codec, hda_nid_t nid, - unsigned int flag) -{ - unsigned int pin_ctl = snd_hda_codec_read(codec, nid, - 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00); - if (pin_ctl & flag) - snd_hda_set_pin_ctl_cache(codec, nid, pin_ctl & ~flag); -} - -static inline int get_pin_presence(struct hda_codec *codec, hda_nid_t nid) -{ - if (!nid) - return 0; - return snd_hda_jack_detect(codec, nid); -} - -static void stac92xx_line_out_detect(struct hda_codec *codec, - int presence) -{ - struct sigmatel_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - int i; - - if (cfg->speaker_outs == 0) - return; - - for (i = 0; i < cfg->line_outs; i++) { - if (presence) - break; - presence = get_pin_presence(codec, cfg->line_out_pins[i]); - if (presence) { - unsigned int pinctl; - pinctl = snd_hda_codec_read(codec, - cfg->line_out_pins[i], 0, - AC_VERB_GET_PIN_WIDGET_CONTROL, 0); - if (pinctl & AC_PINCTL_IN_EN) - presence = 0; /* mic- or line-input */ - } - } - - if (presence) { - /* disable speakers */ - for (i = 0; i < cfg->speaker_outs; i++) - stac92xx_reset_pinctl(codec, cfg->speaker_pins[i], - AC_PINCTL_OUT_EN); - if (spec->eapd_mask && spec->eapd_switch) - stac_gpio_set(codec, spec->gpio_mask, - spec->gpio_dir, spec->gpio_data & - ~spec->eapd_mask); - } else { - /* enable speakers */ - for (i = 0; i < cfg->speaker_outs; i++) - stac92xx_set_pinctl(codec, cfg->speaker_pins[i], - AC_PINCTL_OUT_EN); - if (spec->eapd_mask && spec->eapd_switch) - stac_gpio_set(codec, spec->gpio_mask, - spec->gpio_dir, spec->gpio_data | - spec->eapd_mask); - } -} - -/* return non-zero if the hp-pin of the given array index isn't - * a jack-detection target - */ -static int no_hp_sensing(struct sigmatel_spec *spec, int i) -{ - struct auto_pin_cfg *cfg = &spec->autocfg; - - /* ignore sensing of shared line and mic jacks */ - if (cfg->hp_pins[i] == spec->line_switch) - return 1; - if (cfg->hp_pins[i] == spec->mic_switch) - return 1; - /* ignore if the pin is set as line-out */ - if (cfg->hp_pins[i] == spec->hp_switch) - return 1; - return 0; -} - -static void stac92xx_hp_detect(struct hda_codec *codec) -{ - struct sigmatel_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - int i, presence; - - presence = 0; - if (spec->gpio_mute) - presence = !(snd_hda_codec_read(codec, codec->afg, 0, - AC_VERB_GET_GPIO_DATA, 0) & spec->gpio_mute); - - for (i = 0; i < cfg->hp_outs; i++) { - if (presence) - break; - if (no_hp_sensing(spec, i)) - continue; - presence = get_pin_presence(codec, cfg->hp_pins[i]); - if (presence) { - unsigned int pinctl; - pinctl = snd_hda_codec_read(codec, cfg->hp_pins[i], 0, - AC_VERB_GET_PIN_WIDGET_CONTROL, 0); - if (pinctl & AC_PINCTL_IN_EN) - presence = 0; /* mic- or line-input */ - } - } - - if (presence) { - /* disable lineouts */ - if (spec->hp_switch) - stac92xx_reset_pinctl(codec, spec->hp_switch, - AC_PINCTL_OUT_EN); - for (i = 0; i < cfg->line_outs; i++) - stac92xx_reset_pinctl(codec, cfg->line_out_pins[i], - AC_PINCTL_OUT_EN); - } else { - /* enable lineouts */ - if (spec->hp_switch) - stac92xx_set_pinctl(codec, spec->hp_switch, - AC_PINCTL_OUT_EN); - for (i = 0; i < cfg->line_outs; i++) - stac92xx_set_pinctl(codec, cfg->line_out_pins[i], - AC_PINCTL_OUT_EN); - } - stac92xx_line_out_detect(codec, presence); - /* toggle hp outs */ - for (i = 0; i < cfg->hp_outs; i++) { - unsigned int val = AC_PINCTL_OUT_EN | AC_PINCTL_HP_EN; - if (no_hp_sensing(spec, i)) - continue; - if (1 /*presence*/) - stac92xx_set_pinctl(codec, cfg->hp_pins[i], val); -#if 0 /* FIXME */ -/* Resetting the pinctl like below may lead to (a sort of) regressions - * on some devices since they use the HP pin actually for line/speaker - * outs although the default pin config shows a different pin (that is - * wrong and useless). - * - * So, it's basically a problem of default pin configs, likely a BIOS issue. - * But, disabling the code below just works around it, and I'm too tired of - * bug reports with such devices... - */ - else - stac92xx_reset_pinctl(codec, cfg->hp_pins[i], val); -#endif /* FIXME */ - } -} - -static void stac_toggle_power_map(struct hda_codec *codec, hda_nid_t nid, - int enable) -{ - struct sigmatel_spec *spec = codec->spec; - unsigned int idx, val; - - for (idx = 0; idx < spec->num_pwrs; idx++) { - if (spec->pwr_nids[idx] == nid) - break; - } - if (idx >= spec->num_pwrs) - return; - - idx = 1 << idx; - - val = spec->power_map_bits; - if (enable) - val &= ~idx; - else - val |= idx; - - /* power down unused output ports */ - if (val != spec->power_map_bits) { - spec->power_map_bits = val; - snd_hda_codec_write(codec, codec->afg, 0, - AC_VERB_IDT_SET_POWER_MAP, val); - } -} - -static void stac92xx_pin_sense(struct hda_codec *codec, hda_nid_t nid) -{ - stac_toggle_power_map(codec, nid, get_pin_presence(codec, nid)); -} + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x021c, + "Dell Precision", STAC_9205_DELL_M43), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x021f, + "Dell Inspiron", STAC_9205_DELL_M44), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0228, + "Dell Vostro 1500", STAC_9205_DELL_M42), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0229, + "Dell Vostro 1700", STAC_9205_DELL_M42), + /* Gateway */ + SND_PCI_QUIRK(0x107b, 0x0560, "Gateway T6834c", STAC_9205_EAPD), + SND_PCI_QUIRK(0x107b, 0x0565, "Gateway T1616", STAC_9205_EAPD), + {} /* terminator */ +}; -/* get the pin connection (fixed, none, etc) */ -static unsigned int stac_get_defcfg_connect(struct hda_codec *codec, int idx) +static int stac_parse_auto_config(struct hda_codec *codec) { struct sigmatel_spec *spec = codec->spec; - unsigned int cfg; + int err; - cfg = snd_hda_codec_get_pincfg(codec, spec->pin_nids[idx]); - return get_defcfg_connect(cfg); -} + err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL, 0); + if (err < 0) + return err; -static int stac92xx_connected_ports(struct hda_codec *codec, - const hda_nid_t *nids, int num_nids) -{ - struct sigmatel_spec *spec = codec->spec; - int idx, num; - unsigned int def_conf; - - for (num = 0; num < num_nids; num++) { - for (idx = 0; idx < spec->num_pins; idx++) - if (spec->pin_nids[idx] == nids[num]) - break; - if (idx >= spec->num_pins) - break; - def_conf = stac_get_defcfg_connect(codec, idx); - if (def_conf == AC_JACK_PORT_NONE) - break; - } - return num; -} + /* add hooks */ + spec->gen.pcm_playback_hook = stac_playback_pcm_hook; + spec->gen.pcm_capture_hook = stac_capture_pcm_hook; -static void stac92xx_mic_detect(struct hda_codec *codec) -{ - struct sigmatel_spec *spec = codec->spec; - struct sigmatel_mic_route *mic; + spec->gen.automute_hook = stac_update_outputs; + spec->gen.hp_automute_hook = stac_hp_automute; + spec->gen.line_automute_hook = stac_line_automute; - if (get_pin_presence(codec, spec->ext_mic.pin)) - mic = &spec->ext_mic; - else if (get_pin_presence(codec, spec->dock_mic.pin)) - mic = &spec->dock_mic; - else - mic = &spec->int_mic; - if (mic->dmux_idx >= 0) - snd_hda_codec_write_cache(codec, spec->dmux_nids[0], 0, - AC_VERB_SET_CONNECT_SEL, - mic->dmux_idx); - if (mic->mux_idx >= 0) - snd_hda_codec_write_cache(codec, spec->mux_nids[0], 0, - AC_VERB_SET_CONNECT_SEL, - mic->mux_idx); -} + err = snd_hda_gen_parse_auto_config(codec, &spec->gen.autocfg); + if (err < 0) + return err; -static void handle_unsol_event(struct hda_codec *codec, - struct hda_jack_tbl *event) -{ - struct sigmatel_spec *spec = codec->spec; - int data; + /* minimum value is actually mute */ + spec->gen.vmaster_tlv[3] |= TLV_DB_SCALE_MUTE; - switch (event->action) { - case STAC_HP_EVENT: - case STAC_LO_EVENT: - stac92xx_hp_detect(codec); - break; - case STAC_MIC_EVENT: - stac92xx_mic_detect(codec); - break; + /* setup analog beep controls */ + if (spec->anabeep_nid > 0) { + err = stac_auto_create_beep_ctls(codec, + spec->anabeep_nid); + if (err < 0) + return err; } - switch (event->action) { - case STAC_HP_EVENT: - case STAC_LO_EVENT: - case STAC_MIC_EVENT: - case STAC_INSERT_EVENT: - case STAC_PWR_EVENT: - if (spec->num_pwrs > 0) - stac92xx_pin_sense(codec, event->nid); - - switch (codec->subsystem_id) { - case 0x103c308f: - if (event->nid == 0xb) { - int pin = AC_PINCTL_IN_EN; - - if (get_pin_presence(codec, 0xa) - && get_pin_presence(codec, 0xb)) - pin |= AC_PINCTL_VREF_80; - if (!get_pin_presence(codec, 0xb)) - pin |= AC_PINCTL_VREF_80; - - /* toggle VREF state based on mic + hp pin - * status - */ - stac92xx_auto_set_pinctl(codec, 0x0a, pin); + /* setup digital beep controls and input device */ +#ifdef CONFIG_SND_HDA_INPUT_BEEP + if (spec->digbeep_nid > 0) { + hda_nid_t nid = spec->digbeep_nid; + unsigned int caps; + + err = stac_auto_create_beep_ctls(codec, nid); + if (err < 0) + return err; + err = snd_hda_attach_beep_device(codec, nid); + if (err < 0) + return err; + if (codec->beep) { + /* IDT/STAC codecs have linear beep tone parameter */ + codec->beep->linear_tone = spec->linear_tone_beep; + /* if no beep switch is available, make its own one */ + caps = query_amp_caps(codec, nid, HDA_OUTPUT); + if (!(caps & AC_AMPCAP_MUTE)) { + err = stac_beep_switch_ctl(codec); + if (err < 0) + return err; } } - break; - case STAC_VREF_EVENT: - data = snd_hda_codec_read(codec, codec->afg, 0, - AC_VERB_GET_GPIO_DATA, 0); - /* toggle VREF state based on GPIOx status */ - snd_hda_codec_write(codec, codec->afg, 0, 0x7e0, - !!(data & (1 << event->private_data))); - break; } -} +#endif -static void stac_issue_unsol_event(struct hda_codec *codec, hda_nid_t nid) -{ - struct hda_jack_tbl *event = snd_hda_jack_tbl_get(codec, nid); - if (!event) - return; - handle_unsol_event(codec, event); + if (spec->gpio_led) + spec->gen.vmaster_mute.hook = stac_vmaster_hook; + + if (spec->aloopback_ctl && + snd_hda_get_bool_hint(codec, "loopback") == 1) { + if (!snd_hda_gen_add_kctl(&spec->gen, NULL, spec->aloopback_ctl)) + return -ENOMEM; + } + + stac_init_power_map(codec); + + return 0; } -static void set_hp_led_gpio(struct hda_codec *codec) + +static int stac_init(struct hda_codec *codec) { struct sigmatel_spec *spec = codec->spec; unsigned int gpio; + int i; - if (spec->gpio_led) - return; + /* override some hints */ + stac_store_hints(codec); - gpio = snd_hda_param_read(codec, codec->afg, AC_PAR_GPIO_CAP); - gpio &= AC_GPIO_IO_COUNT; - if (gpio > 3) - spec->gpio_led = 0x08; /* GPIO 3 */ - else - spec->gpio_led = 0x01; /* GPIO 0 */ -} + /* set up GPIO */ + gpio = spec->gpio_data; + /* turn on EAPD statically when spec->eapd_switch isn't set. + * otherwise, unsol event will turn it on/off dynamically + */ + if (!spec->eapd_switch) + gpio |= spec->eapd_mask; + stac_gpio_set(codec, spec->gpio_mask, spec->gpio_dir, gpio); -/* - * This method searches for the mute LED GPIO configuration - * provided as OEM string in SMBIOS. The format of that string - * is HP_Mute_LED_P_G or HP_Mute_LED_P - * where P can be 0 or 1 and defines mute LED GPIO control state (low/high) - * that corresponds to the NOT muted state of the master volume - * and G is the index of the GPIO to use as the mute LED control (0..9) - * If _G portion is missing it is assigned based on the codec ID - * - * So, HP B-series like systems may have HP_Mute_LED_0 (current models) - * or HP_Mute_LED_0_3 (future models) OEM SMBIOS strings - * - * - * The dv-series laptops don't seem to have the HP_Mute_LED* strings in - * SMBIOS - at least the ones I have seen do not have them - which include - * my own system (HP Pavilion dv6-1110ax) and my cousin's - * HP Pavilion dv9500t CTO. - * Need more information on whether it is true across the entire series. - * -- kunal - */ -static int find_mute_led_cfg(struct hda_codec *codec, int default_polarity) -{ - struct sigmatel_spec *spec = codec->spec; - const struct dmi_device *dev = NULL; + snd_hda_gen_init(codec); - if (get_int_hint(codec, "gpio_led", &spec->gpio_led)) { - get_int_hint(codec, "gpio_led_polarity", - &spec->gpio_led_polarity); - return 1; - } + /* sync the power-map */ + if (spec->num_pwrs) + snd_hda_codec_write(codec, codec->afg, 0, + AC_VERB_IDT_SET_POWER_MAP, + spec->power_map_bits); - while ((dev = dmi_find_device(DMI_DEV_TYPE_OEM_STRING, NULL, dev))) { - if (sscanf(dev->name, "HP_Mute_LED_%d_%x", - &spec->gpio_led_polarity, - &spec->gpio_led) == 2) { - unsigned int max_gpio; - max_gpio = snd_hda_param_read(codec, codec->afg, - AC_PAR_GPIO_CAP); - max_gpio &= AC_GPIO_IO_COUNT; - if (spec->gpio_led < max_gpio) - spec->gpio_led = 1 << spec->gpio_led; - else - spec->vref_mute_led_nid = spec->gpio_led; - return 1; - } - if (sscanf(dev->name, "HP_Mute_LED_%d", - &spec->gpio_led_polarity) == 1) { - set_hp_led_gpio(codec); - return 1; - } - /* BIOS bug: unfilled OEM string */ - if (strstr(dev->name, "HP_Mute_LED_P_G")) { - set_hp_led_gpio(codec); - if (default_polarity >= 0) - spec->gpio_led_polarity = default_polarity; - else - spec->gpio_led_polarity = 1; - return 1; + /* power down inactive ADCs */ + if (spec->powerdown_adcs) { + for (i = 0; i < spec->gen.num_all_adcs; i++) { + if (spec->active_adcs & (1 << i)) + continue; + snd_hda_codec_write(codec, spec->gen.all_adcs[i], 0, + AC_VERB_SET_POWER_STATE, + AC_PWRST_D3); } } - /* - * Fallback case - if we don't find the DMI strings, - * we statically set the GPIO - if not a B-series system - * and default polarity is provided - */ - if (!hp_blike_system(codec->subsystem_id) && - (default_polarity == 0 || default_polarity == 1)) { - set_hp_led_gpio(codec); - spec->gpio_led_polarity = default_polarity; - return 1; + /* power down unused DACs */ + for (i = 0; i < spec->gen.num_all_dacs; i++) { + if (!snd_hda_get_nid_path(codec, spec->gen.all_dacs[i], 0)) + snd_hda_codec_write(codec, spec->gen.all_dacs[i], 0, + AC_VERB_SET_POWER_STATE, + AC_PWRST_D3); } + return 0; } -static int hp_blike_system(u32 subsystem_id) +static void stac_shutup(struct hda_codec *codec) { - switch (subsystem_id) { - case 0x103c1520: - case 0x103c1521: - case 0x103c1523: - case 0x103c1524: - case 0x103c1525: - case 0x103c1722: - case 0x103c1723: - case 0x103c1724: - case 0x103c1725: - case 0x103c1726: - case 0x103c1727: - case 0x103c1728: - case 0x103c1729: - case 0x103c172a: - case 0x103c172b: - case 0x103c307e: - case 0x103c307f: - case 0x103c3080: - case 0x103c3081: - case 0x103c7007: - case 0x103c7008: - return 1; - } - return 0; + struct sigmatel_spec *spec = codec->spec; + + snd_hda_shutup_pins(codec); + + if (spec->eapd_mask) + stac_gpio_set(codec, spec->gpio_mask, + spec->gpio_dir, spec->gpio_data & + ~spec->eapd_mask); +} + +static void stac_free(struct hda_codec *codec) +{ + struct sigmatel_spec *spec = codec->spec; + + if (!spec) + return; + + snd_hda_gen_spec_free(&spec->gen); + kfree(spec); + snd_hda_detach_beep_device(codec); } #ifdef CONFIG_PROC_FS @@ -6341,101 +3585,69 @@ static void stac927x_proc_hook(struct snd_info_buffer *buffer, #endif #ifdef CONFIG_PM -static int stac92xx_resume(struct hda_codec *codec) +static int stac_resume(struct hda_codec *codec) { - stac92xx_init(codec); + codec->patch_ops.init(codec); snd_hda_codec_resume_amp(codec); snd_hda_codec_resume_cache(codec); - /* fake event to set up pins again to override cached values */ - stac_fake_hp_events(codec); return 0; } -static int stac92xx_suspend(struct hda_codec *codec) -{ - stac92xx_shutup(codec); - return 0; -} - -static void stac92xx_set_power_state(struct hda_codec *codec, hda_nid_t fg, - unsigned int power_state) -{ - unsigned int afg_power_state = power_state; - struct sigmatel_spec *spec = codec->spec; - - if (power_state == AC_PWRST_D3) { - if (spec->vref_mute_led_nid) { - /* with vref-out pin used for mute led control - * codec AFG is prevented from D3 state - */ - afg_power_state = AC_PWRST_D1; - } - /* this delay seems necessary to avoid click noise at power-down */ - msleep(100); - } - snd_hda_codec_read(codec, fg, 0, AC_VERB_SET_POWER_STATE, - afg_power_state); - snd_hda_codec_set_power_to_all(codec, fg, power_state, true); -} -#else -#define stac92xx_suspend NULL -#define stac92xx_resume NULL -#define stac92xx_set_power_state NULL -#endif /* CONFIG_PM */ - -/* update mute-LED accoring to the master switch */ -static void stac92xx_update_led_status(struct hda_codec *codec, int enabled) -{ - struct sigmatel_spec *spec = codec->spec; - int muted = !enabled; - - if (!spec->gpio_led) - return; - - /* LED state is inverted on these systems */ - if (spec->gpio_led_polarity) - muted = !muted; - - if (!spec->vref_mute_led_nid) { - if (muted) - spec->gpio_data |= spec->gpio_led; - else - spec->gpio_data &= ~spec->gpio_led; - stac_gpio_set(codec, spec->gpio_mask, - spec->gpio_dir, spec->gpio_data); - } else { - spec->vref_led = muted ? AC_PINCTL_VREF_50 : AC_PINCTL_VREF_GRD; - stac_vrefout_set(codec, spec->vref_mute_led_nid, - spec->vref_led); +static int stac_suspend(struct hda_codec *codec) +{ + stac_shutup(codec); + return 0; +} + +static void stac_set_power_state(struct hda_codec *codec, hda_nid_t fg, + unsigned int power_state) +{ + unsigned int afg_power_state = power_state; + struct sigmatel_spec *spec = codec->spec; + + if (power_state == AC_PWRST_D3) { + if (spec->vref_mute_led_nid) { + /* with vref-out pin used for mute led control + * codec AFG is prevented from D3 state + */ + afg_power_state = AC_PWRST_D1; + } + /* this delay seems necessary to avoid click noise at power-down */ + msleep(100); } + snd_hda_codec_read(codec, fg, 0, AC_VERB_SET_POWER_STATE, + afg_power_state); + snd_hda_codec_set_power_to_all(codec, fg, power_state, true); } +#else +#define stac_suspend NULL +#define stac_resume NULL +#define stac_set_power_state NULL +#endif /* CONFIG_PM */ -static const struct hda_codec_ops stac92xx_patch_ops = { - .build_controls = stac92xx_build_controls, - .build_pcms = stac92xx_build_pcms, - .init = stac92xx_init, - .free = stac92xx_free, +static const struct hda_codec_ops stac_patch_ops = { + .build_controls = snd_hda_gen_build_controls, + .build_pcms = snd_hda_gen_build_pcms, + .init = stac_init, + .free = stac_free, .unsol_event = snd_hda_jack_unsol_event, #ifdef CONFIG_PM - .suspend = stac92xx_suspend, - .resume = stac92xx_resume, + .suspend = stac_suspend, + .resume = stac_resume, #endif - .reboot_notify = stac92xx_shutup, + .reboot_notify = stac_shutup, }; -static int alloc_stac_spec(struct hda_codec *codec, int num_pins, - const hda_nid_t *pin_nids) +static int alloc_stac_spec(struct hda_codec *codec) { struct sigmatel_spec *spec; spec = kzalloc(sizeof(*spec), GFP_KERNEL); if (!spec) return -ENOMEM; + snd_hda_gen_spec_init(&spec->gen); codec->spec = spec; codec->no_trigger_sense = 1; /* seems common with STAC/IDT codecs */ - spec->num_pins = num_pins; - spec->pin_nids = pin_nids; - snd_array_init(&spec->kctls, sizeof(struct snd_kcontrol_new), 32); return 0; } @@ -6444,40 +3656,28 @@ static int patch_stac9200(struct hda_codec *codec) struct sigmatel_spec *spec; int err; - err = alloc_stac_spec(codec, ARRAY_SIZE(stac9200_pin_nids), - stac9200_pin_nids); + err = alloc_stac_spec(codec); if (err < 0) return err; spec = codec->spec; spec->linear_tone_beep = 1; + spec->gen.own_eapd_ctl = 1; - snd_hda_pick_fixup(codec, stac9200_models, stac9200_fixup_tbl, - stac9200_fixups); + codec->patch_ops = stac_patch_ops; - spec->multiout.max_channels = 2; - spec->multiout.num_dacs = 1; - spec->multiout.dac_nids = stac9200_dac_nids; - spec->adc_nids = stac9200_adc_nids; - spec->mux_nids = stac9200_mux_nids; - spec->num_muxes = 1; - spec->num_dmics = 0; - spec->num_adcs = 1; - spec->num_pwrs = 0; snd_hda_add_verbs(codec, stac9200_eapd_init); - spec->mixer = stac9200_mixer; - + snd_hda_pick_fixup(codec, stac9200_models, stac9200_fixup_tbl, + stac9200_fixups); snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); - err = stac9200_parse_auto_config(codec); + err = stac_parse_auto_config(codec); if (err < 0) { - stac92xx_free(codec); + stac_free(codec); return err; } - codec->patch_ops = stac92xx_patch_ops; - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); return 0; @@ -6488,56 +3688,28 @@ static int patch_stac925x(struct hda_codec *codec) struct sigmatel_spec *spec; int err; - err = alloc_stac_spec(codec, ARRAY_SIZE(stac925x_pin_nids), - stac925x_pin_nids); + err = alloc_stac_spec(codec); if (err < 0) return err; spec = codec->spec; spec->linear_tone_beep = 1; + spec->gen.own_eapd_ctl = 1; - snd_hda_pick_fixup(codec, stac925x_models, stac925x_fixup_tbl, - stac925x_fixups); - - spec->multiout.max_channels = 2; - spec->multiout.num_dacs = 1; - spec->multiout.dac_nids = stac925x_dac_nids; - spec->adc_nids = stac925x_adc_nids; - spec->mux_nids = stac925x_mux_nids; - spec->num_muxes = 1; - spec->num_adcs = 1; - spec->num_pwrs = 0; - switch (codec->vendor_id) { - case 0x83847632: /* STAC9202 */ - case 0x83847633: /* STAC9202D */ - case 0x83847636: /* STAC9251 */ - case 0x83847637: /* STAC9251D */ - spec->num_dmics = STAC925X_NUM_DMICS; - spec->dmic_nids = stac925x_dmic_nids; - spec->num_dmuxes = ARRAY_SIZE(stac925x_dmux_nids); - spec->dmux_nids = stac925x_dmux_nids; - break; - default: - spec->num_dmics = 0; - break; - } + codec->patch_ops = stac_patch_ops; snd_hda_add_verbs(codec, stac925x_core_init); - spec->mixer = stac925x_mixer; - spec->num_caps = 1; - spec->capvols = stac925x_capvols; - spec->capsws = stac925x_capsws; + snd_hda_pick_fixup(codec, stac925x_models, stac925x_fixup_tbl, + stac925x_fixups); snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); - err = stac92xx_parse_auto_config(codec); + err = stac_parse_auto_config(codec); if (err < 0) { - stac92xx_free(codec); + stac_free(codec); return err; } - codec->patch_ops = stac92xx_patch_ops; - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); return 0; @@ -6546,86 +3718,66 @@ static int patch_stac925x(struct hda_codec *codec) static int patch_stac92hd73xx(struct hda_codec *codec) { struct sigmatel_spec *spec; - hda_nid_t conn[STAC92HD73_DAC_COUNT + 2]; int err; int num_dacs; - err = alloc_stac_spec(codec, ARRAY_SIZE(stac92hd73xx_pin_nids), - stac92hd73xx_pin_nids); + err = alloc_stac_spec(codec); if (err < 0) return err; spec = codec->spec; spec->linear_tone_beep = 0; - codec->slave_dig_outs = stac92hd73xx_slave_dig_outs; - - snd_hda_pick_fixup(codec, stac92hd73xx_models, stac92hd73xx_fixup_tbl, - stac92hd73xx_fixups); - - num_dacs = snd_hda_get_connections(codec, 0x0a, - conn, STAC92HD73_DAC_COUNT + 2) - 1; + num_dacs = snd_hda_get_num_conns(codec, 0x0a) - 1; if (num_dacs < 3 || num_dacs > 5) { printk(KERN_WARNING "hda_codec: Could not determine " "number of channels defaulting to DAC count\n"); - num_dacs = STAC92HD73_DAC_COUNT; + num_dacs = 5; } switch (num_dacs) { case 0x3: /* 6 Channel */ - spec->aloopback_ctl = stac92hd73xx_6ch_loopback; + spec->aloopback_ctl = &stac92hd73xx_6ch_loopback; break; case 0x4: /* 8 Channel */ - spec->aloopback_ctl = stac92hd73xx_8ch_loopback; + spec->aloopback_ctl = &stac92hd73xx_8ch_loopback; break; case 0x5: /* 10 Channel */ - spec->aloopback_ctl = stac92hd73xx_10ch_loopback; + spec->aloopback_ctl = &stac92hd73xx_10ch_loopback; break; } - spec->multiout.dac_nids = spec->dac_nids; spec->aloopback_mask = 0x01; spec->aloopback_shift = 8; spec->digbeep_nid = 0x1c; - spec->mux_nids = stac92hd73xx_mux_nids; - spec->adc_nids = stac92hd73xx_adc_nids; - spec->dmic_nids = stac92hd73xx_dmic_nids; - spec->dmux_nids = stac92hd73xx_dmux_nids; - spec->smux_nids = stac92hd73xx_smux_nids; - - spec->num_muxes = ARRAY_SIZE(stac92hd73xx_mux_nids); - spec->num_adcs = ARRAY_SIZE(stac92hd73xx_adc_nids); - spec->num_dmuxes = ARRAY_SIZE(stac92hd73xx_dmux_nids); - - spec->num_caps = STAC92HD73XX_NUM_CAPS; - spec->capvols = stac92hd73xx_capvols; - spec->capsws = stac92hd73xx_capsws; /* GPIO0 High = Enable EAPD */ spec->eapd_mask = spec->gpio_mask = spec->gpio_dir = 0x1; spec->gpio_data = 0x01; - spec->num_dmics = STAC92HD73XX_NUM_DMICS; - spec->num_smuxes = ARRAY_SIZE(stac92hd73xx_smux_nids); spec->eapd_switch = 1; spec->num_pwrs = ARRAY_SIZE(stac92hd73xx_pwr_nids); spec->pwr_nids = stac92hd73xx_pwr_nids; + spec->gen.own_eapd_ctl = 1; + + codec->patch_ops = stac_patch_ops; + + snd_hda_pick_fixup(codec, stac92hd73xx_models, stac92hd73xx_fixup_tbl, + stac92hd73xx_fixups); snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); if (!spec->volknob_init) snd_hda_add_verbs(codec, stac92hd73xx_core_init); - err = stac92xx_parse_auto_config(codec); + err = stac_parse_auto_config(codec); if (err < 0) { - stac92xx_free(codec); + stac_free(codec); return err; } - codec->patch_ops = stac92xx_patch_ops; - codec->proc_widget_hook = stac92hd7x_proc_hook; snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); @@ -6633,145 +3785,6 @@ static int patch_stac92hd73xx(struct hda_codec *codec) return 0; } -static int hp_bnb2011_with_dock(struct hda_codec *codec) -{ - if (codec->vendor_id != 0x111d7605 && - codec->vendor_id != 0x111d76d1) - return 0; - - switch (codec->subsystem_id) { - case 0x103c1618: - case 0x103c1619: - case 0x103c161a: - case 0x103c161b: - case 0x103c161c: - case 0x103c161d: - case 0x103c161e: - case 0x103c161f: - - case 0x103c162a: - case 0x103c162b: - - case 0x103c1630: - case 0x103c1631: - - case 0x103c1633: - case 0x103c1634: - case 0x103c1635: - - case 0x103c3587: - case 0x103c3588: - case 0x103c3589: - case 0x103c358a: - - case 0x103c3667: - case 0x103c3668: - case 0x103c3669: - - return 1; - } - return 0; -} - -static void stac92hd8x_add_pin(struct hda_codec *codec, hda_nid_t nid) -{ - struct sigmatel_spec *spec = codec->spec; - unsigned int def_conf = snd_hda_codec_get_pincfg(codec, nid); - int i; - - spec->auto_pin_nids[spec->auto_pin_cnt] = nid; - spec->auto_pin_cnt++; - - if (get_defcfg_device(def_conf) == AC_JACK_MIC_IN && - get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE) { - for (i = 0; i < ARRAY_SIZE(stac92hd83xxx_dmic_nids); i++) { - if (nid == stac92hd83xxx_dmic_nids[i]) { - spec->auto_dmic_nids[spec->auto_dmic_cnt] = nid; - spec->auto_dmic_cnt++; - } - } - } -} - -static void stac92hd8x_add_adc(struct hda_codec *codec, hda_nid_t nid) -{ - struct sigmatel_spec *spec = codec->spec; - - spec->auto_adc_nids[spec->auto_adc_cnt] = nid; - spec->auto_adc_cnt++; -} - -static void stac92hd8x_add_mux(struct hda_codec *codec, hda_nid_t nid) -{ - int i, j; - struct sigmatel_spec *spec = codec->spec; - - for (i = 0; i < spec->auto_adc_cnt; i++) { - if (get_connection_index(codec, - spec->auto_adc_nids[i], nid) >= 0) { - /* mux and volume for adc_nids[i] */ - if (!spec->auto_mux_nids[i]) { - spec->auto_mux_nids[i] = nid; - /* 92hd codecs capture volume is in mux */ - spec->auto_capvols[i] = HDA_COMPOSE_AMP_VAL(nid, - 3, 0, HDA_OUTPUT); - } - for (j = 0; j < spec->auto_dmic_cnt; j++) { - if (get_connection_index(codec, nid, - spec->auto_dmic_nids[j]) >= 0) { - /* dmux for adc_nids[i] */ - if (!spec->auto_dmux_nids[i]) - spec->auto_dmux_nids[i] = nid; - break; - } - } - break; - } - } -} - -static void stac92hd8x_fill_auto_spec(struct hda_codec *codec) -{ - hda_nid_t nid, end_nid; - unsigned int wid_caps, wid_type; - struct sigmatel_spec *spec = codec->spec; - - end_nid = codec->start_nid + codec->num_nodes; - - for (nid = codec->start_nid; nid < end_nid; nid++) { - wid_caps = get_wcaps(codec, nid); - wid_type = get_wcaps_type(wid_caps); - - if (wid_type == AC_WID_PIN) - stac92hd8x_add_pin(codec, nid); - - if (wid_type == AC_WID_AUD_IN && !(wid_caps & AC_WCAP_DIGITAL)) - stac92hd8x_add_adc(codec, nid); - } - - for (nid = codec->start_nid; nid < end_nid; nid++) { - wid_caps = get_wcaps(codec, nid); - wid_type = get_wcaps_type(wid_caps); - - if (wid_type == AC_WID_AUD_SEL) - stac92hd8x_add_mux(codec, nid); - } - - spec->pin_nids = spec->auto_pin_nids; - spec->num_pins = spec->auto_pin_cnt; - spec->adc_nids = spec->auto_adc_nids; - spec->num_adcs = spec->auto_adc_cnt; - spec->capvols = spec->auto_capvols; - spec->capsws = spec->auto_capvols; - spec->num_caps = spec->auto_adc_cnt; - spec->mux_nids = spec->auto_mux_nids; - spec->num_muxes = spec->auto_adc_cnt; - spec->dmux_nids = spec->auto_dmux_nids; - spec->num_dmuxes = spec->auto_adc_cnt; - spec->dmic_nids = spec->auto_dmic_nids; - spec->num_dmics = spec->auto_dmic_cnt; -} - static void stac_setup_gpio(struct hda_codec *codec) { struct sigmatel_spec *spec = codec->spec; @@ -6783,7 +3796,7 @@ static void stac_setup_gpio(struct hda_codec *codec) spec->gpio_data |= spec->gpio_led; } else { codec->patch_ops.set_power_state = - stac92xx_set_power_state; + stac_set_power_state; } } @@ -6792,6 +3805,8 @@ static void stac_setup_gpio(struct hda_codec *codec) spec->gpio_dir |= spec->mic_mute_led_gpio; spec->mic_mute_led_on = true; spec->gpio_data |= spec->mic_mute_led_gpio; + + spec->gen.capture_switch_hook = stac_capture_led_hook; } } @@ -6800,36 +3815,34 @@ static int patch_stac92hd83xxx(struct hda_codec *codec) struct sigmatel_spec *spec; int err; - err = alloc_stac_spec(codec, 0, NULL); /* pins filled later */ + err = alloc_stac_spec(codec); if (err < 0) return err; codec->epss = 0; /* longer delay needed for D3 */ - stac92hd8x_fill_auto_spec(codec); spec = codec->spec; spec->linear_tone_beep = 0; - codec->slave_dig_outs = stac92hd83xxx_slave_dig_outs; + spec->gen.own_eapd_ctl = 1; + spec->digbeep_nid = 0x21; spec->pwr_nids = stac92hd83xxx_pwr_nids; spec->num_pwrs = ARRAY_SIZE(stac92hd83xxx_pwr_nids); - spec->multiout.dac_nids = spec->dac_nids; + spec->default_polarity = -1; /* no default cfg */ - snd_hda_pick_fixup(codec, stac92hd83xxx_models, stac92hd83xxx_fixup_tbl, - stac92hd83xxx_fixups); + codec->patch_ops = stac_patch_ops; snd_hda_add_verbs(codec, stac92hd83xxx_core_init); - spec->default_polarity = -1; /* no default cfg */ - - codec->patch_ops = stac92xx_patch_ops; + snd_hda_pick_fixup(codec, stac92hd83xxx_models, stac92hd83xxx_fixup_tbl, + stac92hd83xxx_fixups); snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); stac_setup_gpio(codec); - err = stac92xx_parse_auto_config(codec); + err = stac_parse_auto_config(codec); if (err < 0) { - stac92xx_free(codec); + stac_free(codec); return err; } @@ -6840,133 +3853,31 @@ static int patch_stac92hd83xxx(struct hda_codec *codec) return 0; } -static int stac92hd71bxx_connected_smuxes(struct hda_codec *codec, - hda_nid_t dig0pin) -{ - struct sigmatel_spec *spec = codec->spec; - int idx; - - for (idx = 0; idx < spec->num_pins; idx++) - if (spec->pin_nids[idx] == dig0pin) - break; - if ((idx + 2) >= spec->num_pins) - return 0; - - /* dig1pin case */ - if (stac_get_defcfg_connect(codec, idx + 1) != AC_JACK_PORT_NONE) - return 2; - - /* dig0pin + dig2pin case */ - if (stac_get_defcfg_connect(codec, idx + 2) != AC_JACK_PORT_NONE) - return 2; - if (stac_get_defcfg_connect(codec, idx) != AC_JACK_PORT_NONE) - return 1; - else - return 0; -} - -/* HP dv7 bass switch - GPIO5 */ -#define stac_hp_bass_gpio_info snd_ctl_boolean_mono_info -static int stac_hp_bass_gpio_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct sigmatel_spec *spec = codec->spec; - ucontrol->value.integer.value[0] = !!(spec->gpio_data & 0x20); - return 0; -} - -static int stac_hp_bass_gpio_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct sigmatel_spec *spec = codec->spec; - unsigned int gpio_data; - - gpio_data = (spec->gpio_data & ~0x20) | - (ucontrol->value.integer.value[0] ? 0x20 : 0); - if (gpio_data == spec->gpio_data) - return 0; - spec->gpio_data = gpio_data; - stac_gpio_set(codec, spec->gpio_mask, spec->gpio_dir, spec->gpio_data); - return 1; -} - -static const struct snd_kcontrol_new stac_hp_bass_sw_ctrl = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .info = stac_hp_bass_gpio_info, - .get = stac_hp_bass_gpio_get, - .put = stac_hp_bass_gpio_put, -}; - -static int stac_add_hp_bass_switch(struct hda_codec *codec) -{ - struct sigmatel_spec *spec = codec->spec; - - if (!stac_control_new(spec, &stac_hp_bass_sw_ctrl, - "Bass Speaker Playback Switch", 0)) - return -ENOMEM; - - spec->gpio_mask |= 0x20; - spec->gpio_dir |= 0x20; - spec->gpio_data |= 0x20; - return 0; -} - static int patch_stac92hd71bxx(struct hda_codec *codec) { struct sigmatel_spec *spec; const struct hda_verb *unmute_init = stac92hd71bxx_unmute_core_init; int err; - err = alloc_stac_spec(codec, STAC92HD71BXX_NUM_PINS, - stac92hd71bxx_pin_nids_4port); + err = alloc_stac_spec(codec); if (err < 0) return err; spec = codec->spec; spec->linear_tone_beep = 0; - codec->patch_ops = stac92xx_patch_ops; - switch (codec->vendor_id) { - case 0x111d76b6: - case 0x111d76b7: - break; - case 0x111d7603: - case 0x111d7608: - /* On 92HD75Bx 0x27 isn't a pin nid */ - spec->num_pins--; - /* fallthrough */ - default: - spec->pin_nids = stac92hd71bxx_pin_nids_6port; - } - spec->num_pwrs = ARRAY_SIZE(stac92hd71bxx_pwr_nids); + spec->gen.own_eapd_ctl = 1; - snd_hda_pick_fixup(codec, stac92hd71bxx_models, stac92hd71bxx_fixup_tbl, - stac92hd71bxx_fixups); + codec->patch_ops = stac_patch_ops; /* GPIO0 = EAPD */ spec->gpio_mask = 0x01; spec->gpio_dir = 0x01; spec->gpio_data = 0x01; - spec->dmic_nids = stac92hd71bxx_dmic_nids; - spec->dmux_nids = stac92hd71bxx_dmux_nids; - - spec->num_caps = STAC92HD71BXX_NUM_CAPS; - spec->capvols = stac92hd71bxx_capvols; - spec->capsws = stac92hd71bxx_capsws; - switch (codec->vendor_id) { case 0x111d76b6: /* 4 Port without Analog Mixer */ case 0x111d76b7: unmute_init++; - /* fallthru */ - case 0x111d76b4: /* 6 Port without Analog Mixer */ - case 0x111d76b5: - codec->slave_dig_outs = stac92hd71bxx_slave_dig_outs; - spec->num_dmics = stac92xx_connected_ports(codec, - stac92hd71bxx_dmic_nids, - STAC92HD71BXX_NUM_DMICS); break; case 0x111d7608: /* 5 Port with Analog Mixer */ if ((codec->revision_id & 0xf) == 0 || @@ -6977,21 +3888,11 @@ static int patch_stac92hd71bxx(struct hda_codec *codec) unmute_init++; snd_hda_codec_set_pincfg(codec, 0x0f, 0x40f000f0); snd_hda_codec_set_pincfg(codec, 0x19, 0x40f000f3); - spec->dmic_nids = stac92hd71bxx_dmic_5port_nids; - spec->num_dmics = stac92xx_connected_ports(codec, - stac92hd71bxx_dmic_5port_nids, - STAC92HD71BXX_NUM_DMICS - 1); break; case 0x111d7603: /* 6 Port with Analog Mixer */ if ((codec->revision_id & 0xf) == 1) spec->stream_delay = 40; /* 40 milliseconds */ - /* fallthru */ - default: - codec->slave_dig_outs = stac92hd71bxx_slave_dig_outs; - spec->num_dmics = stac92xx_connected_ports(codec, - stac92hd71bxx_dmic_nids, - STAC92HD71BXX_NUM_DMICS); break; } @@ -7001,32 +3902,24 @@ static int patch_stac92hd71bxx(struct hda_codec *codec) if (get_wcaps(codec, 0xa) & AC_WCAP_IN_AMP) snd_hda_sequence_write_cache(codec, unmute_init); - spec->aloopback_ctl = stac92hd71bxx_loopback; + spec->aloopback_ctl = &stac92hd71bxx_loopback; spec->aloopback_mask = 0x50; spec->aloopback_shift = 0; spec->powerdown_adcs = 1; spec->digbeep_nid = 0x26; - spec->mux_nids = stac92hd71bxx_mux_nids; - spec->adc_nids = stac92hd71bxx_adc_nids; - spec->smux_nids = stac92hd71bxx_smux_nids; + spec->num_pwrs = ARRAY_SIZE(stac92hd71bxx_pwr_nids); spec->pwr_nids = stac92hd71bxx_pwr_nids; - spec->num_muxes = ARRAY_SIZE(stac92hd71bxx_mux_nids); - spec->num_adcs = ARRAY_SIZE(stac92hd71bxx_adc_nids); - spec->num_dmuxes = ARRAY_SIZE(stac92hd71bxx_dmux_nids); - spec->num_smuxes = stac92hd71bxx_connected_smuxes(codec, 0x1e); - - spec->multiout.dac_nids = spec->dac_nids; - spec->default_polarity = 1; - + snd_hda_pick_fixup(codec, stac92hd71bxx_models, stac92hd71bxx_fixup_tbl, + stac92hd71bxx_fixups); snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); stac_setup_gpio(codec); - err = stac92xx_parse_auto_config(codec); + err = stac_parse_auto_config(codec); if (err < 0) { - stac92xx_free(codec); + stac_free(codec); return err; } @@ -7042,42 +3935,18 @@ static int patch_stac922x(struct hda_codec *codec) struct sigmatel_spec *spec; int err; - err = alloc_stac_spec(codec, ARRAY_SIZE(stac922x_pin_nids), - stac922x_pin_nids); + err = alloc_stac_spec(codec); if (err < 0) return err; spec = codec->spec; spec->linear_tone_beep = 1; + spec->gen.own_eapd_ctl = 1; - snd_hda_pick_fixup(codec, stac922x_models, stac922x_fixup_tbl, - stac922x_fixups); - - spec->adc_nids = stac922x_adc_nids; - spec->mux_nids = stac922x_mux_nids; - spec->num_muxes = ARRAY_SIZE(stac922x_mux_nids); - spec->num_adcs = ARRAY_SIZE(stac922x_adc_nids); - spec->num_dmics = 0; - spec->num_pwrs = 0; + codec->patch_ops = stac_patch_ops; - spec->num_caps = STAC922X_NUM_CAPS; - spec->capvols = stac922x_capvols; - spec->capsws = stac922x_capsws; - - spec->multiout.dac_nids = spec->dac_nids; - snd_hda_add_verbs(codec, stac922x_core_init); - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); - - err = stac92xx_parse_auto_config(codec); - if (err < 0) { - stac92xx_free(codec); - return err; - } - - codec->patch_ops = stac92xx_patch_ops; - /* Fix Mux capture level; max to 2 */ snd_hda_override_amp_caps(codec, 0x12, HDA_OUTPUT, (0 << AC_AMPCAP_OFFSET_SHIFT) | @@ -7085,6 +3954,16 @@ static int patch_stac922x(struct hda_codec *codec) (0x27 << AC_AMPCAP_STEP_SIZE_SHIFT) | (0 << AC_AMPCAP_MUTE_SHIFT)); + snd_hda_pick_fixup(codec, stac922x_models, stac922x_fixup_tbl, + stac922x_fixups); + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); + + err = stac_parse_auto_config(codec); + if (err < 0) { + stac_free(codec); + return err; + } + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); return 0; @@ -7095,58 +3974,40 @@ static int patch_stac927x(struct hda_codec *codec) struct sigmatel_spec *spec; int err; - err = alloc_stac_spec(codec, ARRAY_SIZE(stac927x_pin_nids), - stac927x_pin_nids); + err = alloc_stac_spec(codec); if (err < 0) return err; spec = codec->spec; spec->linear_tone_beep = 1; - codec->slave_dig_outs = stac927x_slave_dig_outs; - - snd_hda_pick_fixup(codec, stac927x_models, stac927x_fixup_tbl, - stac927x_fixups); + spec->gen.own_eapd_ctl = 1; spec->digbeep_nid = 0x23; - spec->adc_nids = stac927x_adc_nids; - spec->num_adcs = ARRAY_SIZE(stac927x_adc_nids); - spec->mux_nids = stac927x_mux_nids; - spec->num_muxes = ARRAY_SIZE(stac927x_mux_nids); - spec->smux_nids = stac927x_smux_nids; - spec->num_smuxes = ARRAY_SIZE(stac927x_smux_nids); - spec->spdif_labels = stac927x_spdif_labels; - spec->dac_list = stac927x_dac_nids; - spec->multiout.dac_nids = spec->dac_nids; /* GPIO0 High = Enable EAPD */ spec->eapd_mask = spec->gpio_mask = 0x01; spec->gpio_dir = spec->gpio_data = 0x01; - spec->num_dmics = 0; - - spec->num_caps = STAC927X_NUM_CAPS; - spec->capvols = stac927x_capvols; - spec->capsws = stac927x_capsws; - - spec->num_pwrs = 0; - spec->aloopback_ctl = stac927x_loopback; + spec->aloopback_ctl = &stac927x_loopback; spec->aloopback_mask = 0x40; spec->aloopback_shift = 0; spec->eapd_switch = 1; + codec->patch_ops = stac_patch_ops; + + snd_hda_pick_fixup(codec, stac927x_models, stac927x_fixup_tbl, + stac927x_fixups); snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); if (!spec->volknob_init) snd_hda_add_verbs(codec, stac927x_core_init); - err = stac92xx_parse_auto_config(codec); + err = stac_parse_auto_config(codec); if (err < 0) { - stac92xx_free(codec); + stac_free(codec); return err; } - codec->patch_ops = stac92xx_patch_ops; - codec->proc_widget_hook = stac927x_proc_hook; /* @@ -7171,40 +4032,21 @@ static int patch_stac9205(struct hda_codec *codec) struct sigmatel_spec *spec; int err; - err = alloc_stac_spec(codec, ARRAY_SIZE(stac9205_pin_nids), - stac9205_pin_nids); + err = alloc_stac_spec(codec); if (err < 0) return err; spec = codec->spec; spec->linear_tone_beep = 1; - - snd_hda_pick_fixup(codec, stac9205_models, stac9205_fixup_tbl, - stac9205_fixups); + spec->gen.own_eapd_ctl = 1; spec->digbeep_nid = 0x23; - spec->adc_nids = stac9205_adc_nids; - spec->num_adcs = ARRAY_SIZE(stac9205_adc_nids); - spec->mux_nids = stac9205_mux_nids; - spec->num_muxes = ARRAY_SIZE(stac9205_mux_nids); - spec->smux_nids = stac9205_smux_nids; - spec->num_smuxes = ARRAY_SIZE(stac9205_smux_nids); - spec->dmic_nids = stac9205_dmic_nids; - spec->num_dmics = STAC9205_NUM_DMICS; - spec->dmux_nids = stac9205_dmux_nids; - spec->num_dmuxes = ARRAY_SIZE(stac9205_dmux_nids); - spec->num_pwrs = 0; snd_hda_add_verbs(codec, stac9205_core_init); - spec->aloopback_ctl = stac9205_loopback; - - spec->num_caps = STAC9205_NUM_CAPS; - spec->capvols = stac9205_capvols; - spec->capsws = stac9205_capsws; + spec->aloopback_ctl = &stac9205_loopback; spec->aloopback_mask = 0x40; spec->aloopback_shift = 0; - spec->multiout.dac_nids = spec->dac_nids; /* GPIO0 High = EAPD */ spec->eapd_mask = spec->gpio_mask = spec->gpio_dir = 0x1; @@ -7213,16 +4055,18 @@ static int patch_stac9205(struct hda_codec *codec) /* Turn on/off EAPD per HP plugging */ spec->eapd_switch = 1; + codec->patch_ops = stac_patch_ops; + + snd_hda_pick_fixup(codec, stac9205_models, stac9205_fixup_tbl, + stac9205_fixups); snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); - err = stac92xx_parse_auto_config(codec); + err = stac_parse_auto_config(codec); if (err < 0) { - stac92xx_free(codec); + stac_free(codec); return err; } - codec->patch_ops = stac92xx_patch_ops; - codec->proc_widget_hook = stac9205_proc_hook; snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); @@ -7240,24 +4084,6 @@ static const struct hda_verb stac9872_core_init[] = { {} }; -static const hda_nid_t stac9872_pin_nids[] = { - 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, - 0x11, 0x13, 0x14, -}; - -static const hda_nid_t stac9872_adc_nids[] = { - 0x8 /*,0x6*/ -}; - -static const hda_nid_t stac9872_mux_nids[] = { - 0x15 -}; - -static const unsigned long stac9872_capvols[] = { - HDA_COMPOSE_AMP_VAL(0x09, 3, 0, HDA_INPUT), -}; -#define stac9872_capsws stac9872_capvols - static const struct hda_pintbl stac9872_vaio_pin_configs[] = { { 0x0a, 0x03211020 }, { 0x0b, 0x411111f0 }, @@ -7294,36 +4120,27 @@ static int patch_stac9872(struct hda_codec *codec) struct sigmatel_spec *spec; int err; - err = alloc_stac_spec(codec, ARRAY_SIZE(stac9872_pin_nids), - stac9872_pin_nids); + err = alloc_stac_spec(codec); if (err < 0) return err; spec = codec->spec; spec->linear_tone_beep = 1; + spec->gen.own_eapd_ctl = 1; - snd_hda_pick_fixup(codec, stac9872_models, stac9872_fixup_tbl, - stac9872_fixups); + codec->patch_ops = stac_patch_ops; - spec->multiout.dac_nids = spec->dac_nids; - spec->num_adcs = ARRAY_SIZE(stac9872_adc_nids); - spec->adc_nids = stac9872_adc_nids; - spec->num_muxes = ARRAY_SIZE(stac9872_mux_nids); - spec->mux_nids = stac9872_mux_nids; - spec->num_caps = 1; - spec->capvols = stac9872_capvols; - spec->capsws = stac9872_capsws; snd_hda_add_verbs(codec, stac9872_core_init); + snd_hda_pick_fixup(codec, stac9872_models, stac9872_fixup_tbl, + stac9872_fixups); snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); - err = stac92xx_parse_auto_config(codec); + err = stac_parse_auto_config(codec); if (err < 0) { - stac92xx_free(codec); + stac_free(codec); return -EINVAL; } - spec->input_mux = &spec->private_imux; - codec->patch_ops = stac92xx_patch_ops; snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); -- cgit v1.2.3 From 8a6c21aee8ab94c35e9db7a4ad35cfeb94f57d59 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 18 Jan 2013 07:51:17 +0100 Subject: ALSA: hda - Fix missing unsol event handler in some codec drivers This resulted in non-working auto-mute behavior, of course... Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_analog.c | 1 + sound/pci/hda/patch_ca0110.c | 1 + sound/pci/hda/patch_cmedia.c | 1 + 3 files changed, 3 insertions(+) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index 02fe0d1da6e0..6feaec4cd6b9 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c @@ -595,6 +595,7 @@ static const struct hda_codec_ops ad198x_auto_patch_ops = { .build_pcms = snd_hda_gen_build_pcms, .init = snd_hda_gen_init, .free = ad198x_free, + .unsol_event = snd_hda_jack_unsol_event, #ifdef CONFIG_PM .check_power_status = snd_hda_gen_check_power_status, .suspend = ad198x_suspend, diff --git a/sound/pci/hda/patch_ca0110.c b/sound/pci/hda/patch_ca0110.c index 8d093254c204..db7635cd8255 100644 --- a/sound/pci/hda/patch_ca0110.c +++ b/sound/pci/hda/patch_ca0110.c @@ -36,6 +36,7 @@ static const struct hda_codec_ops ca0110_patch_ops = { .build_pcms = snd_hda_gen_build_pcms, .init = snd_hda_gen_init, .free = snd_hda_gen_free, + .unsol_event = snd_hda_jack_unsol_event, }; static int ca0110_parse_auto_config(struct hda_codec *codec) diff --git a/sound/pci/hda/patch_cmedia.c b/sound/pci/hda/patch_cmedia.c index 04dd3b6b7db5..087cabbcf25c 100644 --- a/sound/pci/hda/patch_cmedia.c +++ b/sound/pci/hda/patch_cmedia.c @@ -576,6 +576,7 @@ static const struct hda_codec_ops cmi_auto_patch_ops = { .build_pcms = snd_hda_gen_build_pcms, .init = snd_hda_gen_init, .free = snd_hda_gen_free, + .unsol_event = snd_hda_jack_unsol_event, }; static int cmi_parse_auto_config(struct hda_codec *codec) -- cgit v1.2.3 From 9dba205b486152e3d31ab04f3b60efeef035cec5 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 18 Jan 2013 10:01:15 +0100 Subject: ALSA: hda - Keep autocfg.input idx value in imux table Since the imux table entries can be a subset of autocfg.input table, the indices of these aren't always same. For passing the proper index value of autocfg.input at creating input ctl labels (via snd_hda_autocfg_input_label()), keep the corresponding autocfg.input idx value in the index field of each imux item, which isn't used in the generic driver. Also, this makes easier to check the invalid imux pin for stereo mix. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index ef4c04adbc21..7444d2e7a82a 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -2478,7 +2478,8 @@ static int check_dyn_adc_switch(struct hda_codec *codec) /* parse capture source paths from the given pin and create imux items */ static int parse_capture_source(struct hda_codec *codec, hda_nid_t pin, - int num_adcs, const char *label, int anchor) + int cfg_idx, int num_adcs, + const char *label, int anchor) { struct hda_gen_spec *spec = codec->spec; struct hda_input_mux *imux = &spec->input_mux; @@ -2501,8 +2502,7 @@ static int parse_capture_source(struct hda_codec *codec, hda_nid_t pin, if (!imux_added) { spec->imux_pins[imux->num_items] = pin; - snd_hda_add_imux_item(imux, label, - imux->num_items, NULL); + snd_hda_add_imux_item(imux, label, cfg_idx, NULL); imux_added = true; } } @@ -2513,6 +2513,9 @@ static int parse_capture_source(struct hda_codec *codec, hda_nid_t pin, /* * create playback/capture controls for input pins */ + +#define CFG_IDX_MIX 99 /* a dummy cfg->input idx for stereo mix */ + static int create_input_ctls(struct hda_codec *codec) { struct hda_gen_spec *spec = codec->spec; @@ -2556,7 +2559,8 @@ static int create_input_ctls(struct hda_codec *codec) } } - err = parse_capture_source(codec, pin, num_adcs, label, -mixer); + err = parse_capture_source(codec, pin, i, + num_adcs, label, -mixer); if (err < 0) return err; @@ -2568,7 +2572,7 @@ static int create_input_ctls(struct hda_codec *codec) } if (mixer && spec->add_stereo_mix_input) { - err = parse_capture_source(codec, mixer, num_adcs, + err = parse_capture_source(codec, mixer, CFG_IDX_MIX, num_adcs, "Stereo Mix", 0); if (err < 0) return err; @@ -2909,7 +2913,11 @@ static int create_multi_cap_vol_ctl(struct hda_codec *codec) for (i = 0; i < imux->num_items; i++) { const char *label; bool inv_dmic; - label = hda_get_autocfg_input_label(codec, &spec->autocfg, i); + + if (imux->items[i].index >= spec->autocfg.num_inputs) + continue; + label = hda_get_autocfg_input_label(codec, &spec->autocfg, + imux->items[i].index); if (prev_label && !strcmp(label, prev_label)) type_idx++; else -- cgit v1.2.3 From c970042c129fbda2017e537d284e61ef4966a140 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 18 Jan 2013 10:17:30 +0100 Subject: ALSA: hda - Unify input label creations in generic parser There are a few places creating the labels and indices of kctls for each input pin in the current generic parser code. This is redundant and makes harder to maintain. Let's create the labels and indices at once and keep them in hda_gen_spec. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 91 +++++++++++++++++++++++++++------------------ sound/pci/hda/hda_generic.h | 2 + 2 files changed, 56 insertions(+), 37 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 7444d2e7a82a..ebb558414314 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -2514,6 +2514,38 @@ static int parse_capture_source(struct hda_codec *codec, hda_nid_t pin, * create playback/capture controls for input pins */ +/* fill the label for each input at first */ +static int fill_input_pin_labels(struct hda_codec *codec) +{ + struct hda_gen_spec *spec = codec->spec; + const struct auto_pin_cfg *cfg = &spec->autocfg; + int i; + + for (i = 0; i < cfg->num_inputs; i++) { + hda_nid_t pin = cfg->inputs[i].pin; + const char *label; + int j, idx; + + if (!is_input_pin(codec, pin)) + continue; + + label = hda_get_autocfg_input_label(codec, cfg, i); + idx = 0; + for (j = i; j >= 0; j--) { + if (spec->input_labels[j] && + !strcmp(spec->input_labels[j], label)) { + idx = spec->input_label_idxs[j] + 1; + break; + } + } + + spec->input_labels[i] = label; + spec->input_label_idxs[i] = idx; + } + + return 0; +} + #define CFG_IDX_MIX 99 /* a dummy cfg->input idx for stereo mix */ static int create_input_ctls(struct hda_codec *codec) @@ -2522,29 +2554,24 @@ static int create_input_ctls(struct hda_codec *codec) const struct auto_pin_cfg *cfg = &spec->autocfg; hda_nid_t mixer = spec->mixer_nid; int num_adcs; - int i, err, type_idx = 0; - const char *prev_label = NULL; + int i, err; unsigned int val; num_adcs = fill_adc_nids(codec); if (num_adcs < 0) return 0; + err = fill_input_pin_labels(codec); + if (err < 0) + return err; + for (i = 0; i < cfg->num_inputs; i++) { hda_nid_t pin; - const char *label; pin = cfg->inputs[i].pin; if (!is_input_pin(codec, pin)) continue; - label = hda_get_autocfg_input_label(codec, cfg, i); - if (prev_label && !strcmp(label, prev_label)) - type_idx++; - else - type_idx = 0; - prev_label = label; - val = PIN_IN; if (cfg->inputs[i].type == AUTO_PIN_MIC) val |= snd_hda_get_default_vref(codec, pin); @@ -2553,14 +2580,16 @@ static int create_input_ctls(struct hda_codec *codec) if (mixer) { if (is_reachable_path(codec, pin, mixer)) { err = new_analog_input(codec, i, pin, - label, type_idx, mixer); + spec->input_labels[i], + spec->input_label_idxs[i], + mixer); if (err < 0) return err; } } - err = parse_capture_source(codec, pin, i, - num_adcs, label, -mixer); + err = parse_capture_source(codec, pin, i, num_adcs, + spec->input_labels[i], -mixer); if (err < 0) return err; @@ -2907,26 +2936,22 @@ static int create_multi_cap_vol_ctl(struct hda_codec *codec) { struct hda_gen_spec *spec = codec->spec; struct hda_input_mux *imux = &spec->input_mux; - int i, err, type, type_idx = 0; - const char *prev_label = NULL; + int i, err, type; for (i = 0; i < imux->num_items; i++) { - const char *label; bool inv_dmic; + int idx; - if (imux->items[i].index >= spec->autocfg.num_inputs) + idx = imux->items[i].index; + if (idx >= spec->autocfg.num_inputs) continue; - label = hda_get_autocfg_input_label(codec, &spec->autocfg, - imux->items[i].index); - if (prev_label && !strcmp(label, prev_label)) - type_idx++; - else - type_idx = 0; - prev_label = label; inv_dmic = is_inv_dmic_pin(codec, spec->imux_pins[i]); for (type = 0; type < 2; type++) { - err = add_single_cap_ctl(codec, label, type_idx, type, + err = add_single_cap_ctl(codec, + spec->input_labels[idx], + spec->input_label_idxs[idx], + type, get_first_cap_ctl(codec, i, type), inv_dmic); if (err < 0) @@ -3012,16 +3037,13 @@ static int parse_mic_boost(struct hda_codec *codec) struct hda_gen_spec *spec = codec->spec; struct auto_pin_cfg *cfg = &spec->autocfg; int i, err; - int type_idx = 0; hda_nid_t nid; - const char *prev_label = NULL; for (i = 0; i < cfg->num_inputs; i++) { if (cfg->inputs[i].type > AUTO_PIN_MIC) break; nid = cfg->inputs[i].pin; if (get_wcaps(codec, nid) & AC_WCAP_IN_AMP) { - const char *label; char boost_label[44]; struct nid_path *path; unsigned int val; @@ -3029,18 +3051,13 @@ static int parse_mic_boost(struct hda_codec *codec) if (!nid_has_volume(codec, nid, HDA_INPUT)) continue; - label = hda_get_autocfg_input_label(codec, cfg, i); - if (prev_label && !strcmp(label, prev_label)) - type_idx++; - else - type_idx = 0; - prev_label = label; - snprintf(boost_label, sizeof(boost_label), - "%s Boost Volume", label); + "%s Boost Volume", + spec->input_labels[i]); val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT); err = add_control(spec, HDA_CTL_WIDGET_VOL, - boost_label, type_idx, val); + boost_label, + spec->input_label_idxs[i], val); if (err < 0) return err; diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h index 7b14e9ce7486..f6b88cd4584f 100644 --- a/sound/pci/hda/hda_generic.h +++ b/sound/pci/hda/hda_generic.h @@ -105,6 +105,8 @@ struct hda_gen_spec { hda_nid_t adc_nids[AUTO_CFG_MAX_OUTS]; hda_nid_t dig_in_nid; /* digital-in NID; optional */ hda_nid_t mixer_nid; /* analog-mixer NID */ + const char *input_labels[AUTO_CFG_MAX_OUTS]; + int input_label_idxs[AUTO_CFG_MAX_OUTS]; /* capture setup for dynamic dual-adc switch */ hda_nid_t cur_adc; -- cgit v1.2.3 From 8999bf0af035ecbea039914a5af2f23f5a621d62 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 18 Jan 2013 11:01:33 +0100 Subject: ALSA: hda - Fix invalid mute in path activation When an amp in the activation path is associated with mixer controls, activate_amp() tries to skip the initialization. It's good, but only if the mixer really initializes both mute and volume. Otherwise, either the mute of the volume is left uninitialized. This patch adds this missing check and properly initialize the partially controlled amps in an activation path. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 60 +++++++++++++++++++++++++++++++-------------- 1 file changed, 42 insertions(+), 18 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index ebb558414314..edec98f1a9a5 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -315,11 +315,10 @@ static bool is_ctl_used(struct hda_codec *codec, unsigned int val, int type) /* check whether a control with the given (nid, dir, idx) was assigned */ static bool is_ctl_associated(struct hda_codec *codec, hda_nid_t nid, - int dir, int idx) + int dir, int idx, int type) { unsigned int val = HDA_COMPOSE_AMP_VAL(nid, 3, idx, dir); - return is_ctl_used(codec, val, NID_PATH_VOL_CTL) || - is_ctl_used(codec, val, NID_PATH_MUTE_CTL); + return is_ctl_used(codec, val, type); } static void print_nid_path(const char *pfx, struct nid_path *path) @@ -590,12 +589,10 @@ static bool is_active_nid(struct hda_codec *codec, hda_nid_t nid, /* get the default amp value for the target state */ static int get_amp_val_to_activate(struct hda_codec *codec, hda_nid_t nid, - int dir, bool enable) + int dir, unsigned int caps, bool enable) { - unsigned int caps; unsigned int val = 0; - caps = query_amp_caps(codec, nid, dir); if (caps & AC_AMPCAP_NUM_STEPS) { /* set to 0dB */ if (enable) @@ -611,19 +608,49 @@ static int get_amp_val_to_activate(struct hda_codec *codec, hda_nid_t nid, /* initialize the amp value (only at the first time) */ static void init_amp(struct hda_codec *codec, hda_nid_t nid, int dir, int idx) { - int val = get_amp_val_to_activate(codec, nid, dir, false); + unsigned int caps = query_amp_caps(codec, nid, dir); + int val = get_amp_val_to_activate(codec, nid, dir, caps, false); snd_hda_codec_amp_init_stereo(codec, nid, dir, idx, 0xff, val); } +/* calculate amp value mask we can modify; + * if the given amp is controlled by mixers, don't touch it + */ +static unsigned int get_amp_mask_to_modify(struct hda_codec *codec, + hda_nid_t nid, int dir, int idx, + unsigned int caps) +{ + unsigned int mask = 0xff; + + if (caps & AC_AMPCAP_MUTE) { + if (is_ctl_associated(codec, nid, dir, idx, NID_PATH_MUTE_CTL)) + mask &= ~0x80; + } + if (caps & AC_AMPCAP_NUM_STEPS) { + if (is_ctl_associated(codec, nid, dir, idx, NID_PATH_VOL_CTL) || + is_ctl_associated(codec, nid, dir, idx, NID_PATH_BOOST_CTL)) + mask &= ~0x7f; + } + return mask; +} + static void activate_amp(struct hda_codec *codec, hda_nid_t nid, int dir, - int idx, bool enable) + int idx, int idx_to_check, bool enable) { - int val; - if (is_ctl_associated(codec, nid, dir, idx) || - (!enable && is_active_nid(codec, nid, dir, idx))) + unsigned int caps; + unsigned int mask, val; + + if (!enable && is_active_nid(codec, nid, dir, idx)) + return; + + caps = query_amp_caps(codec, nid, dir); + val = get_amp_val_to_activate(codec, nid, dir, caps, enable); + mask = get_amp_mask_to_modify(codec, nid, dir, idx_to_check, caps); + if (!mask) return; - val = get_amp_val_to_activate(codec, nid, dir, enable); - snd_hda_codec_amp_stereo(codec, nid, dir, idx, 0xff, val); + + val &= mask; + snd_hda_codec_amp_stereo(codec, nid, dir, idx, mask, val); } static void activate_amp_out(struct hda_codec *codec, struct nid_path *path, @@ -631,7 +658,7 @@ static void activate_amp_out(struct hda_codec *codec, struct nid_path *path, { hda_nid_t nid = path->path[i]; init_amp(codec, nid, HDA_OUTPUT, 0); - activate_amp(codec, nid, HDA_OUTPUT, 0, enable); + activate_amp(codec, nid, HDA_OUTPUT, 0, 0, enable); } static void activate_amp_in(struct hda_codec *codec, struct nid_path *path, @@ -655,16 +682,13 @@ static void activate_amp_in(struct hda_codec *codec, struct nid_path *path, for (n = 0; n < nums; n++) init_amp(codec, nid, HDA_INPUT, n); - if (is_ctl_associated(codec, nid, HDA_INPUT, idx)) - return; - /* here is a little bit tricky in comparison with activate_amp_out(); * when aa-mixer is available, we need to enable the path as well */ for (n = 0; n < nums; n++) { if (n != idx && (!add_aamix || conn[n] != spec->mixer_nid)) continue; - activate_amp(codec, nid, HDA_INPUT, n, enable); + activate_amp(codec, nid, HDA_INPUT, n, idx, enable); } } -- cgit v1.2.3 From 6f7c83afc6cc3f66d13e4ad0a0f5693d9175e1ab Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 18 Jan 2013 11:07:15 +0100 Subject: ALSA: hda - Look for boost controls more deeply In the current generic parser code, we look for the (mic) boost controls only on input pins. But many codecs assign the boost volume to a widget connected to each input pin instead of the input amp of the pin itself. In this patch, the parser tries to look through more widgets connected to the pin and find a boost amp. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 101 +++++++++++++++++++++++++++++++++----------- 1 file changed, 77 insertions(+), 24 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index edec98f1a9a5..cadfe65e2fe1 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -3056,39 +3056,92 @@ static int create_capture_mixers(struct hda_codec *codec) /* * add mic boosts if needed */ + +/* check whether the given amp is feasible as a boost volume */ +static bool check_boost_vol(struct hda_codec *codec, hda_nid_t nid, + int dir, int idx) +{ + unsigned int step; + + if (!nid_has_volume(codec, nid, dir) || + is_ctl_associated(codec, nid, dir, idx, NID_PATH_VOL_CTL) || + is_ctl_associated(codec, nid, dir, idx, NID_PATH_BOOST_CTL)) + return false; + + step = (query_amp_caps(codec, nid, dir) & AC_AMPCAP_STEP_SIZE) + >> AC_AMPCAP_STEP_SIZE_SHIFT; + if (step < 0x20) + return false; + return true; +} + +/* look for a boost amp in a widget close to the pin */ +static unsigned int look_for_boost_amp(struct hda_codec *codec, + struct nid_path *path) +{ + unsigned int val = 0; + hda_nid_t nid; + int depth; + + for (depth = 0; depth < 3; depth++) { + if (depth >= path->depth - 1) + break; + nid = path->path[depth]; + if (depth && check_boost_vol(codec, nid, HDA_OUTPUT, 0)) { + val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT); + break; + } else if (check_boost_vol(codec, nid, HDA_INPUT, + path->idx[depth])) { + val = HDA_COMPOSE_AMP_VAL(nid, 3, path->idx[depth], + HDA_INPUT); + break; + } + } + + return val; +} + static int parse_mic_boost(struct hda_codec *codec) { struct hda_gen_spec *spec = codec->spec; struct auto_pin_cfg *cfg = &spec->autocfg; + struct hda_input_mux *imux = &spec->input_mux; int i, err; - hda_nid_t nid; - for (i = 0; i < cfg->num_inputs; i++) { - if (cfg->inputs[i].type > AUTO_PIN_MIC) - break; - nid = cfg->inputs[i].pin; - if (get_wcaps(codec, nid) & AC_WCAP_IN_AMP) { - char boost_label[44]; - struct nid_path *path; - unsigned int val; + if (!spec->num_adc_nids) + return 0; - if (!nid_has_volume(codec, nid, HDA_INPUT)) - continue; + for (i = 0; i < imux->num_items; i++) { + struct nid_path *path; + unsigned int val; + int idx; + char boost_label[44]; - snprintf(boost_label, sizeof(boost_label), - "%s Boost Volume", - spec->input_labels[i]); - val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT); - err = add_control(spec, HDA_CTL_WIDGET_VOL, - boost_label, - spec->input_label_idxs[i], val); - if (err < 0) - return err; + idx = imux->items[i].index; + if (idx >= imux->num_items) + continue; - path = snd_hda_get_nid_path(codec, nid, 0); - if (path) - path->ctls[NID_PATH_BOOST_CTL] = val; - } + /* check only line-in and mic pins */ + if (cfg->inputs[idx].type > AUTO_PIN_MIC) + continue; + + path = get_input_path(codec, 0, i); + if (!path) + continue; + + val = look_for_boost_amp(codec, path); + if (!val) + continue; + + /* create a boost control */ + snprintf(boost_label, sizeof(boost_label), + "%s Boost Volume", spec->input_labels[idx]); + err = add_control(spec, HDA_CTL_WIDGET_VOL, boost_label, + spec->input_label_idxs[idx], val); + if (err < 0) + return err; + + path->ctls[NID_PATH_BOOST_CTL] = val; } return 0; } -- cgit v1.2.3 From a35bd1e3e6eadba210faedf93354c7657dd59238 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 18 Jan 2013 14:01:14 +0100 Subject: ALSA: hda - Fix missing call of capture_switch_hook When a standard capture switch without multiple binding is used, the call for capture_switch_hook isn't called properly. Replace the put ops to add the hook call in that case. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 61 ++++++++++++++++++++++++++++++++++----------- 1 file changed, 46 insertions(+), 15 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index cadfe65e2fe1..e9af9ab4fac5 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -750,19 +750,20 @@ static const struct snd_kcontrol_new control_templates[] = { }; /* add dynamic controls from template */ -static int add_control(struct hda_gen_spec *spec, int type, const char *name, +static struct snd_kcontrol_new * +add_control(struct hda_gen_spec *spec, int type, const char *name, int cidx, unsigned long val) { struct snd_kcontrol_new *knew; knew = snd_hda_gen_add_kctl(spec, name, &control_templates[type]); if (!knew) - return -ENOMEM; + return NULL; knew->index = cidx; if (get_amp_nid_(val)) knew->subdevice = HDA_SUBDEV_AMP_FLAG; knew->private_value = val; - return 0; + return knew; } static int add_control_with_pfx(struct hda_gen_spec *spec, int type, @@ -771,7 +772,9 @@ static int add_control_with_pfx(struct hda_gen_spec *spec, int type, { char name[32]; snprintf(name, sizeof(name), "%s %s %s", pfx, dir, sfx); - return add_control(spec, type, name, cidx, val); + if (!add_control(spec, type, name, cidx, val)) + return -ENOMEM; + return 0; } #define add_pb_vol_ctrl(spec, type, pfx, val) \ @@ -2857,6 +2860,26 @@ static bool is_inv_dmic_pin(struct hda_codec *codec, hda_nid_t nid) return false; } +static int cap_single_sw_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct hda_gen_spec *spec = codec->spec; + int ret; + + ret = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol); + if (ret < 0) + return ret; + + if (spec->capture_switch_hook) { + bool enable = (ucontrol->value.integer.value[0] || + ucontrol->value.integer.value[1]); + spec->capture_switch_hook(codec, enable); + } + + return ret; +} + static int add_single_cap_ctl(struct hda_codec *codec, const char *label, int idx, bool is_switch, unsigned int ctl, bool inv_dmic) @@ -2866,7 +2889,7 @@ static int add_single_cap_ctl(struct hda_codec *codec, const char *label, int type = is_switch ? HDA_CTL_WIDGET_MUTE : HDA_CTL_WIDGET_VOL; const char *sfx = is_switch ? "Switch" : "Volume"; unsigned int chs = inv_dmic ? 1 : 3; - int err; + struct snd_kcontrol_new *knew; if (!ctl) return 0; @@ -2877,10 +2900,14 @@ static int add_single_cap_ctl(struct hda_codec *codec, const char *label, else snprintf(tmpname, sizeof(tmpname), "Capture %s", sfx); - err = add_control(spec, type, tmpname, idx, - amp_val_replace_channels(ctl, chs)); - if (err < 0 || !inv_dmic) - return err; + knew = add_control(spec, type, tmpname, idx, + amp_val_replace_channels(ctl, chs)); + if (!knew) + return -ENOMEM; + if (is_switch && spec->capture_switch_hook) + knew->put = cap_single_sw_put; + if (!inv_dmic) + return 0; /* Make independent right kcontrol */ if (label) @@ -2889,8 +2916,13 @@ static int add_single_cap_ctl(struct hda_codec *codec, const char *label, else snprintf(tmpname, sizeof(tmpname), "Inverted Capture %s", sfx); - return add_control(spec, type, tmpname, idx, + knew = add_control(spec, type, tmpname, idx, amp_val_replace_channels(ctl, 2)); + if (!knew) + return -ENOMEM; + if (is_switch && spec->capture_switch_hook) + knew->put = cap_single_sw_put; + return 0; } /* create single (and simple) capture volume and switch controls */ @@ -3106,7 +3138,7 @@ static int parse_mic_boost(struct hda_codec *codec) struct hda_gen_spec *spec = codec->spec; struct auto_pin_cfg *cfg = &spec->autocfg; struct hda_input_mux *imux = &spec->input_mux; - int i, err; + int i; if (!spec->num_adc_nids) return 0; @@ -3136,10 +3168,9 @@ static int parse_mic_boost(struct hda_codec *codec) /* create a boost control */ snprintf(boost_label, sizeof(boost_label), "%s Boost Volume", spec->input_labels[idx]); - err = add_control(spec, HDA_CTL_WIDGET_VOL, boost_label, - spec->input_label_idxs[idx], val); - if (err < 0) - return err; + if (!add_control(spec, HDA_CTL_WIDGET_VOL, boost_label, + spec->input_label_idxs[idx], val)) + return -ENOMEM; path->ctls[NID_PATH_BOOST_CTL] = val; } -- cgit v1.2.3 From a90229e0517938a5c5d9d682708b6ee7cd0e19b9 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 18 Jan 2013 14:10:00 +0100 Subject: ALSA: hda - Consolidate cap_sync_hook and capture_switch_hook Two hooks in hda_gen_spec, cap_sync_hook and capture_switch_hook, play very similar roles. The only differences are that the former is called more often (e.g. at init or switching capsrc) while the latter can take an on/off argument. As a more generic implementation, consolidate these two hooks, and pass snd_ctl_elem_value pointer as the second argument. If the secondary argument is non-NULL, it can take the on/off value, so the caller handles it like the former capture_switch_hook. If it's NULL, it's called in the init or capsrc switch case. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 34 +++++++++------------------------- sound/pci/hda/hda_generic.h | 6 ++---- sound/pci/hda/patch_realtek.c | 3 ++- sound/pci/hda/patch_sigmatel.c | 12 +++++++++--- 4 files changed, 22 insertions(+), 33 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index e9af9ab4fac5..f0d83b2f42ac 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -2738,7 +2738,7 @@ static int cap_put_caller(struct snd_kcontrol *kcontrol, mutex_unlock(&codec->control_mutex); snd_hda_codec_flush_amp_cache(codec); /* flush the updates */ if (err >= 0 && spec->cap_sync_hook) - spec->cap_sync_hook(codec); + spec->cap_sync_hook(codec, ucontrol); return err; } @@ -2774,23 +2774,9 @@ static const struct snd_kcontrol_new cap_vol_temp = { static int cap_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct hda_gen_spec *spec = codec->spec; - int ret; - - ret = cap_put_caller(kcontrol, ucontrol, + return cap_put_caller(kcontrol, ucontrol, snd_hda_mixer_amp_switch_put, NID_PATH_MUTE_CTL); - if (ret < 0) - return ret; - - if (spec->capture_switch_hook) { - bool enable = (ucontrol->value.integer.value[0] || - ucontrol->value.integer.value[1]); - spec->capture_switch_hook(codec, enable); - } - - return ret; } static const struct snd_kcontrol_new cap_sw_temp = { @@ -2860,6 +2846,7 @@ static bool is_inv_dmic_pin(struct hda_codec *codec, hda_nid_t nid) return false; } +/* capture switch put callback for a single control with hook call */ static int cap_single_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -2871,11 +2858,8 @@ static int cap_single_sw_put(struct snd_kcontrol *kcontrol, if (ret < 0) return ret; - if (spec->capture_switch_hook) { - bool enable = (ucontrol->value.integer.value[0] || - ucontrol->value.integer.value[1]); - spec->capture_switch_hook(codec, enable); - } + if (spec->cap_sync_hook) + spec->cap_sync_hook(codec, ucontrol); return ret; } @@ -2904,7 +2888,7 @@ static int add_single_cap_ctl(struct hda_codec *codec, const char *label, amp_val_replace_channels(ctl, chs)); if (!knew) return -ENOMEM; - if (is_switch && spec->capture_switch_hook) + if (is_switch) knew->put = cap_single_sw_put; if (!inv_dmic) return 0; @@ -2920,7 +2904,7 @@ static int add_single_cap_ctl(struct hda_codec *codec, const char *label, amp_val_replace_channels(ctl, 2)); if (!knew) return -ENOMEM; - if (is_switch && spec->capture_switch_hook) + if (is_switch) knew->put = cap_single_sw_put; return 0; } @@ -3280,7 +3264,7 @@ static int mux_select(struct hda_codec *codec, unsigned int adc_idx, return 0; snd_hda_activate_path(codec, path, true, false); if (spec->cap_sync_hook) - spec->cap_sync_hook(codec); + spec->cap_sync_hook(codec, NULL); return 1; } @@ -4610,7 +4594,7 @@ static void init_input_src(struct hda_codec *codec) update_shared_mic_hp(codec, spec->cur_mux[0]); if (spec->cap_sync_hook) - spec->cap_sync_hook(codec); + spec->cap_sync_hook(codec, NULL); } /* set right pin controls for digital I/O */ diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h index f6b88cd4584f..594a9cc4ecca 100644 --- a/sound/pci/hda/hda_generic.h +++ b/sound/pci/hda/hda_generic.h @@ -233,7 +233,8 @@ struct hda_gen_spec { /* hooks */ void (*init_hook)(struct hda_codec *codec); void (*automute_hook)(struct hda_codec *codec); - void (*cap_sync_hook)(struct hda_codec *codec); + void (*cap_sync_hook)(struct hda_codec *codec, + struct snd_ctl_elem_value *ucontrol); /* PCM hooks */ void (*pcm_playback_hook)(struct hda_pcm_stream *hinfo, @@ -252,9 +253,6 @@ struct hda_gen_spec { struct hda_jack_tbl *tbl); void (*mic_autoswitch_hook)(struct hda_codec *codec, struct hda_jack_tbl *tbl); - - /* capture switch hook (for mic-mute LED) */ - void (*capture_switch_hook)(struct hda_codec *codec, bool enable); }; int snd_hda_gen_spec_init(struct hda_gen_spec *spec); diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 71a8894438ab..70b0e323f5af 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -670,7 +670,8 @@ static void alc_inv_dmic_sync(struct hda_codec *codec, bool force) } } -static void alc_inv_dmic_hook(struct hda_codec *codec) +static void alc_inv_dmic_hook(struct hda_codec *codec, + struct snd_ctl_elem_value *ucontrol) { alc_inv_dmic_sync(codec, false); } diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 9d2dfad7f0bc..456ebc771313 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -316,11 +316,17 @@ static void stac_gpio_set(struct hda_codec *codec, unsigned int mask, } /* hook for controlling mic-mute LED GPIO */ -static void stac_capture_led_hook(struct hda_codec *codec, bool enable) +static void stac_capture_led_hook(struct hda_codec *codec, + struct snd_ctl_elem_value *ucontrol) { struct sigmatel_spec *spec = codec->spec; - bool mute = !enable; + bool mute; + if (!ucontrol) + return; + + mute = !(ucontrol->value.integer.value[0] || + ucontrol->value.integer.value[1]); if (spec->mic_mute_led_on != mute) { spec->mic_mute_led_on = mute; if (mute) @@ -3806,7 +3812,7 @@ static void stac_setup_gpio(struct hda_codec *codec) spec->mic_mute_led_on = true; spec->gpio_data |= spec->mic_mute_led_gpio; - spec->gen.capture_switch_hook = stac_capture_led_hook; + spec->gen.cap_sync_hook = stac_capture_led_hook; } } -- cgit v1.2.3 From 49920427eca5830eb65ea1be241090e425de37cb Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 18 Jan 2013 14:28:07 +0100 Subject: ALSA: hda/sigmatel - Add bass speaker support for HP ENVY Spectre XT The pin configuration for the bass speaker needs to be corrected in a fixup. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_sigmatel.c | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 456ebc771313..fe3e08243d76 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -100,6 +100,7 @@ enum { STAC_92HD83XXX_HP_MIC_LED, STAC_92HD83XXX_HEADSET_JACK, STAC_92HD83XXX_HP, + STAC_HP_ENVY_BASS, STAC_92HD83XXX_MODELS }; @@ -2048,6 +2049,13 @@ static const struct hda_fixup stac92hd83xxx_fixups[] = { .type = HDA_FIXUP_FUNC, .v.func = stac92hd83xxx_fixup_headset_jack, }, + [STAC_HP_ENVY_BASS] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x0f, 0x90170111 }, + {} + }, + }, }; static const struct hda_model_fixup stac92hd83xxx_models[] = { @@ -2062,6 +2070,7 @@ static const struct hda_model_fixup stac92hd83xxx_models[] = { { .id = STAC_92HD83XXX_HP_INV_LED, .name = "hp-inv-led" }, { .id = STAC_92HD83XXX_HP_MIC_LED, .name = "hp-mic-led" }, { .id = STAC_92HD83XXX_HEADSET_JACK, .name = "headset-jack" }, + { .id = STAC_HP_ENVY_BASS, .name = "hp-envy-bass" }, {} }; @@ -2105,6 +2114,8 @@ static const struct snd_pci_quirk stac92hd83xxx_fixup_tbl[] = { "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD), SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x165B, "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD), + SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1888, + "HP Envy Spectre", STAC_HP_ENVY_BASS), SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x18df, "HP Folio", STAC_92HD83XXX_HP_MIC_LED), SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3388, -- cgit v1.2.3 From 1799cdd51adeca201625542faeef19ca7a74b2d3 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 18 Jan 2013 14:37:16 +0100 Subject: ALSA: hda - Add boost to line inputs, too Although I commented that boost volumes would be added only for line-in and mic pins in the source code, the actual code excludes but for mic-in. Fix it to accept the line-ins, too. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index f0d83b2f42ac..174806e52236 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -3138,7 +3138,7 @@ static int parse_mic_boost(struct hda_codec *codec) continue; /* check only line-in and mic pins */ - if (cfg->inputs[idx].type > AUTO_PIN_MIC) + if (cfg->inputs[idx].type > AUTO_PIN_LINE_IN) continue; path = get_input_path(codec, 0, i); -- cgit v1.2.3 From 7513e6dae58cf6583ff38bfe684bd5b3afcc564e Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 18 Jan 2013 15:41:34 +0100 Subject: ALSA: hda - Fix speaker pin of FSC Lifebook S7110 laptop Some BIOS version of FSC Lifebook S7110 laptop seems to give a wrong default pin config for NID 0x15, which confuses the parser. Give a fixup to correct the value. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 70b0e323f5af..79ff34df887a 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -2151,6 +2151,7 @@ static int alc262_parse_auto_config(struct hda_codec *codec) */ enum { ALC262_FIXUP_FSC_H270, + ALC262_FIXUP_FSC_S7110, ALC262_FIXUP_HP_Z200, ALC262_FIXUP_TYAN, ALC262_FIXUP_LENOVO_3000, @@ -2169,6 +2170,15 @@ static const struct hda_fixup alc262_fixups[] = { { } } }, + [ALC262_FIXUP_FSC_S7110] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x15, 0x90170110 }, /* speaker */ + { } + }, + .chained = true, + .chain_id = ALC262_FIXUP_BENQ, + }, [ALC262_FIXUP_HP_Z200] = { .type = HDA_FIXUP_PINS, .v.pins = (const struct hda_pintbl[]) { @@ -2216,7 +2226,7 @@ static const struct hda_fixup alc262_fixups[] = { static const struct snd_pci_quirk alc262_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x170b, "HP Z200", ALC262_FIXUP_HP_Z200), - SND_PCI_QUIRK(0x10cf, 0x1397, "Fujitsu", ALC262_FIXUP_BENQ), + SND_PCI_QUIRK(0x10cf, 0x1397, "Fujitsu Lifebook S7110", ALC262_FIXUP_FSC_S7110), SND_PCI_QUIRK(0x10cf, 0x142d, "Fujitsu Lifebook E8410", ALC262_FIXUP_BENQ), SND_PCI_QUIRK(0x10f1, 0x2915, "Tyan Thunder n6650W", ALC262_FIXUP_TYAN), SND_PCI_QUIRK(0x1734, 0x1147, "FSC Celsius H270", ALC262_FIXUP_FSC_H270), -- cgit v1.2.3 From d3d982f7447ba9f5cbb57c1a525c3b61bfcffc37 Mon Sep 17 00:00:00 2001 From: David Henningsson Date: Fri, 18 Jan 2013 15:43:01 +0100 Subject: ALSA: hda - make sure there are enough input labels and paths I found a codec configuration which had six inputs, so the max of five was not appropriate. Signed-off-by: David Henningsson Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 2 +- sound/pci/hda/hda_generic.h | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 174806e52236..10b14a925ddb 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -2652,7 +2652,7 @@ static struct nid_path *get_input_path(struct hda_codec *codec, int adc_idx, int } if (spec->dyn_adc_switch) adc_idx = spec->dyn_adc_idx[imux_idx]; - if (adc_idx < 0 || adc_idx >= AUTO_CFG_MAX_OUTS) { + if (adc_idx < 0 || adc_idx >= AUTO_CFG_MAX_INS) { snd_BUG(); return NULL; } diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h index 594a9cc4ecca..eacfca93215a 100644 --- a/sound/pci/hda/hda_generic.h +++ b/sound/pci/hda/hda_generic.h @@ -105,8 +105,8 @@ struct hda_gen_spec { hda_nid_t adc_nids[AUTO_CFG_MAX_OUTS]; hda_nid_t dig_in_nid; /* digital-in NID; optional */ hda_nid_t mixer_nid; /* analog-mixer NID */ - const char *input_labels[AUTO_CFG_MAX_OUTS]; - int input_label_idxs[AUTO_CFG_MAX_OUTS]; + const char *input_labels[AUTO_CFG_MAX_INS]; + int input_label_idxs[AUTO_CFG_MAX_INS]; /* capture setup for dynamic dual-adc switch */ hda_nid_t cur_adc; @@ -159,7 +159,7 @@ struct hda_gen_spec { int speaker_paths[AUTO_CFG_MAX_OUTS]; int aamix_out_paths[3]; int digout_paths[AUTO_CFG_MAX_OUTS]; - int input_paths[HDA_MAX_NUM_INPUTS][AUTO_CFG_MAX_OUTS]; + int input_paths[HDA_MAX_NUM_INPUTS][AUTO_CFG_MAX_INS]; int loopback_paths[HDA_MAX_NUM_INPUTS]; int digin_path; -- cgit v1.2.3 From 8e8db7f123dcc4d56464fc584667c8f9ae9f4aef Mon Sep 17 00:00:00 2001 From: David Henningsson Date: Fri, 18 Jan 2013 15:43:02 +0100 Subject: ALSA: hda - don't compare with yourself in fill_input_pin_labels Just stumbled over this one while reading the code. Signed-off-by: David Henningsson Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 10b14a925ddb..e4e71fa8d87c 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -2558,7 +2558,7 @@ static int fill_input_pin_labels(struct hda_codec *codec) label = hda_get_autocfg_input_label(codec, cfg, i); idx = 0; - for (j = i; j >= 0; j--) { + for (j = i - 1; j >= 0; j--) { if (spec->input_labels[j] && !strcmp(spec->input_labels[j], label)) { idx = spec->input_label_idxs[j] + 1; -- cgit v1.2.3 From 3f25dcf691ebf45924a34b9aaedec78e5a255798 Mon Sep 17 00:00:00 2001 From: David Henningsson Date: Fri, 18 Jan 2013 15:43:03 +0100 Subject: ALSA: hda - Don't add unnecessary indices on HDMI and SPDIF If there's one each of HDMI and SPDIF, we should not add an index on the one that comes second. [slight code refactoring by tiwai] Signed-off-by: David Henningsson Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_auto_parser.c | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_auto_parser.c b/sound/pci/hda/hda_auto_parser.c index a4810c7437bd..0088bb0b04de 100644 --- a/sound/pci/hda/hda_auto_parser.c +++ b/sound/pci/hda/hda_auto_parser.c @@ -583,6 +583,9 @@ static int fill_audio_out_name(struct hda_codec *codec, hda_nid_t nid, return 1; } +#define is_hdmi_cfg(conf) \ + (get_defcfg_location(conf) == AC_JACK_LOC_HDMI) + /** * snd_hda_get_pin_label - Get a label for the given I/O pin * @@ -603,6 +606,7 @@ int snd_hda_get_pin_label(struct hda_codec *codec, hda_nid_t nid, unsigned int def_conf = snd_hda_codec_get_pincfg(codec, nid); const char *name = NULL; int i; + bool hdmi; if (indexp) *indexp = 0; @@ -621,16 +625,18 @@ int snd_hda_get_pin_label(struct hda_codec *codec, hda_nid_t nid, label, maxlen, indexp); case AC_JACK_SPDIF_OUT: case AC_JACK_DIG_OTHER_OUT: - if (get_defcfg_location(def_conf) == AC_JACK_LOC_HDMI) - name = "HDMI"; - else - name = "SPDIF"; - if (cfg && indexp) { - i = find_idx_in_nid_list(nid, cfg->dig_out_pins, - cfg->dig_outs); - if (i >= 0) - *indexp = i; - } + hdmi = is_hdmi_cfg(def_conf); + name = hdmi ? "HDMI" : "SPDIF"; + if (cfg && indexp) + for (i = 0; i < cfg->dig_outs; i++) { + hda_nid_t pin = cfg->dig_out_pins[i]; + unsigned int c; + if (pin == nid) + break; + c = snd_hda_codec_get_pincfg(codec, pin); + if (hdmi == is_hdmi_cfg(c)) + (*indexp)++; + } break; default: if (cfg) { -- cgit v1.2.3 From 2a8d53916b9cea3eac615f0bb1e7fabec3d5c026 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 18 Jan 2013 16:23:25 +0100 Subject: ALSA: hda - Fix the wrong adc_idx for capture source The patch "ALSA: hda - fix wrong adc_idx in generic parser" fixed the adc_idx for the capture volume and capture switch controls. But also modified the adc_idx retrieval for the capture source controls wrongly. As multiple capture source controls are created in a single shot with counts > 1, the id.index doesn't contain the real value. The real index has to be taken via snd_ctl_get_ioffidx() as in the original code. This patch reverts the fixes partially to recover from the regression. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index e4e71fa8d87c..29f37c90225d 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -2675,7 +2675,8 @@ static int mux_enum_get(struct snd_kcontrol *kcontrol, { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct hda_gen_spec *spec = codec->spec; - unsigned int adc_idx = kcontrol->id.index; + /* the ctls are created at once with multiple counts */ + unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx]; return 0; @@ -2685,7 +2686,7 @@ static int mux_enum_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - unsigned int adc_idx = kcontrol->id.index; + unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); return mux_select(codec, adc_idx, ucontrol->value.enumerated.item[0]); } -- cgit v1.2.3 From cf799aa300b38f86684944bf9f61f7a13277f8b2 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 18 Jan 2013 16:38:08 +0100 Subject: ALSA: hda - Correct more array rooms in hda_gen_spec Looking through the whole definitions, some fields have inappropriate array sizes, especially about the capture. The array assigned to each input (pin) should have HDA_MAX_NUM_INPUTS entries while the array assigned to each ADC should have AUTO_CFG_MAX_INS entries. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h index eacfca93215a..696b6068a889 100644 --- a/sound/pci/hda/hda_generic.h +++ b/sound/pci/hda/hda_generic.h @@ -102,11 +102,11 @@ struct hda_gen_spec { /* capture */ unsigned int num_adc_nids; - hda_nid_t adc_nids[AUTO_CFG_MAX_OUTS]; + hda_nid_t adc_nids[AUTO_CFG_MAX_INS]; hda_nid_t dig_in_nid; /* digital-in NID; optional */ hda_nid_t mixer_nid; /* analog-mixer NID */ - const char *input_labels[AUTO_CFG_MAX_INS]; - int input_label_idxs[AUTO_CFG_MAX_INS]; + const char *input_labels[HDA_MAX_NUM_INPUTS]; + int input_label_idxs[HDA_MAX_NUM_INPUTS]; /* capture setup for dynamic dual-adc switch */ hda_nid_t cur_adc; @@ -148,7 +148,7 @@ struct hda_gen_spec { int num_all_dacs; hda_nid_t all_dacs[16]; int num_all_adcs; - hda_nid_t all_adcs[AUTO_CFG_MAX_OUTS]; + hda_nid_t all_adcs[AUTO_CFG_MAX_INS]; /* path list */ struct snd_array paths; -- cgit v1.2.3 From 164a7adac9b4fbd2a4838eff3597d27eb010c787 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 18 Jan 2013 18:25:45 +0100 Subject: ALSA: hda/conexant - Set mixer NID 0x19 for CX20551 codec Conexant CX20551 codec has a mixer in NID 0x19 and a few outputs have to take the input through this widget. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_conexant.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index 2f94acb16bde..2e6e2b08b5e9 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -3197,6 +3197,9 @@ static const struct hda_codec_ops cx_auto_patch_ops = { .init = snd_hda_gen_init, .free = snd_hda_gen_free, .unsol_event = snd_hda_jack_unsol_event, +#ifdef CONFIG_PM + .check_power_status = snd_hda_gen_check_power_status, +#endif }; /* @@ -3348,6 +3351,10 @@ static int patch_conexant_auto(struct hda_codec *codec) case 0x14f15045: codec->single_adc_amp = 1; break; + case 0x14f15047: + codec->pin_amp_workaround = 1; + spec->gen.mixer_nid = 0x19; + break; case 0x14f15051: add_cx5051_fake_mutes(codec); codec->pin_amp_workaround = 1; -- cgit v1.2.3 From a769409cf325b697c439acef5d7c0dc4b6a591ba Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 21 Jan 2013 10:43:18 +0100 Subject: ALSA: hda - Improve debug prints for output paths Print the information of outputs in a bit more details and concisely in a single place instead of printing the path at each time when detected. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 57 ++++++++++++++++++++++++++++++++++++--------- 1 file changed, 46 insertions(+), 11 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 29f37c90225d..37d7ed7af2a5 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -1113,7 +1113,7 @@ static int try_assign_dacs(struct hda_codec *codec, int num_outs, if (!path) dac = dacs[i] = 0; else { - print_nid_path("output", path); + /* print_nid_path("output", path); */ path->active = true; path_idx[i] = snd_hda_get_path_idx(codec, path); badness += assign_out_path_ctls(codec, path); @@ -1240,7 +1240,7 @@ static int fill_multi_ios(struct hda_codec *codec, badness++; continue; } - print_nid_path("multiio", path); + /* print_nid_path("multiio", path); */ spec->multi_io[spec->multi_ios].pin = nid; spec->multi_io[spec->multi_ios].dac = dac; spec->out_paths[cfg->line_outs + spec->multi_ios] = @@ -1297,7 +1297,7 @@ static bool map_singles(struct hda_codec *codec, int outs, if (path) { dacs[i] = dac; found = true; - print_nid_path("output", path); + /* print_nid_path("output", path); */ path->active = true; path_idx[i] = snd_hda_get_path_idx(codec, path); } @@ -1320,7 +1320,7 @@ static int check_aamix_out_path(struct hda_codec *codec, int path_idx) spec->mixer_nid); if (!path) return 0; - print_nid_path("output-aamix", path); + /* print_nid_path("output-aamix", path); */ path->active = false; /* unused as default */ return snd_hda_get_path_idx(codec, path); } @@ -1514,35 +1514,70 @@ static int fill_and_eval_dacs(struct hda_codec *codec, #define debug_badness(...) #endif -static void debug_show_configs(struct hda_gen_spec *spec, struct auto_pin_cfg *cfg) +#ifdef DEBUG_BADNESS +static inline void print_nid_path_idx(struct hda_codec *codec, + const char *pfx, int idx) +{ + struct nid_path *path; + + path = snd_hda_get_path_from_idx(codec, idx); + if (path) + print_nid_path(pfx, path); +} + +static void debug_show_configs(struct hda_codec *codec, + struct auto_pin_cfg *cfg) { - debug_badness("multi_outs = %x/%x/%x/%x : %x/%x/%x/%x\n", + struct hda_gen_spec *spec = codec->spec; +#ifdef CONFIG_SND_DEBUG_VERBOSE + static const char * const lo_type[3] = { "LO", "SP", "HP" }; +#endif + int i; + + debug_badness("multi_outs = %x/%x/%x/%x : %x/%x/%x/%x (type %s)\n", cfg->line_out_pins[0], cfg->line_out_pins[1], cfg->line_out_pins[2], cfg->line_out_pins[3], spec->multiout.dac_nids[0], spec->multiout.dac_nids[1], spec->multiout.dac_nids[2], - spec->multiout.dac_nids[3]); + spec->multiout.dac_nids[3], + lo_type[cfg->line_out_type]); + for (i = 0; i < cfg->line_outs; i++) + print_nid_path_idx(codec, " out", spec->out_paths[i]); if (spec->multi_ios > 0) debug_badness("multi_ios(%d) = %x/%x : %x/%x\n", spec->multi_ios, spec->multi_io[0].pin, spec->multi_io[1].pin, spec->multi_io[0].dac, spec->multi_io[1].dac); - debug_badness("hp_outs = %x/%x/%x/%x : %x/%x/%x/%x\n", + for (i = 0; i < spec->multi_ios; i++) + print_nid_path_idx(codec, " mio", + spec->out_paths[cfg->line_outs + i]); + if (cfg->hp_outs) + debug_badness("hp_outs = %x/%x/%x/%x : %x/%x/%x/%x\n", cfg->hp_pins[0], cfg->hp_pins[1], cfg->hp_pins[2], cfg->hp_pins[3], spec->multiout.hp_out_nid[0], spec->multiout.hp_out_nid[1], spec->multiout.hp_out_nid[2], spec->multiout.hp_out_nid[3]); - debug_badness("spk_outs = %x/%x/%x/%x : %x/%x/%x/%x\n", + for (i = 0; i < cfg->hp_outs; i++) + print_nid_path_idx(codec, " hp ", spec->hp_paths[i]); + if (cfg->speaker_outs) + debug_badness("spk_outs = %x/%x/%x/%x : %x/%x/%x/%x\n", cfg->speaker_pins[0], cfg->speaker_pins[1], cfg->speaker_pins[2], cfg->speaker_pins[3], spec->multiout.extra_out_nid[0], spec->multiout.extra_out_nid[1], spec->multiout.extra_out_nid[2], spec->multiout.extra_out_nid[3]); + for (i = 0; i < cfg->speaker_outs; i++) + print_nid_path_idx(codec, " spk", spec->speaker_paths[i]); + for (i = 0; i < 3; i++) + print_nid_path_idx(codec, " mix", spec->aamix_out_paths[i]); } +#else +#define debug_show_configs(codec, cfg) /* NOP */ +#endif /* find all available DACs of the codec */ static void fill_all_dac_nids(struct hda_codec *codec) @@ -1590,7 +1625,7 @@ static int parse_output_paths(struct hda_codec *codec) debug_badness("==> lo_type=%d, wired=%d, mio=%d, badness=0x%x\n", cfg->line_out_type, fill_hardwired, fill_mio_first, badness); - debug_show_configs(spec, cfg); + debug_show_configs(codec, cfg); if (badness < best_badness) { best_badness = badness; *best_cfg = *cfg; @@ -1646,7 +1681,7 @@ static int parse_output_paths(struct hda_codec *codec) } debug_badness("==> Best config: lo_type=%d, wired=%d, mio=%d\n", cfg->line_out_type, best_wired, best_mio); - debug_show_configs(spec, cfg); + debug_show_configs(codec, cfg); if (cfg->line_out_pins[0]) { struct nid_path *path; -- cgit v1.2.3 From 9314a5813f62e85c8173adf7fd7e088af3b58942 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 21 Jan 2013 10:49:05 +0100 Subject: ALSA: hda - Set the pin targets after deciding output config Since fill_and_eval_dacs() may be called repeatedly with different configurations, setting pinctls at each time there isn't optimal. We can set it better only once after deciding the output configuration in parse_output_paths(). Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 37d7ed7af2a5..7b739b589818 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -1352,7 +1352,6 @@ static int fill_and_eval_dacs(struct hda_codec *codec, struct hda_gen_spec *spec = codec->spec; struct auto_pin_cfg *cfg = &spec->autocfg; int i, err, badness; - unsigned int val; /* set num_dacs once to full for look_for_dac() */ spec->multiout.num_dacs = cfg->line_outs; @@ -1489,20 +1488,6 @@ static int fill_and_eval_dacs(struct hda_codec *codec, spec->multiout.extra_out_nid, spec->speaker_paths); - /* set initial pinctl targets */ - if (spec->prefer_hp_amp || cfg->line_out_type == AUTO_PIN_HP_OUT) - val = PIN_HP; - else - val = PIN_OUT; - set_pin_targets(codec, cfg->line_outs, cfg->line_out_pins, val); - if (cfg->line_out_type != AUTO_PIN_HP_OUT) - set_pin_targets(codec, cfg->hp_outs, cfg->hp_pins, PIN_HP); - if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) { - val = spec->prefer_hp_amp ? PIN_HP : PIN_OUT; - set_pin_targets(codec, cfg->speaker_outs, - cfg->speaker_pins, val); - } - return badness; } @@ -1604,6 +1589,7 @@ static int parse_output_paths(struct hda_codec *codec) struct hda_gen_spec *spec = codec->spec; struct auto_pin_cfg *cfg = &spec->autocfg; struct auto_pin_cfg *best_cfg; + unsigned int val; int best_badness = INT_MAX; int badness; bool fill_hardwired = true, fill_mio_first = true; @@ -1693,6 +1679,20 @@ static int parse_output_paths(struct hda_codec *codec) HDA_OUTPUT, spec->vmaster_tlv); } + /* set initial pinctl targets */ + if (spec->prefer_hp_amp || cfg->line_out_type == AUTO_PIN_HP_OUT) + val = PIN_HP; + else + val = PIN_OUT; + set_pin_targets(codec, cfg->line_outs, cfg->line_out_pins, val); + if (cfg->line_out_type != AUTO_PIN_HP_OUT) + set_pin_targets(codec, cfg->hp_outs, cfg->hp_pins, PIN_HP); + if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) { + val = spec->prefer_hp_amp ? PIN_HP : PIN_OUT; + set_pin_targets(codec, cfg->speaker_outs, + cfg->speaker_pins, val); + } + kfree(best_cfg); return 0; } -- cgit v1.2.3 From 1fa335b0b797811d66a5f88373edd523f947cce4 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 21 Jan 2013 11:43:19 +0100 Subject: ALSA: hda - Add missing badness evaluation for unresolved paths When a patch couldn't be resolved in try_assign_dacs() although the target DAC is expected, we forgot to add a proper badness value but continued. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 7b739b589818..4e9761a91816 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -1105,14 +1105,17 @@ static int try_assign_dacs(struct hda_codec *codec, int num_outs, else badness += bad->no_dac; } + if (!dac) + continue; path = snd_hda_add_new_path(codec, dac, pin, -spec->mixer_nid); if (!path && !i && spec->mixer_nid) { /* try with aamix */ path = snd_hda_add_new_path(codec, dac, pin, 0); } - if (!path) + if (!path) { dac = dacs[i] = 0; - else { + badness += bad->no_dac; + } else { /* print_nid_path("output", path); */ path->active = true; path_idx[i] = snd_hda_get_path_idx(codec, path); -- cgit v1.2.3 From f87498b65197f951899d8bbd99e5553227c41ec9 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 21 Jan 2013 14:24:31 +0100 Subject: ALSA: hda - Check aamix-output paths from other DACs, too Many codecs provide routes to multiple output pins through an aamix widget, but most of them do it only from a single DAC. However, the current generic parser checks only the aamix paths from the original (directly bound) DACs through aamix NID, and miss the path: primary DAC -> aamix -> target out pin This patch adds a more check for the routes like the above. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 4e9761a91816..e26e8d3430f2 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -1313,14 +1313,26 @@ static int check_aamix_out_path(struct hda_codec *codec, int path_idx) { struct hda_gen_spec *spec = codec->spec; struct nid_path *path; + hda_nid_t dac, pin; path = snd_hda_get_path_from_idx(codec, path_idx); if (!path || !path->depth || is_nid_contained(path, spec->mixer_nid)) return 0; - path = snd_hda_add_new_path(codec, path->path[0], - path->path[path->depth - 1], - spec->mixer_nid); + dac = path->path[0]; + pin = path->path[path->depth - 1]; + path = snd_hda_add_new_path(codec, dac, pin, spec->mixer_nid); + if (!path) { + if (dac != spec->multiout.dac_nids[0]) + dac = spec->multiout.dac_nids[0]; + else if (spec->multiout.hp_out_nid[0]) + dac = spec->multiout.hp_out_nid[0]; + else if (spec->multiout.extra_out_nid[0]) + dac = spec->multiout.extra_out_nid[0]; + if (dac) + path = snd_hda_add_new_path(codec, dac, pin, + spec->mixer_nid); + } if (!path) return 0; /* print_nid_path("output-aamix", path); */ -- cgit v1.2.3 From a1e908edccd1b6928cda78371026b458e2c1973a Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 21 Jan 2013 15:11:25 +0100 Subject: ALSA: hda - Fix conflicts between Loopback Mixing and Independent HP This patch eventually fixes two issues: - Handle the case where the primary output is a headphone and can have independent HP mode; so far we checked only the case where the headphone is the secondary output. - Fix the conflict of HP independent mode and aamix mode; when switched to aamix mode, the DAC might be also switched to another widget shared with other outputs. Then even if we disable the DAC for the original output, it doesn't change -- because the active route is from another (shared) DAC to HP pin through aamix. So, in such a case, we have to prohibit the switch to aamix for HP routes. This fixes issues appearing on VT codecs. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 59 +++++++++++++++++++++++++++++++++++++++------ sound/pci/hda/hda_generic.h | 1 + 2 files changed, 52 insertions(+), 8 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index e26e8d3430f2..6d1e843c6e8d 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -1845,6 +1845,10 @@ static int indep_hp_get(struct snd_kcontrol *kcontrol, return 0; } +static void update_aamix_paths(struct hda_codec *codec, bool do_mix, + int nomix_path_idx, int mix_path_idx, + int out_type); + static int indep_hp_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -1860,11 +1864,31 @@ static int indep_hp_put(struct snd_kcontrol *kcontrol, } if (spec->indep_hp_enabled != select) { + hda_nid_t *dacp; + if (spec->autocfg.line_out_type == AUTO_PIN_HP_OUT) + dacp = &spec->private_dac_nids[0]; + else + dacp = &spec->multiout.hp_out_nid[0]; + + /* update HP aamix paths in case it conflicts with indep HP */ + if (spec->have_aamix_ctl) { + if (spec->autocfg.line_out_type == AUTO_PIN_HP_OUT) + update_aamix_paths(codec, spec->aamix_mode, + spec->out_paths[0], + spec->aamix_out_paths[0], + spec->autocfg.line_out_type); + else + update_aamix_paths(codec, spec->aamix_mode, + spec->hp_paths[0], + spec->aamix_out_paths[1], + AUTO_PIN_HP_OUT); + } + spec->indep_hp_enabled = select; if (spec->indep_hp_enabled) - spec->multiout.hp_out_nid[0] = 0; + *dacp = 0; else - spec->multiout.hp_out_nid[0] = spec->alt_dac_nid; + *dacp = spec->alt_dac_nid; ret = 1; } unlock: @@ -1884,16 +1908,21 @@ static const struct snd_kcontrol_new indep_hp_ctl = { static int create_indep_hp_ctls(struct hda_codec *codec) { struct hda_gen_spec *spec = codec->spec; + hda_nid_t dac; if (!spec->indep_hp) return 0; - if (!spec->multiout.hp_out_nid[0]) { + if (spec->autocfg.line_out_type == AUTO_PIN_HP_OUT) + dac = spec->multiout.dac_nids[0]; + else + dac = spec->multiout.hp_out_nid[0]; + if (!dac) { spec->indep_hp = 0; return 0; } spec->indep_hp_enabled = false; - spec->alt_dac_nid = spec->multiout.hp_out_nid[0]; + spec->alt_dac_nid = dac; if (!snd_hda_gen_add_kctl(spec, NULL, &indep_hp_ctl)) return -ENOMEM; return 0; @@ -2026,14 +2055,24 @@ static int loopback_mixing_get(struct snd_kcontrol *kcontrol, } static void update_aamix_paths(struct hda_codec *codec, bool do_mix, - int nomix_path_idx, int mix_path_idx) + int nomix_path_idx, int mix_path_idx, + int out_type) { + struct hda_gen_spec *spec = codec->spec; struct nid_path *nomix_path, *mix_path; nomix_path = snd_hda_get_path_from_idx(codec, nomix_path_idx); mix_path = snd_hda_get_path_from_idx(codec, mix_path_idx); if (!nomix_path || !mix_path) return; + + /* if HP aamix path is driven from a different DAC and the + * independent HP mode is ON, can't turn on aamix path + */ + if (out_type == AUTO_PIN_HP_OUT && spec->indep_hp_enabled && + mix_path->path[0] != spec->alt_dac_nid) + do_mix = false; + if (do_mix) { snd_hda_activate_path(codec, nomix_path, false, true); snd_hda_activate_path(codec, mix_path, true, true); @@ -2054,11 +2093,14 @@ static int loopback_mixing_put(struct snd_kcontrol *kcontrol, return 0; spec->aamix_mode = val; update_aamix_paths(codec, val, spec->out_paths[0], - spec->aamix_out_paths[0]); + spec->aamix_out_paths[0], + spec->autocfg.line_out_type); update_aamix_paths(codec, val, spec->hp_paths[0], - spec->aamix_out_paths[1]); + spec->aamix_out_paths[1], + AUTO_PIN_HP_OUT); update_aamix_paths(codec, val, spec->speaker_paths[0], - spec->aamix_out_paths[2]); + spec->aamix_out_paths[2], + AUTO_PIN_SPEAKER_OUT); return 1; } @@ -2081,6 +2123,7 @@ static int create_loopback_mixing_ctl(struct hda_codec *codec) return 0; if (!snd_hda_gen_add_kctl(spec, NULL, &loopback_mixing_enum)) return -ENOMEM; + spec->have_aamix_ctl = 1; return 0; } diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h index 696b6068a889..9c63555b971c 100644 --- a/sound/pci/hda/hda_generic.h +++ b/sound/pci/hda/hda_generic.h @@ -212,6 +212,7 @@ struct hda_gen_spec { unsigned int no_analog:1; /* digital I/O only */ unsigned int dyn_adc_switch:1; /* switch ADCs (for ALC275) */ unsigned int indep_hp_enabled:1; /* independent HP enabled */ + unsigned int have_aamix_ctl:1; /* loopback mixing mode */ bool aamix_mode; -- cgit v1.2.3 From 139611705ad5ce7b35b8b7957c5ca406deb3ff9b Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 21 Jan 2013 15:15:55 +0100 Subject: ALSA: hda - Enable parsing the independent HP mode as default for VIA codecs The original VIA codec parser enabled it as default, so let's keep the behavior as it was. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_via.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index eade21c3e0b1..9d9583ca5f5b 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -135,6 +135,7 @@ static struct via_spec *via_new_spec(struct hda_codec *codec) if (spec->codec_type == VT1708BCE) spec->codec_type = VT1708S; spec->no_pin_power_ctl = 1; + spec->gen.indep_hp = 1; spec->gen.pcm_playback_hook = via_playback_pcm_hook; return spec; } -- cgit v1.2.3 From 6efcc52653676fde888bf7837d01468d4f846465 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 21 Jan 2013 16:10:56 +0100 Subject: ALSA: hda - Remove superfluous header inclusions Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_analog.c | 1 - sound/pci/hda/patch_ca0110.c | 1 - sound/pci/hda/patch_cirrus.c | 1 - sound/pci/hda/patch_cmedia.c | 1 - sound/pci/hda/patch_realtek.c | 1 - sound/pci/hda/patch_sigmatel.c | 1 - 6 files changed, 6 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index 6feaec4cd6b9..162bc2f38450 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c @@ -20,7 +20,6 @@ */ #include -#include #include #include #include diff --git a/sound/pci/hda/patch_ca0110.c b/sound/pci/hda/patch_ca0110.c index db7635cd8255..30b3a4bc06ee 100644 --- a/sound/pci/hda/patch_ca0110.c +++ b/sound/pci/hda/patch_ca0110.c @@ -19,7 +19,6 @@ */ #include -#include #include #include #include diff --git a/sound/pci/hda/patch_cirrus.c b/sound/pci/hda/patch_cirrus.c index b9dfbd85d550..72ebb8a36b13 100644 --- a/sound/pci/hda/patch_cirrus.c +++ b/sound/pci/hda/patch_cirrus.c @@ -19,7 +19,6 @@ */ #include -#include #include #include #include diff --git a/sound/pci/hda/patch_cmedia.c b/sound/pci/hda/patch_cmedia.c index 087cabbcf25c..9c6ce73b03c5 100644 --- a/sound/pci/hda/patch_cmedia.c +++ b/sound/pci/hda/patch_cmedia.c @@ -22,7 +22,6 @@ */ #include -#include #include #include #include diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 79ff34df887a..6eb9551e9723 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -29,7 +29,6 @@ #include #include #include -#include #include #include #include "hda_codec.h" diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index fe3e08243d76..fd29f4918879 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -31,7 +31,6 @@ #include #include #include -#include #include #include #include "hda_codec.h" -- cgit v1.2.3 From 2748746f40da674cd6ba405fd3ef83e12a94b8ed Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 21 Jan 2013 16:25:18 +0100 Subject: ALSA: hda - Add aamix NID to IDT 92HD codecs IDT codecs have analog-loopback mixer widgets, but we haven't cared about it, so far. Let's set them. This will avoid also possible wrong routes for the input paths. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_sigmatel.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index fd29f4918879..c53b6f9f2ca8 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -3743,6 +3743,7 @@ static int patch_stac92hd73xx(struct hda_codec *codec) spec = codec->spec; spec->linear_tone_beep = 0; + spec->gen.mixer_nid = 0x1d; num_dacs = snd_hda_get_num_conns(codec, 0x0a) - 1; if (num_dacs < 3 || num_dacs > 5) { @@ -3840,6 +3841,7 @@ static int patch_stac92hd83xxx(struct hda_codec *codec) spec = codec->spec; spec->linear_tone_beep = 0; spec->gen.own_eapd_ctl = 1; + spec->gen.mixer_nid = 0x1b; spec->digbeep_nid = 0x21; spec->pwr_nids = stac92hd83xxx_pwr_nids; @@ -3882,6 +3884,7 @@ static int patch_stac92hd71bxx(struct hda_codec *codec) spec = codec->spec; spec->linear_tone_beep = 0; spec->gen.own_eapd_ctl = 1; + spec->gen.mixer_nid = 0x17; codec->patch_ops = stac_patch_ops; -- cgit v1.2.3 From f2f8be43c5c92355feea2ec332375ece00bc0ff9 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 21 Jan 2013 16:40:16 +0100 Subject: ALSA: hda - Add aamix NID to AD codecs The aamix NIDs are also missing for AD codecs. All AD codecs seem to have a (more or less) working aamix widget. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_analog.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index 162bc2f38450..a186b3da20b7 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c @@ -1187,6 +1187,7 @@ static int ad1986a_parse_auto_config(struct hda_codec *codec) /* AD1986A has the inverted EAPD implementation */ codec->inv_eapd = 1; + spec->gen.mixer_nid = 0x07; spec->beep_dev_nid = 0x19; set_beep_amp(spec, 0x18, 0, HDA_OUTPUT); @@ -1950,6 +1951,7 @@ static int ad1981_parse_auto_config(struct hda_codec *codec) { struct ad198x_spec *spec = codec->spec; + spec->gen.mixer_nid = 0x0e; spec->beep_dev_nid = 0x10; set_beep_amp(spec, 0x0d, 0, HDA_OUTPUT); return ad198x_parse_auto_config(codec); @@ -2825,6 +2827,7 @@ static int ad1988_parse_auto_config(struct hda_codec *codec) { struct ad198x_spec *spec = codec->spec; + spec->gen.mixer_nid = 0x20; spec->beep_dev_nid = 0x10; set_beep_amp(spec, 0x10, 0, HDA_OUTPUT); return ad198x_parse_auto_config(codec); @@ -3172,6 +3175,7 @@ static int ad1884_parse_auto_config(struct hda_codec *codec) { struct ad198x_spec *spec = codec->spec; + spec->gen.mixer_nid = 0x20; spec->beep_dev_nid = 0x10; set_beep_amp(spec, 0x10, 0, HDA_OUTPUT); return ad198x_parse_auto_config(codec); @@ -4632,6 +4636,7 @@ static int ad1882_parse_auto_config(struct hda_codec *codec) { struct ad198x_spec *spec = codec->spec; + spec->gen.mixer_nid = 0x20; spec->beep_dev_nid = 0x10; set_beep_amp(spec, 0x10, 0, HDA_OUTPUT); return ad198x_parse_auto_config(codec); -- cgit v1.2.3 From a607148ff3b9f40427c0f0d5fa039a3a758735c7 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 21 Jan 2013 16:50:09 +0100 Subject: ALSA: hda - Set individual name to secondary analog PCM stream It'd be better to give another name to the secondary (alt) analog PCM stream, which is dedicated for the independent HP out and extra inputs. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 5 ++++- sound/pci/hda/hda_generic.h | 2 ++ 2 files changed, 6 insertions(+), 1 deletion(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 6d1e843c6e8d..63d12efb7c1a 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -4531,9 +4531,12 @@ int snd_hda_gen_build_pcms(struct hda_codec *codec) !spec->dyn_adc_switch && !spec->auto_mic; /* Additional Analaog capture for index #2 */ if (spec->alt_dac_nid || have_multi_adcs) { + fill_pcm_stream_name(spec->stream_name_alt_analog, + sizeof(spec->stream_name_alt_analog), + " Alt Analog", codec->chip_name); codec->num_pcms = 3; info = spec->pcm_rec + 2; - info->name = spec->stream_name_analog; + info->name = spec->stream_name_alt_analog; if (spec->alt_dac_nid) { p = spec->stream_analog_alt_playback; if (!p) diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h index 9c63555b971c..980707fcd30a 100644 --- a/sound/pci/hda/hda_generic.h +++ b/sound/pci/hda/hda_generic.h @@ -80,6 +80,8 @@ struct hda_gen_spec { char stream_name_analog[32]; /* analog PCM stream */ const struct hda_pcm_stream *stream_analog_playback; const struct hda_pcm_stream *stream_analog_capture; + + char stream_name_alt_analog[32]; /* alternative analog PCM stream */ const struct hda_pcm_stream *stream_analog_alt_playback; const struct hda_pcm_stream *stream_analog_alt_capture; -- cgit v1.2.3 From 92603c594579e744a13b06efbbebb126db254655 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 22 Jan 2013 07:46:31 +0100 Subject: ALSA: hda - Disable HP auto-mute during independent HP mode Both the HP auto-mute and the independent HP mode conflict with each other. Make HP auto-mute disabled (only for the affected HP jack) during the driver is in HP independent mode. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 63d12efb7c1a..258fb5ee75c5 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -1889,6 +1889,13 @@ static int indep_hp_put(struct snd_kcontrol *kcontrol, *dacp = 0; else *dacp = spec->alt_dac_nid; + + /* update HP auto-mute state too */ + if (spec->hp_automute_hook) + spec->hp_automute_hook(codec, NULL); + else + snd_hda_gen_hp_automute(codec, NULL); + ret = 1; } unlock: @@ -3467,10 +3474,16 @@ static void call_update_outputs(struct hda_codec *codec) void snd_hda_gen_hp_automute(struct hda_codec *codec, struct hda_jack_tbl *jack) { struct hda_gen_spec *spec = codec->spec; + hda_nid_t *pins = spec->autocfg.hp_pins; + int num_pins = ARRAY_SIZE(spec->autocfg.hp_pins); + + /* No detection for the first HP jack during indep-HP mode */ + if (spec->indep_hp_enabled) { + pins++; + num_pins--; + } - spec->hp_jack_present = - detect_jacks(codec, ARRAY_SIZE(spec->autocfg.hp_pins), - spec->autocfg.hp_pins); + spec->hp_jack_present = detect_jacks(codec, num_pins, pins); if (!spec->detect_hp || (!spec->automute_speaker && !spec->automute_lo)) return; call_update_outputs(codec); -- cgit v1.2.3 From 42875479b21e8f38ad1d7b09cde8906c41f17bf8 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 22 Jan 2013 09:34:48 +0100 Subject: ALSA: hda - Revive SPDIF mux for IDT/STAC codecs The stuff that was dropped while transition to the generic parser is now recovered. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_sigmatel.c | 101 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index c53b6f9f2ca8..0aa0ceba0806 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -177,6 +177,7 @@ struct sigmatel_spec { unsigned int headset_jack:1; /* 4-pin headset jack (hp + mono mic) */ unsigned int volknob_init:1; /* special volume-knob initialization */ unsigned int powerdown_adcs:1; + unsigned int have_spdif_mux:1; /* gpio lines */ unsigned int eapd_mask; @@ -211,6 +212,11 @@ struct sigmatel_spec { /* beep widgets */ hda_nid_t anabeep_nid; hda_nid_t digbeep_nid; + + /* SPDIF-out mux */ + const char * const *spdif_labels; + struct hda_input_mux spdif_mux; + unsigned int cur_smux[2]; }; #define AC_VERB_IDT_SET_POWER_MAP 0x7ec @@ -884,6 +890,85 @@ static int stac_beep_switch_ctl(struct hda_codec *codec) } #endif +/* + * SPDIF-out mux controls + */ + +static int stac_smux_enum_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct sigmatel_spec *spec = codec->spec; + return snd_hda_input_mux_info(&spec->spdif_mux, uinfo); +} + +static int stac_smux_enum_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct sigmatel_spec *spec = codec->spec; + unsigned int smux_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); + + ucontrol->value.enumerated.item[0] = spec->cur_smux[smux_idx]; + return 0; +} + +static int stac_smux_enum_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct sigmatel_spec *spec = codec->spec; + unsigned int smux_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); + + return snd_hda_input_mux_put(codec, &spec->spdif_mux, ucontrol, + spec->gen.autocfg.dig_out_pins[smux_idx], + &spec->cur_smux[smux_idx]); +} + +static struct snd_kcontrol_new stac_smux_mixer = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "IEC958 Playback Source", + /* count set later */ + .info = stac_smux_enum_info, + .get = stac_smux_enum_get, + .put = stac_smux_enum_put, +}; + +static const char * const stac_spdif_labels[] = { + "Digital Playback", "Analog Mux 1", "Analog Mux 2", NULL +}; + +static int stac_create_spdif_mux_ctls(struct hda_codec *codec) +{ + struct sigmatel_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->gen.autocfg; + const char * const *labels = spec->spdif_labels; + struct snd_kcontrol_new *kctl; + int i, num_cons; + + if (cfg->dig_outs < 1) + return 0; + + num_cons = snd_hda_get_num_conns(codec, cfg->dig_out_pins[0]); + if (num_cons <= 1) + return 0; + + if (!labels) + labels = stac_spdif_labels; + for (i = 0; i < num_cons; i++) { + if (snd_BUG_ON(!labels[i])) + return -EINVAL; + snd_hda_add_imux_item(&spec->spdif_mux, labels[i], i, NULL); + } + + kctl = snd_hda_gen_add_kctl(&spec->gen, NULL, &stac_smux_mixer); + if (!kctl) + return -ENOMEM; + kctl->count = cfg->dig_outs; + + return 0; +} + /* */ @@ -3475,6 +3560,12 @@ static int stac_parse_auto_config(struct hda_codec *codec) return -ENOMEM; } + if (spec->have_spdif_mux) { + err = stac_create_spdif_mux_ctls(codec); + if (err < 0) + return err; + } + stac_init_power_map(codec); return 0; @@ -3744,6 +3835,7 @@ static int patch_stac92hd73xx(struct hda_codec *codec) spec = codec->spec; spec->linear_tone_beep = 0; spec->gen.mixer_nid = 0x1d; + spec->have_spdif_mux = 1; num_dacs = snd_hda_get_num_conns(codec, 0x0a) - 1; if (num_dacs < 3 || num_dacs > 5) { @@ -3885,6 +3977,7 @@ static int patch_stac92hd71bxx(struct hda_codec *codec) spec->linear_tone_beep = 0; spec->gen.own_eapd_ctl = 1; spec->gen.mixer_nid = 0x17; + spec->have_spdif_mux = 1; codec->patch_ops = stac_patch_ops; @@ -3988,6 +4081,11 @@ static int patch_stac922x(struct hda_codec *codec) return 0; } +static const char * const stac927x_spdif_labels[] = { + "Digital Playback", "ADAT", "Analog Mux 1", + "Analog Mux 2", "Analog Mux 3", NULL +}; + static int patch_stac927x(struct hda_codec *codec) { struct sigmatel_spec *spec; @@ -4000,6 +4098,8 @@ static int patch_stac927x(struct hda_codec *codec) spec = codec->spec; spec->linear_tone_beep = 1; spec->gen.own_eapd_ctl = 1; + spec->have_spdif_mux = 1; + spec->spdif_labels = stac927x_spdif_labels; spec->digbeep_nid = 0x23; @@ -4058,6 +4158,7 @@ static int patch_stac9205(struct hda_codec *codec) spec = codec->spec; spec->linear_tone_beep = 1; spec->gen.own_eapd_ctl = 1; + spec->have_spdif_mux = 1; spec->digbeep_nid = 0x23; -- cgit v1.2.3 From 4bd01e9336cd0fa037c79b6b203a4b79aecbfb09 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 22 Jan 2013 15:17:20 +0100 Subject: ALSA: hda - Add missing exports to helper functions Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 258fb5ee75c5..374fd6c6558b 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -261,6 +261,7 @@ int snd_hda_get_path_idx(struct hda_codec *codec, struct nid_path *path) return 0; return idx + 1; } +EXPORT_SYMBOL_HDA(snd_hda_get_path_idx); /* get the path instance corresponding to the given index number */ struct nid_path *snd_hda_get_path_from_idx(struct hda_codec *codec, int idx) @@ -271,6 +272,7 @@ struct nid_path *snd_hda_get_path_from_idx(struct hda_codec *codec, int idx) return NULL; return snd_array_elem(&spec->paths, idx - 1); } +EXPORT_SYMBOL_HDA(snd_hda_get_path_from_idx); /* check whether the given DAC is already found in any existing paths */ static bool is_dac_already_used(struct hda_codec *codec, hda_nid_t nid) -- cgit v1.2.3 From a836dbf685fa58c7db6cd56ad4559b2e6c02c8d9 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 22 Jan 2013 15:18:17 +0100 Subject: ALSA: hda - Fix missing call of cmd flush in capture volume put callback The capture volume put callback may call the node selection change, and its actual call won't be triggered unless flushed. In general, we always need to call both snd_hda_codec_flush_amp_cache() and snd_hda_codec_flush_cmd_cache() at the same place... Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 374fd6c6558b..b301952dc081 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -2840,6 +2840,7 @@ static int cap_put_caller(struct snd_kcontrol *kcontrol, codec->cached_write = 0; mutex_unlock(&codec->control_mutex); snd_hda_codec_flush_amp_cache(codec); /* flush the updates */ + snd_hda_codec_flush_cmd_cache(codec); if (err >= 0 && spec->cap_sync_hook) spec->cap_sync_hook(codec, ucontrol); return err; -- cgit v1.2.3 From dc870f38e9faf7dd89355aae2252126688a1a372 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 22 Jan 2013 15:24:30 +0100 Subject: ALSA: hda - Combine snd_hda_codec_flush_*_cache() to a single function Since both snd_hda_codec_flush_amp_cache() and snd_hda_codec_flush_cmd_cache() are called usually at the same time, we can simply combine them to a single function, snd_hda_codec_flush_cache(). Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_codec.c | 11 +++++++++++ sound/pci/hda/hda_codec.h | 6 ++---- sound/pci/hda/hda_generic.c | 6 ++---- sound/pci/hda/hda_local.h | 4 ---- sound/pci/hda/patch_realtek.c | 2 +- 5 files changed, 16 insertions(+), 13 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index e6cdad713734..77ddd34fffed 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -3637,6 +3637,17 @@ void snd_hda_sequence_write_cache(struct hda_codec *codec, } EXPORT_SYMBOL_HDA(snd_hda_sequence_write_cache); +/** + * snd_hda_codec_flush_cache - Execute all pending (cached) amps / verbs + * @codec: HD-audio codec + */ +void snd_hda_codec_flush_cache(struct hda_codec *codec) +{ + snd_hda_codec_resume_amp(codec); + snd_hda_codec_resume_cache(codec); +} +EXPORT_SYMBOL_HDA(snd_hda_codec_flush_cache); + void snd_hda_codec_set_power_to_all(struct hda_codec *codec, hda_nid_t fg, unsigned int power_state, bool eapd_workaround) diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index 61085b311059..cc73287341de 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -974,10 +974,8 @@ void snd_hda_sequence_write_cache(struct hda_codec *codec, int snd_hda_codec_update_cache(struct hda_codec *codec, hda_nid_t nid, int direct, unsigned int verb, unsigned int parm); void snd_hda_codec_resume_cache(struct hda_codec *codec); - -/* it's alias but a bit clearer meaning */ -#define snd_hda_codec_flush_cmd_cache(codec) \ - snd_hda_codec_resume_cache(codec) +/* both for cmd & amp caches */ +void snd_hda_codec_flush_cache(struct hda_codec *codec); /* the struct for codec->pin_configs */ struct hda_pincfg { diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index b301952dc081..758dcc1a07b0 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -2839,8 +2839,7 @@ static int cap_put_caller(struct snd_kcontrol *kcontrol, error: codec->cached_write = 0; mutex_unlock(&codec->control_mutex); - snd_hda_codec_flush_amp_cache(codec); /* flush the updates */ - snd_hda_codec_flush_cmd_cache(codec); + snd_hda_codec_flush_cache(codec); /* flush the updates */ if (err >= 0 && spec->cap_sync_hook) spec->cap_sync_hook(codec, ucontrol); return err; @@ -4773,8 +4772,7 @@ int snd_hda_gen_init(struct hda_codec *codec) /* call init functions of standard auto-mute helpers */ update_automute_all(codec); - snd_hda_codec_flush_amp_cache(codec); - snd_hda_codec_flush_cmd_cache(codec); + snd_hda_codec_flush_cache(codec); if (spec->vmaster_mute.sw_kctl && spec->vmaster_mute.hook) snd_hda_sync_vmaster_hook(&spec->vmaster_mute); diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index 9e6353aafb1a..f92979c6b023 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -139,10 +139,6 @@ int snd_hda_codec_amp_init_stereo(struct hda_codec *codec, hda_nid_t nid, int dir, int idx, int mask, int val); void snd_hda_codec_resume_amp(struct hda_codec *codec); -/* it's alias but a bit clearer meaning */ -#define snd_hda_codec_flush_amp_cache(codec) \ - snd_hda_codec_resume_amp(codec) - void snd_hda_set_vmaster_tlv(struct hda_codec *codec, hda_nid_t nid, int dir, unsigned int *tlv); struct snd_kcontrol *snd_hda_find_mixer_ctl(struct hda_codec *codec, diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 6eb9551e9723..feb8cef341e0 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -621,7 +621,7 @@ static void alc_inv_dmic_sync_adc(struct hda_codec *codec, int adc_idx) (dir == HDA_OUTPUT ? AC_AMP_SET_OUTPUT : AC_AMP_SET_INPUT); /* flush all cached amps at first */ - snd_hda_codec_flush_amp_cache(codec); + snd_hda_codec_flush_cache(codec); /* we care only right channel */ val = snd_hda_codec_amp_read(codec, nid, 1, dir, 0); -- cgit v1.2.3 From 272f3ea317762e55740326c01af64052a5fbb819 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 22 Jan 2013 15:31:33 +0100 Subject: ALSA: hda - Add SPDIF mux control to AD codec auto-parser AD codecs have strange implementations for choosing the SPDIF-output mux source: the digital audio out widget may take the sources from multiple connections, where 0x01 indicates it's a PCM while others point ADCs. It's obviously invalid in the HD-audio spec POV, but it's somehow convincing, too. And, to make things more complex, AD1988A and AD1882 have deeper connection routes that aren't expressed correctly. In this patch, the SPDIF mux control is implemented in two ways: - For easier one like AD1981, AD1983, AD1884 and AD1984, where the SPDIF audio out widget takes just two or three sources, we can simply implement via the normal input_mux and connection verb calls. - For the complex routes like AD1988A (but not AD1988B) or AD1882, we prepare "faked" paths represented statically, and switch the paths using these static ones, instead of parsing the routes from the widget tree. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_analog.c | 266 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 261 insertions(+), 5 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index a186b3da20b7..5d8328a64207 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c @@ -35,6 +35,10 @@ struct ad198x_spec { struct hda_gen_spec gen; + /* for auto parser */ + int smux_paths[4]; + unsigned int cur_smux; + const struct snd_kcontrol_new *mixers[6]; int num_mixers; unsigned int beep_amp; /* beep amp value, set via set_beep_amp() */ @@ -1519,13 +1523,94 @@ static const char * const ad1983_models[AD1983_MODELS] = { [AD1983_BASIC] = "basic", }; +/* + * SPDIF mux control for AD1983 auto-parser + */ +static int ad1983_auto_smux_enum_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct ad198x_spec *spec = codec->spec; + static const char * const texts2[] = { "PCM", "ADC" }; + static const char * const texts3[] = { "PCM", "ADC1", "ADC2" }; + hda_nid_t dig_out = spec->gen.multiout.dig_out_nid; + int num_conns = snd_hda_get_num_conns(codec, dig_out); + + if (num_conns == 2) + return snd_hda_enum_helper_info(kcontrol, uinfo, 2, texts2); + else if (num_conns == 3) + return snd_hda_enum_helper_info(kcontrol, uinfo, 3, texts3); + else + return -EINVAL; +} + +static int ad1983_auto_smux_enum_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct ad198x_spec *spec = codec->spec; + + ucontrol->value.enumerated.item[0] = spec->cur_smux; + return 0; +} + +static int ad1983_auto_smux_enum_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct ad198x_spec *spec = codec->spec; + unsigned int val = ucontrol->value.enumerated.item[0]; + hda_nid_t dig_out = spec->gen.multiout.dig_out_nid; + int num_conns = snd_hda_get_num_conns(codec, dig_out); + + if (val >= num_conns) + return -EINVAL; + if (spec->cur_smux == val) + return 0; + spec->cur_smux = val; + snd_hda_codec_write_cache(codec, dig_out, 0, + AC_VERB_SET_CONNECT_SEL, val); + return 1; +} + +static struct snd_kcontrol_new ad1983_auto_smux_mixer = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "IEC958 Playback Source", + .info = ad1983_auto_smux_enum_info, + .get = ad1983_auto_smux_enum_get, + .put = ad1983_auto_smux_enum_put, +}; + +static int ad1983_add_spdif_mux_ctl(struct hda_codec *codec) +{ + struct ad198x_spec *spec = codec->spec; + hda_nid_t dig_out = spec->gen.multiout.dig_out_nid; + int num_conns; + + if (!dig_out) + return 0; + num_conns = snd_hda_get_num_conns(codec, dig_out); + if (num_conns != 2 && num_conns != 3) + return 0; + if (!snd_hda_gen_add_kctl(&spec->gen, NULL, &ad1983_auto_smux_mixer)) + return -ENOMEM; + return 0; +} + static int ad1983_parse_auto_config(struct hda_codec *codec) { struct ad198x_spec *spec = codec->spec; + int err; spec->beep_dev_nid = 0x10; set_beep_amp(spec, 0x10, 0, HDA_OUTPUT); - return ad198x_parse_auto_config(codec); + err = ad198x_parse_auto_config(codec); + if (err < 0) + return err; + err = ad1983_add_spdif_mux_ctl(codec); + if (err < 0) + return err; + return 0; } static int patch_ad1983(struct hda_codec *codec) @@ -1950,11 +2035,18 @@ static const struct snd_pci_quirk ad1981_cfg_tbl[] = { static int ad1981_parse_auto_config(struct hda_codec *codec) { struct ad198x_spec *spec = codec->spec; + int err; spec->gen.mixer_nid = 0x0e; spec->beep_dev_nid = 0x10; set_beep_amp(spec, 0x0d, 0, HDA_OUTPUT); - return ad198x_parse_auto_config(codec); + err = ad198x_parse_auto_config(codec); + if (err < 0) + return err; + err = ad1983_add_spdif_mux_ctl(codec); + if (err < 0) + return err; + return 0; } static int patch_ad1981(struct hda_codec *codec) @@ -2820,17 +2912,167 @@ static const struct hda_amp_list ad1988_loopbacks[] = { }; #endif +static int ad1988_auto_smux_enum_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + static const char * const texts[] = { + "PCM", "ADC1", "ADC2", "ADC3", + }; + int num_conns = snd_hda_get_num_conns(codec, 0x0b) + 1; + if (num_conns > 4) + num_conns = 4; + return snd_hda_enum_helper_info(kcontrol, uinfo, num_conns, texts); +} + +static int ad1988_auto_smux_enum_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct ad198x_spec *spec = codec->spec; + + ucontrol->value.enumerated.item[0] = spec->cur_smux; + return 0; +} + +static int ad1988_auto_smux_enum_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct ad198x_spec *spec = codec->spec; + unsigned int val = ucontrol->value.enumerated.item[0]; + struct nid_path *path; + int num_conns = snd_hda_get_num_conns(codec, 0x0b) + 1; + + if (val >= num_conns) + return -EINVAL; + if (spec->cur_smux == val) + return 0; + + mutex_lock(&codec->control_mutex); + codec->cached_write = 1; + path = snd_hda_get_path_from_idx(codec, + spec->smux_paths[spec->cur_smux]); + if (path) + snd_hda_activate_path(codec, path, false, true); + path = snd_hda_get_path_from_idx(codec, spec->smux_paths[val]); + if (path) + snd_hda_activate_path(codec, path, true, true); + spec->cur_smux = val; + codec->cached_write = 0; + mutex_unlock(&codec->control_mutex); + snd_hda_codec_flush_cache(codec); /* flush the updates */ + return 1; +} + +static struct snd_kcontrol_new ad1988_auto_smux_mixer = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "IEC958 Playback Source", + .info = ad1988_auto_smux_enum_info, + .get = ad1988_auto_smux_enum_get, + .put = ad1988_auto_smux_enum_put, +}; + +static int ad1988_auto_init(struct hda_codec *codec) +{ + struct ad198x_spec *spec = codec->spec; + int i, err; + + err = snd_hda_gen_init(codec); + if (err < 0) + return err; + if (!spec->gen.autocfg.dig_outs) + return 0; + + for (i = 0; i < 4; i++) { + struct nid_path *path; + path = snd_hda_get_path_from_idx(codec, spec->smux_paths[i]); + if (path) + snd_hda_activate_path(codec, path, path->active, false); + } + + return 0; +} + +static int ad1988_add_spdif_mux_ctl(struct hda_codec *codec) +{ + struct ad198x_spec *spec = codec->spec; + int i, num_conns; + /* we create four static faked paths, since AD codecs have odd + * widget connections regarding the SPDIF out source + */ + static struct nid_path fake_paths[4] = { + { + .depth = 3, + .path = { 0x02, 0x1d, 0x1b }, + .idx = { 0, 0, 0 }, + .multi = { 0, 0, 0 }, + }, + { + .depth = 4, + .path = { 0x08, 0x0b, 0x1d, 0x1b }, + .idx = { 0, 0, 1, 0 }, + .multi = { 0, 1, 0, 0 }, + }, + { + .depth = 4, + .path = { 0x09, 0x0b, 0x1d, 0x1b }, + .idx = { 0, 1, 1, 0 }, + .multi = { 0, 1, 0, 0 }, + }, + { + .depth = 4, + .path = { 0x0f, 0x0b, 0x1d, 0x1b }, + .idx = { 0, 2, 1, 0 }, + .multi = { 0, 1, 0, 0 }, + }, + }; + + /* SPDIF source mux appears to be present only on AD1988A */ + if (!spec->gen.autocfg.dig_outs || + get_wcaps_type(get_wcaps(codec, 0x1d)) != AC_WID_AUD_MIX) + return 0; + + num_conns = snd_hda_get_num_conns(codec, 0x0b) + 1; + if (num_conns != 3 && num_conns != 4) + return 0; + + for (i = 0; i < num_conns; i++) { + struct nid_path *path = snd_array_new(&spec->gen.paths); + if (!path) + return -ENOMEM; + *path = fake_paths[i]; + if (!i) + path->active = 1; + spec->smux_paths[i] = snd_hda_get_path_idx(codec, path); + } + + if (!snd_hda_gen_add_kctl(&spec->gen, NULL, &ad1988_auto_smux_mixer)) + return -ENOMEM; + + codec->patch_ops.init = ad1988_auto_init; + + return 0; +} + /* */ static int ad1988_parse_auto_config(struct hda_codec *codec) { struct ad198x_spec *spec = codec->spec; + int err; spec->gen.mixer_nid = 0x20; spec->beep_dev_nid = 0x10; set_beep_amp(spec, 0x10, 0, HDA_OUTPUT); - return ad198x_parse_auto_config(codec); + err = ad198x_parse_auto_config(codec); + if (err < 0) + return err; + err = ad1988_add_spdif_mux_ctl(codec); + if (err < 0) + return err; + return 0; } /* @@ -3174,11 +3416,18 @@ static const char * const ad1884_models[AD1884_MODELS] = { static int ad1884_parse_auto_config(struct hda_codec *codec) { struct ad198x_spec *spec = codec->spec; + int err; spec->gen.mixer_nid = 0x20; spec->beep_dev_nid = 0x10; set_beep_amp(spec, 0x10, 0, HDA_OUTPUT); - return ad198x_parse_auto_config(codec); + err = ad198x_parse_auto_config(codec); + if (err < 0) + return err; + err = ad1983_add_spdif_mux_ctl(codec); + if (err < 0) + return err; + return 0; } static int patch_ad1884_auto(struct hda_codec *codec) @@ -4635,11 +4884,18 @@ static const char * const ad1882_models[AD1986A_MODELS] = { static int ad1882_parse_auto_config(struct hda_codec *codec) { struct ad198x_spec *spec = codec->spec; + int err; spec->gen.mixer_nid = 0x20; spec->beep_dev_nid = 0x10; set_beep_amp(spec, 0x10, 0, HDA_OUTPUT); - return ad198x_parse_auto_config(codec); + err = ad198x_parse_auto_config(codec); + if (err < 0) + return err; + err = ad1988_add_spdif_mux_ctl(codec); + if (err < 0) + return err; + return 0; } static int patch_ad1882(struct hda_codec *codec) -- cgit v1.2.3 From 9ff4bc8f72751d225f457c05f856657091573a16 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 22 Jan 2013 16:45:58 +0100 Subject: ALSA: hda - Rearrange for dropping static quirk codes in AD codec driver As done for patch_conexant.c, put ifdef ENABLE_AD_STATIC_QUIRKS for preparing t odrop the static quirk codes in patch_analog.c. The whole static quirk code can be omitted by commenting out ENABLE_AD_STATIC_QUIRKS define now. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_analog.c | 262 +++++++++++++++++++++++++++---------------- 1 file changed, 165 insertions(+), 97 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index 5d8328a64207..98cbc983435a 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c @@ -32,6 +32,8 @@ #include "hda_jack.h" #include "hda_generic.h" +#define ENABLE_AD_STATIC_QUIRKS + struct ad198x_spec { struct hda_gen_spec gen; @@ -39,10 +41,12 @@ struct ad198x_spec { int smux_paths[4]; unsigned int cur_smux; - const struct snd_kcontrol_new *mixers[6]; - int num_mixers; unsigned int beep_amp; /* beep amp value, set via set_beep_amp() */ hda_nid_t beep_dev_nid; + +#ifdef ENABLE_AD_STATIC_QUIRKS + const struct snd_kcontrol_new *mixers[6]; + int num_mixers; const struct hda_verb *init_verbs[6]; /* initialization verbs * don't forget NULL termination! */ @@ -87,8 +91,10 @@ struct ad198x_spec { hda_nid_t vmaster_nid; const char * const *slave_vols; const char * const *slave_sws; +#endif /* ENABLE_AD_STATIC_QUIRKS */ }; +#ifdef ENABLE_AD_STATIC_QUIRKS /* * input MUX handling (common part) */ @@ -144,6 +150,7 @@ static const char * const ad1988_6stack_fp_slave_pfxs[] = { "Front", "Surround", "Center", "LFE", "Side", "IEC958", NULL }; +#endif /* ENABLE_AD_STATIC_QUIRKS */ #ifdef CONFIG_SND_HDA_INPUT_BEEP /* additional beep mixers; the actual parameters are overwritten at build */ @@ -192,6 +199,7 @@ static int create_beep_ctls(struct hda_codec *codec) #define create_beep_ctls(codec) 0 #endif +#ifdef ENABLE_AD_STATIC_QUIRKS static int ad198x_build_controls(struct hda_codec *codec) { struct ad198x_spec *spec = codec->spec; @@ -452,6 +460,7 @@ static int ad198x_build_pcms(struct hda_codec *codec) return 0; } +#endif /* ENABLE_AD_STATIC_QUIRKS */ static void ad198x_power_eapd_write(struct hda_codec *codec, hda_nid_t front, hda_nid_t hp) @@ -518,6 +527,7 @@ static int ad198x_suspend(struct hda_codec *codec) } #endif +#ifdef ENABLE_AD_STATIC_QUIRKS static const struct hda_codec_ops ad198x_patch_ops = { .build_controls = ad198x_build_controls, .build_pcms = ad198x_build_pcms, @@ -574,6 +584,7 @@ static int ad198x_ch_mode_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); static int ad198x_ch_mode_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); +#endif /* ENABLE_AD_STATIC_QUIRKS */ /* @@ -641,6 +652,7 @@ static int ad198x_parse_auto_config(struct hda_codec *codec) * AD1986A specific */ +#ifdef ENABLE_AD_STATIC_QUIRKS #define AD1986A_SPDIF_OUT 0x02 #define AD1986A_FRONT_DAC 0x03 #define AD1986A_SURR_DAC 0x04 @@ -1169,6 +1181,7 @@ static int is_jack_available(struct hda_codec *codec, hda_nid_t nid) unsigned int conf = snd_hda_codec_get_pincfg(codec, nid); return get_defcfg_connect(conf) != AC_JACK_PORT_NONE; } +#endif /* ENABLE_AD_STATIC_QUIRKS */ static int alloc_ad_spec(struct hda_codec *codec) { @@ -1186,7 +1199,13 @@ static int alloc_ad_spec(struct hda_codec *codec) */ static int ad1986a_parse_auto_config(struct hda_codec *codec) { - struct ad198x_spec *spec = codec->spec; + int err; + struct ad198x_spec *spec; + + err = alloc_ad_spec(codec); + if (err < 0) + return err; + spec = codec->spec; /* AD1986A has the inverted EAPD implementation */ codec->inv_eapd = 1; @@ -1203,31 +1222,32 @@ static int ad1986a_parse_auto_config(struct hda_codec *codec) */ spec->gen.multiout.no_share_stream = 1; - return ad198x_parse_auto_config(codec); + err = ad198x_parse_auto_config(codec); + if (err < 0) { + ad198x_free(codec); + return err; + } + + return 0; } +#ifdef ENABLE_AD_STATIC_QUIRKS static int patch_ad1986a(struct hda_codec *codec) { struct ad198x_spec *spec; int err, board_config; + board_config = snd_hda_check_board_config(codec, AD1986A_MODELS, + ad1986a_models, + ad1986a_cfg_tbl); + if (board_config == AD1986A_AUTO) + return ad1986a_parse_auto_config(codec); + err = alloc_ad_spec(codec); if (err < 0) return err; spec = codec->spec; - board_config = snd_hda_check_board_config(codec, AD1986A_MODELS, - ad1986a_models, - ad1986a_cfg_tbl); - if (board_config == AD1986A_AUTO) { - err = ad1986a_parse_auto_config(codec); - if (err < 0) { - ad198x_free(codec); - return err; - } - return 0; - } - err = snd_hda_attach_beep_device(codec, 0x19); if (err < 0) { ad198x_free(codec); @@ -1366,11 +1386,15 @@ static int patch_ad1986a(struct hda_codec *codec) return 0; } +#else /* ENABLE_AD_STATIC_QUIRKS */ +#define patch_ad1986a ad1986a_parse_auto_config +#endif /* ENABLE_AD_STATIC_QUIRKS */ /* * AD1983 specific */ +#ifdef ENABLE_AD_STATIC_QUIRKS #define AD1983_SPDIF_OUT 0x02 #define AD1983_DAC 0x03 #define AD1983_ADC 0x04 @@ -1522,6 +1546,8 @@ static const char * const ad1983_models[AD1983_MODELS] = { [AD1983_AUTO] = "auto", [AD1983_BASIC] = "basic", }; +#endif /* ENABLE_AD_STATIC_QUIRKS */ + /* * SPDIF mux control for AD1983 auto-parser @@ -1599,42 +1625,46 @@ static int ad1983_add_spdif_mux_ctl(struct hda_codec *codec) static int ad1983_parse_auto_config(struct hda_codec *codec) { - struct ad198x_spec *spec = codec->spec; + struct ad198x_spec *spec; int err; + err = alloc_ad_spec(codec); + if (err < 0) + return err; + spec = codec->spec; + spec->beep_dev_nid = 0x10; set_beep_amp(spec, 0x10, 0, HDA_OUTPUT); err = ad198x_parse_auto_config(codec); if (err < 0) - return err; + goto error; err = ad1983_add_spdif_mux_ctl(codec); if (err < 0) - return err; + goto error; return 0; + + error: + ad198x_free(codec); + return err; } +#ifdef ENABLE_AD_STATIC_QUIRKS static int patch_ad1983(struct hda_codec *codec) { struct ad198x_spec *spec; int board_config; int err; + board_config = snd_hda_check_board_config(codec, AD1983_MODELS, + ad1983_models, NULL); + if (board_config == AD1983_AUTO) + return ad1983_parse_auto_config(codec); + err = alloc_ad_spec(codec); if (err < 0) return err; spec = codec->spec; - board_config = snd_hda_check_board_config(codec, AD1983_MODELS, - ad1983_models, NULL); - if (board_config == AD1983_AUTO) { - err = ad1983_parse_auto_config(codec); - if (err < 0) { - ad198x_free(codec); - return err; - } - return 0; - } - err = snd_hda_attach_beep_device(codec, 0x10); if (err < 0) { ad198x_free(codec); @@ -1667,12 +1697,16 @@ static int patch_ad1983(struct hda_codec *codec) return 0; } +#else /* ENABLE_AD_STATIC_QUIRKS */ +#define patch_ad1983 ad1983_parse_auto_config +#endif /* ENABLE_AD_STATIC_QUIRKS */ /* * AD1981 HD specific */ +#ifdef ENABLE_AD_STATIC_QUIRKS #define AD1981_SPDIF_OUT 0x02 #define AD1981_DAC 0x03 #define AD1981_ADC 0x04 @@ -2031,46 +2065,52 @@ static const struct snd_pci_quirk ad1981_cfg_tbl[] = { SND_PCI_QUIRK(0x30b0, 0x103c, "HP nx6320", AD1981_HP), {} }; +#endif /* ENABLE_AD_STATIC_QUIRKS */ + static int ad1981_parse_auto_config(struct hda_codec *codec) { - struct ad198x_spec *spec = codec->spec; + struct ad198x_spec *spec; int err; + err = alloc_ad_spec(codec); + if (err < 0) + return -ENOMEM; + spec = codec->spec; + spec->gen.mixer_nid = 0x0e; spec->beep_dev_nid = 0x10; set_beep_amp(spec, 0x0d, 0, HDA_OUTPUT); err = ad198x_parse_auto_config(codec); if (err < 0) - return err; + goto error; err = ad1983_add_spdif_mux_ctl(codec); if (err < 0) - return err; + goto error; return 0; + + error: + ad198x_free(codec); + return err; } +#ifdef ENABLE_AD_STATIC_QUIRKS static int patch_ad1981(struct hda_codec *codec) { struct ad198x_spec *spec; int err, board_config; + board_config = snd_hda_check_board_config(codec, AD1981_MODELS, + ad1981_models, + ad1981_cfg_tbl); + if (board_config == AD1981_AUTO) + return ad1981_parse_auto_config(codec); + err = alloc_ad_spec(codec); if (err < 0) return -ENOMEM; spec = codec->spec; - board_config = snd_hda_check_board_config(codec, AD1981_MODELS, - ad1981_models, - ad1981_cfg_tbl); - if (board_config == AD1981_AUTO) { - err = ad1981_parse_auto_config(codec); - if (err < 0) { - ad198x_free(codec); - return err; - } - return 0; - } - err = snd_hda_attach_beep_device(codec, 0x10); if (err < 0) { ad198x_free(codec); @@ -2148,6 +2188,9 @@ static int patch_ad1981(struct hda_codec *codec) return 0; } +#else /* ENABLE_AD_STATIC_QUIRKS */ +#define patch_ad1981 ad1981_parse_auto_config +#endif /* ENABLE_AD_STATIC_QUIRKS */ /* @@ -2236,6 +2279,7 @@ static int patch_ad1981(struct hda_codec *codec) */ +#ifdef ENABLE_AD_STATIC_QUIRKS /* models */ enum { AD1988_AUTO, @@ -2911,6 +2955,7 @@ static const struct hda_amp_list ad1988_loopbacks[] = { { } /* end */ }; #endif +#endif /* ENABLE_AD_STATIC_QUIRKS */ static int ad1988_auto_smux_enum_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) @@ -3060,24 +3105,34 @@ static int ad1988_add_spdif_mux_ctl(struct hda_codec *codec) static int ad1988_parse_auto_config(struct hda_codec *codec) { - struct ad198x_spec *spec = codec->spec; + struct ad198x_spec *spec; int err; + err = alloc_ad_spec(codec); + if (err < 0) + return err; + spec = codec->spec; + spec->gen.mixer_nid = 0x20; spec->beep_dev_nid = 0x10; set_beep_amp(spec, 0x10, 0, HDA_OUTPUT); err = ad198x_parse_auto_config(codec); if (err < 0) - return err; + goto error; err = ad1988_add_spdif_mux_ctl(codec); if (err < 0) - return err; + goto error; return 0; + + error: + ad198x_free(codec); + return err; } /* */ +#ifdef ENABLE_AD_STATIC_QUIRKS static const char * const ad1988_models[AD1988_MODEL_LAST] = { [AD1988_6STACK] = "6stack", [AD1988_6STACK_DIG] = "6stack-dig", @@ -3102,11 +3157,6 @@ static int patch_ad1988(struct hda_codec *codec) struct ad198x_spec *spec; int err, board_config; - err = alloc_ad_spec(codec); - if (err < 0) - return err; - spec = codec->spec; - board_config = snd_hda_check_board_config(codec, AD1988_MODEL_LAST, ad1988_models, ad1988_cfg_tbl); if (board_config < 0) { @@ -3115,15 +3165,13 @@ static int patch_ad1988(struct hda_codec *codec) board_config = AD1988_AUTO; } - if (board_config == AD1988_AUTO) { - /* automatic parse from the BIOS config */ - err = ad1988_parse_auto_config(codec); - if (err < 0) { - ad198x_free(codec); - return err; - } - return 0; - } + if (board_config == AD1988_AUTO) + return ad1988_parse_auto_config(codec); + + err = alloc_ad_spec(codec); + if (err < 0) + return err; + spec = codec->spec; if (is_rev2(codec)) snd_printk(KERN_INFO "patch_analog: AD1988A rev.2 is detected, enable workarounds\n"); @@ -3240,6 +3288,9 @@ static int patch_ad1988(struct hda_codec *codec) return 0; } +#else /* ENABLE_AD_STATIC_QUIRKS */ +#define patch_ad1988 ad1988_parse_auto_config +#endif /* ENABLE_AD_STATIC_QUIRKS */ /* @@ -3260,6 +3311,7 @@ static int patch_ad1988(struct hda_codec *codec) * but no build-up framework is given, so far. */ +#ifdef ENABLE_AD_STATIC_QUIRKS static const hda_nid_t ad1884_dac_nids[1] = { 0x04, }; @@ -3412,40 +3464,35 @@ static const char * const ad1884_models[AD1884_MODELS] = { [AD1884_AUTO] = "auto", [AD1884_BASIC] = "basic", }; +#endif /* ENABLE_AD_STATIC_QUIRKS */ static int ad1884_parse_auto_config(struct hda_codec *codec) { - struct ad198x_spec *spec = codec->spec; + struct ad198x_spec *spec; int err; + err = alloc_ad_spec(codec); + if (err < 0) + return err; + spec = codec->spec; + spec->gen.mixer_nid = 0x20; spec->beep_dev_nid = 0x10; set_beep_amp(spec, 0x10, 0, HDA_OUTPUT); err = ad198x_parse_auto_config(codec); if (err < 0) - return err; + goto error; err = ad1983_add_spdif_mux_ctl(codec); if (err < 0) - return err; + goto error; return 0; -} - -static int patch_ad1884_auto(struct hda_codec *codec) -{ - int err; - err = alloc_ad_spec(codec); - if (err < 0) - return err; - - err = ad1884_parse_auto_config(codec); - if (err < 0) { - ad198x_free(codec); - return err; - } - return 0; + error: + ad198x_free(codec); + return err; } +#ifdef ENABLE_AD_STATIC_QUIRKS static int patch_ad1884_basic(struct hda_codec *codec) { struct ad198x_spec *spec; @@ -3500,11 +3547,16 @@ static int patch_ad1884(struct hda_codec *codec) board_config = snd_hda_check_board_config(codec, AD1884_MODELS, ad1884_models, NULL); if (board_config == AD1884_AUTO) - return patch_ad1884_auto(codec); + return ad1884_parse_auto_config(codec); else return patch_ad1884_basic(codec); } +#else /* ENABLE_AD_STATIC_QUIRKS */ +#define patch_ad1884 ad1884_parse_auto_config +#endif /* ENABLE_AD_STATIC_QUIRKS */ + +#ifdef ENABLE_AD_STATIC_QUIRKS /* * Lenovo Thinkpad T61/X61 */ @@ -3707,7 +3759,7 @@ static int patch_ad1984(struct hda_codec *codec) board_config = snd_hda_check_board_config(codec, AD1984_MODELS, ad1984_models, ad1984_cfg_tbl); if (board_config == AD1984_AUTO) - return patch_ad1884_auto(codec); + return ad1884_parse_auto_config(codec); err = patch_ad1884_basic(codec); if (err < 0) @@ -3740,6 +3792,9 @@ static int patch_ad1984(struct hda_codec *codec) } return 0; } +#else /* ENABLE_AD_STATIC_QUIRKS */ +#define patch_ad1984 ad1884_parse_auto_config +#endif /* ENABLE_AD_STATIC_QUIRKS */ /* @@ -3760,6 +3815,7 @@ static int patch_ad1984(struct hda_codec *codec) * We share the single DAC for both HP and line-outs (see AD1884/1984). */ +#ifdef ENABLE_AD_STATIC_QUIRKS static const hda_nid_t ad1884a_dac_nids[1] = { 0x03, }; @@ -4474,7 +4530,7 @@ static int patch_ad1884a(struct hda_codec *codec) ad1884a_models, ad1884a_cfg_tbl); if (board_config == AD1884_AUTO) - return patch_ad1884_auto(codec); + return ad1884_parse_auto_config(codec); err = alloc_ad_spec(codec); if (err < 0) @@ -4577,6 +4633,9 @@ static int patch_ad1884a(struct hda_codec *codec) return 0; } +#else /* ENABLE_AD_STATIC_QUIRKS */ +#define patch_ad1884a ad1884_parse_auto_config +#endif /* ENABLE_AD_STATIC_QUIRKS */ /* @@ -4591,6 +4650,7 @@ static int patch_ad1884a(struct hda_codec *codec) * port-G - rear clfe-out (6stack) */ +#ifdef ENABLE_AD_STATIC_QUIRKS static const hda_nid_t ad1882_dac_nids[3] = { 0x04, 0x03, 0x05 }; @@ -4880,45 +4940,50 @@ static const char * const ad1882_models[AD1986A_MODELS] = { [AD1882_6STACK] = "6stack", [AD1882_3STACK_AUTOMUTE] = "3stack-automute", }; +#endif /* ENABLE_AD_STATIC_QUIRKS */ static int ad1882_parse_auto_config(struct hda_codec *codec) { - struct ad198x_spec *spec = codec->spec; + struct ad198x_spec *spec; int err; + err = alloc_ad_spec(codec); + if (err < 0) + return err; + spec = codec->spec; + spec->gen.mixer_nid = 0x20; spec->beep_dev_nid = 0x10; set_beep_amp(spec, 0x10, 0, HDA_OUTPUT); err = ad198x_parse_auto_config(codec); if (err < 0) - return err; + goto error; err = ad1988_add_spdif_mux_ctl(codec); if (err < 0) - return err; + goto error; return 0; + + error: + ad198x_free(codec); + return err; } +#ifdef ENABLE_AD_STATIC_QUIRKS static int patch_ad1882(struct hda_codec *codec) { struct ad198x_spec *spec; int err, board_config; + board_config = snd_hda_check_board_config(codec, AD1882_MODELS, + ad1882_models, NULL); + if (board_config == AD1882_AUTO) + return ad1882_parse_auto_config(codec); + err = alloc_ad_spec(codec); if (err < 0) return err; spec = codec->spec; - board_config = snd_hda_check_board_config(codec, AD1882_MODELS, - ad1882_models, NULL); - if (board_config == AD1882_AUTO) { - err = ad1882_parse_auto_config(codec); - if (err < 0) { - ad198x_free(codec); - return err; - } - return 0; - } - err = snd_hda_attach_beep_device(codec, 0x10); if (err < 0) { ad198x_free(codec); @@ -4983,6 +5048,9 @@ static int patch_ad1882(struct hda_codec *codec) return 0; } +#else /* ENABLE_AD_STATIC_QUIRKS */ +#define patch_ad1882 ad1882_parse_auto_config +#endif /* ENABLE_AD_STATIC_QUIRKS */ /* -- cgit v1.2.3 From a928bd2c565c30e5906d1ddfc21177173b2eef49 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 22 Jan 2013 18:18:42 +0100 Subject: ALSA: hda - Convert some static quirks to fixup codes for AD codecs Other remaining quirks are mostly resolvable via pincfg fixes, even if it matters. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_analog.c | 164 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 164 insertions(+) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index 98cbc983435a..9692265eef33 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c @@ -40,6 +40,7 @@ struct ad198x_spec { /* for auto parser */ int smux_paths[4]; unsigned int cur_smux; + hda_nid_t eapd_nid; unsigned int beep_amp; /* beep amp value, set via set_beep_amp() */ hda_nid_t beep_dev_nid; @@ -1195,6 +1196,34 @@ static int alloc_ad_spec(struct hda_codec *codec) return 0; } +/* + * AD1986A fixup codes + */ + +/* Lenovo N100 seems to report the reversed bit for HP jack-sensing */ +static void ad_fixup_inv_jack_detect(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + if (action == HDA_FIXUP_ACT_PRE_PROBE) + codec->inv_jack_detect = 1; +} + +enum { + AD1986A_FIXUP_INV_JACK_DETECT, +}; + +static const struct hda_fixup ad1986a_fixups[] = { + [AD1986A_FIXUP_INV_JACK_DETECT] = { + .type = HDA_FIXUP_FUNC, + .v.func = ad_fixup_inv_jack_detect, + }, +}; + +static const struct snd_pci_quirk ad1986a_fixup_tbl[] = { + SND_PCI_QUIRK(0x17aa, 0x2066, "Lenovo N100", AD1986A_FIXUP_INV_JACK_DETECT), + {} +}; + /* */ static int ad1986a_parse_auto_config(struct hda_codec *codec) @@ -1222,12 +1251,17 @@ static int ad1986a_parse_auto_config(struct hda_codec *codec) */ spec->gen.multiout.no_share_stream = 1; + snd_hda_pick_fixup(codec, NULL, ad1986a_fixup_tbl, ad1986a_fixups); + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); + err = ad198x_parse_auto_config(codec); if (err < 0) { ad198x_free(codec); return err; } + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); + return 0; } @@ -2068,6 +2102,68 @@ static const struct snd_pci_quirk ad1981_cfg_tbl[] = { #endif /* ENABLE_AD_STATIC_QUIRKS */ +/* follow EAPD via vmaster hook */ +static void ad_vmaster_eapd_hook(void *private_data, int enabled) +{ + struct hda_codec *codec = private_data; + struct ad198x_spec *spec = codec->spec; + snd_hda_codec_update_cache(codec, spec->eapd_nid, 0, + AC_VERB_SET_EAPD_BTLENABLE, + enabled ? 0x02 : 0x00); +} + +static void ad1981_fixup_hp_eapd(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct ad198x_spec *spec = codec->spec; + + if (action == HDA_FIXUP_ACT_PRE_PROBE) { + spec->gen.vmaster_mute.hook = ad_vmaster_eapd_hook; + spec->eapd_nid = 0x05; + } +} + +/* set the upper-limit for mixer amp to 0dB for avoiding the possible + * damage by overloading + */ +static void ad1981_fixup_amp_override(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + if (action == HDA_FIXUP_ACT_PRE_PROBE) + snd_hda_override_amp_caps(codec, 0x11, HDA_INPUT, + (0x17 << AC_AMPCAP_OFFSET_SHIFT) | + (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) | + (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) | + (1 << AC_AMPCAP_MUTE_SHIFT)); +} + +enum { + AD1981_FIXUP_AMP_OVERRIDE, + AD1981_FIXUP_HP_EAPD, +}; + +static const struct hda_fixup ad1981_fixups[] = { + [AD1981_FIXUP_AMP_OVERRIDE] = { + .type = HDA_FIXUP_FUNC, + .v.func = ad1981_fixup_amp_override, + }, + [AD1981_FIXUP_HP_EAPD] = { + .type = HDA_FIXUP_FUNC, + .v.func = ad1981_fixup_hp_eapd, + .chained = true, + .chain_id = AD1981_FIXUP_AMP_OVERRIDE, + }, +}; + +static const struct snd_pci_quirk ad1981_fixup_tbl[] = { + SND_PCI_QUIRK_VENDOR(0x1014, "Lenovo", AD1981_FIXUP_AMP_OVERRIDE), + SND_PCI_QUIRK_VENDOR(0x103c, "HP", AD1981_FIXUP_HP_EAPD), + SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo", AD1981_FIXUP_AMP_OVERRIDE), + /* HP nx6320 (reversed SSID, H/W bug) */ + SND_PCI_QUIRK(0x30b0, 0x103c, "HP nx6320", AD1981_FIXUP_HP_EAPD), + {} +}; + static int ad1981_parse_auto_config(struct hda_codec *codec) { struct ad198x_spec *spec; @@ -2081,12 +2177,19 @@ static int ad1981_parse_auto_config(struct hda_codec *codec) spec->gen.mixer_nid = 0x0e; spec->beep_dev_nid = 0x10; set_beep_amp(spec, 0x0d, 0, HDA_OUTPUT); + + snd_hda_pick_fixup(codec, NULL, ad1981_fixup_tbl, ad1981_fixups); + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); + err = ad198x_parse_auto_config(codec); if (err < 0) goto error; err = ad1983_add_spdif_mux_ctl(codec); if (err < 0) goto error; + + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); + return 0; error: @@ -3466,6 +3569,60 @@ static const char * const ad1884_models[AD1884_MODELS] = { }; #endif /* ENABLE_AD_STATIC_QUIRKS */ + +/* set the upper-limit for mixer amp to 0dB for avoiding the possible + * damage by overloading + */ +static void ad1884_fixup_amp_override(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + if (action == HDA_FIXUP_ACT_PRE_PROBE) + snd_hda_override_amp_caps(codec, 0x20, HDA_INPUT, + (0x17 << AC_AMPCAP_OFFSET_SHIFT) | + (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) | + (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) | + (1 << AC_AMPCAP_MUTE_SHIFT)); +} + +static void ad1884_fixup_hp_eapd(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct ad198x_spec *spec = codec->spec; + + if (action == HDA_FIXUP_ACT_PRE_PROBE) { + if (spec->gen.autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT) + spec->eapd_nid = spec->gen.autocfg.line_out_pins[0]; + else + spec->eapd_nid = spec->gen.autocfg.speaker_pins[0]; + if (spec->eapd_nid) + spec->gen.vmaster_mute.hook = ad_vmaster_eapd_hook; + } +} + +enum { + AD1884_FIXUP_AMP_OVERRIDE, + AD1884_FIXUP_HP_EAPD, +}; + +static const struct hda_fixup ad1884_fixups[] = { + [AD1884_FIXUP_AMP_OVERRIDE] = { + .type = HDA_FIXUP_FUNC, + .v.func = ad1884_fixup_amp_override, + }, + [AD1884_FIXUP_HP_EAPD] = { + .type = HDA_FIXUP_FUNC, + .v.func = ad1884_fixup_hp_eapd, + .chained = true, + .chain_id = AD1884_FIXUP_AMP_OVERRIDE, + }, +}; + +static const struct snd_pci_quirk ad1884_fixup_tbl[] = { + SND_PCI_QUIRK_VENDOR(0x103c, "HP", AD1884_FIXUP_HP_EAPD), + {} +}; + + static int ad1884_parse_auto_config(struct hda_codec *codec) { struct ad198x_spec *spec; @@ -3479,12 +3636,19 @@ static int ad1884_parse_auto_config(struct hda_codec *codec) spec->gen.mixer_nid = 0x20; spec->beep_dev_nid = 0x10; set_beep_amp(spec, 0x10, 0, HDA_OUTPUT); + + snd_hda_pick_fixup(codec, NULL, ad1884_fixup_tbl, ad1884_fixups); + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); + err = ad198x_parse_auto_config(codec); if (err < 0) goto error; err = ad1983_add_spdif_mux_ctl(codec); if (err < 0) goto error; + + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); + return 0; error: -- cgit v1.2.3 From 657e1b931d42882cb0a59b599247bef696c22406 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 22 Jan 2013 18:42:39 +0100 Subject: ALSA: hda - Select auto-parser as default for AD codecs Now all AD codecs have the proper BIOS auto-parser, and we can make it for default, finally. (AD1988 already did it because it had the auto-parser.) Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_analog.c | 44 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index 9692265eef33..9d82aab1e512 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c @@ -1274,6 +1274,12 @@ static int patch_ad1986a(struct hda_codec *codec) board_config = snd_hda_check_board_config(codec, AD1986A_MODELS, ad1986a_models, ad1986a_cfg_tbl); + if (board_config < 0) { + printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", + codec->chip_name); + board_config = AD1986A_AUTO; + } + if (board_config == AD1986A_AUTO) return ad1986a_parse_auto_config(codec); @@ -1691,6 +1697,12 @@ static int patch_ad1983(struct hda_codec *codec) board_config = snd_hda_check_board_config(codec, AD1983_MODELS, ad1983_models, NULL); + if (board_config < 0) { + printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", + codec->chip_name); + board_config = AD1983_AUTO; + } + if (board_config == AD1983_AUTO) return ad1983_parse_auto_config(codec); @@ -2206,6 +2218,12 @@ static int patch_ad1981(struct hda_codec *codec) board_config = snd_hda_check_board_config(codec, AD1981_MODELS, ad1981_models, ad1981_cfg_tbl); + if (board_config < 0) { + printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", + codec->chip_name); + board_config = AD1981_AUTO; + } + if (board_config == AD1981_AUTO) return ad1981_parse_auto_config(codec); @@ -3710,6 +3728,12 @@ static int patch_ad1884(struct hda_codec *codec) board_config = snd_hda_check_board_config(codec, AD1884_MODELS, ad1884_models, NULL); + if (board_config < 0) { + printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", + codec->chip_name); + board_config = AD1884_AUTO; + } + if (board_config == AD1884_AUTO) return ad1884_parse_auto_config(codec); else @@ -3922,6 +3946,12 @@ static int patch_ad1984(struct hda_codec *codec) board_config = snd_hda_check_board_config(codec, AD1984_MODELS, ad1984_models, ad1984_cfg_tbl); + if (board_config < 0) { + printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", + codec->chip_name); + board_config = AD1984_AUTO; + } + if (board_config == AD1984_AUTO) return ad1884_parse_auto_config(codec); @@ -4693,7 +4723,13 @@ static int patch_ad1884a(struct hda_codec *codec) board_config = snd_hda_check_board_config(codec, AD1884A_MODELS, ad1884a_models, ad1884a_cfg_tbl); - if (board_config == AD1884_AUTO) + if (board_config < 0) { + printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", + codec->chip_name); + board_config = AD1884A_AUTO; + } + + if (board_config == AD1884A_AUTO) return ad1884_parse_auto_config(codec); err = alloc_ad_spec(codec); @@ -5140,6 +5176,12 @@ static int patch_ad1882(struct hda_codec *codec) board_config = snd_hda_check_board_config(codec, AD1882_MODELS, ad1882_models, NULL); + if (board_config < 0) { + printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", + codec->chip_name); + board_config = AD1882_AUTO; + } + if (board_config == AD1882_AUTO) return ad1882_parse_auto_config(codec); -- cgit v1.2.3 From 0db75790e282dd1c8752b2472a9485940dd92c70 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 23 Jan 2013 13:57:20 +0100 Subject: ALSA: hda - Fix invalid snd_BUG_ON() in alc271_hp_gate_mic_jack() The fixup function is called multiple times before parsing the pins, so snd_BUG_ON() hits when loaded. Move it to the proper place in the if block. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 604fe5eaa2e4..515f83b24f1f 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -2729,12 +2729,13 @@ static void alc271_hp_gate_mic_jack(struct hda_codec *codec, { struct alc_spec *spec = codec->spec; - if (snd_BUG_ON(!spec->gen.am_entry[1].pin || - !spec->gen.autocfg.hp_pins[0])) - return; - if (action == HDA_FIXUP_ACT_PROBE) + if (action == HDA_FIXUP_ACT_PROBE) { + if (snd_BUG_ON(!spec->gen.am_entry[1].pin || + !spec->gen.autocfg.hp_pins[0])) + return; snd_hda_jack_set_gating_jack(codec, spec->gen.am_entry[1].pin, spec->gen.autocfg.hp_pins[0]); + } } enum { -- cgit v1.2.3 From e4a395e7819b1e666b2e9da22234059f403dbc11 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 23 Jan 2013 17:00:31 +0100 Subject: ALSA: hda - Fix missing path between aamix and outputs in AD codecs AD1988 family and AD1882 codecs have another mixer widget (0x21) between the analog-loopback mixer widget (0x20) and the actual outputs. Due to this hole, the analog-loopbacks aren't sent properly to the output pins. As a band-aid fix, introduce another fields holding the aamix merge path, and activate it. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 23 ++++++++++++++++++++++- sound/pci/hda/hda_generic.h | 2 ++ sound/pci/hda/patch_analog.c | 2 ++ 3 files changed, 26 insertions(+), 1 deletion(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 758dcc1a07b0..06e203df4059 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -688,7 +688,7 @@ static void activate_amp_in(struct hda_codec *codec, struct nid_path *path, * when aa-mixer is available, we need to enable the path as well */ for (n = 0; n < nums; n++) { - if (n != idx && (!add_aamix || conn[n] != spec->mixer_nid)) + if (n != idx && (!add_aamix || conn[n] != spec->mixer_merge_nid)) continue; activate_amp(codec, nid, HDA_INPUT, n, idx, enable); } @@ -2492,6 +2492,19 @@ static int new_analog_input(struct hda_codec *codec, int input_idx, path->active = true; add_loopback_list(spec, mix_nid, idx); + + if (spec->mixer_nid != spec->mixer_merge_nid && + !spec->loopback_merge_path) { + path = snd_hda_add_new_path(codec, spec->mixer_nid, + spec->mixer_merge_nid, 0); + if (path) { + print_nid_path("loopback-merge", path); + path->active = true; + spec->loopback_merge_path = + snd_hda_get_path_idx(codec, path); + } + } + return 0; } @@ -3847,6 +3860,9 @@ int snd_hda_gen_parse_auto_config(struct hda_codec *codec, parse_user_hints(codec); + if (spec->mixer_nid && !spec->mixer_merge_nid) + spec->mixer_merge_nid = spec->mixer_nid; + if (cfg != &spec->autocfg) { spec->autocfg = *cfg; cfg = &spec->autocfg; @@ -4673,6 +4689,11 @@ static void init_analog_input(struct hda_codec *codec) if (path) snd_hda_activate_path(codec, path, path->active, false); + path = snd_hda_get_path_from_idx(codec, + spec->loopback_merge_path); + if (path) + snd_hda_activate_path(codec, path, path->active, + false); } } } diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h index 980707fcd30a..d226856b2dfa 100644 --- a/sound/pci/hda/hda_generic.h +++ b/sound/pci/hda/hda_generic.h @@ -107,6 +107,7 @@ struct hda_gen_spec { hda_nid_t adc_nids[AUTO_CFG_MAX_INS]; hda_nid_t dig_in_nid; /* digital-in NID; optional */ hda_nid_t mixer_nid; /* analog-mixer NID */ + hda_nid_t mixer_merge_nid; /* aamix merge-point NID (optional) */ const char *input_labels[HDA_MAX_NUM_INPUTS]; int input_label_idxs[HDA_MAX_NUM_INPUTS]; @@ -163,6 +164,7 @@ struct hda_gen_spec { int digout_paths[AUTO_CFG_MAX_OUTS]; int input_paths[HDA_MAX_NUM_INPUTS][AUTO_CFG_MAX_INS]; int loopback_paths[HDA_MAX_NUM_INPUTS]; + int loopback_merge_path; int digin_path; /* auto-mic stuff */ diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index 9d82aab1e512..df8014b27596 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c @@ -3235,6 +3235,7 @@ static int ad1988_parse_auto_config(struct hda_codec *codec) spec = codec->spec; spec->gen.mixer_nid = 0x20; + spec->gen.mixer_merge_nid = 0x21; spec->beep_dev_nid = 0x10; set_beep_amp(spec, 0x10, 0, HDA_OUTPUT); err = ad198x_parse_auto_config(codec); @@ -5153,6 +5154,7 @@ static int ad1882_parse_auto_config(struct hda_codec *codec) spec = codec->spec; spec->gen.mixer_nid = 0x20; + spec->gen.mixer_merge_nid = 0x21; spec->beep_dev_nid = 0x10; set_beep_amp(spec, 0x10, 0, HDA_OUTPUT); err = ad198x_parse_auto_config(codec); -- cgit v1.2.3 From 3e367f155f3ba90b761497ab9e0343dc54778469 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 23 Jan 2013 17:07:23 +0100 Subject: ALSA: hda - Small code refactoring about path re-initialization Introduce a helper function to do the same thing. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 06e203df4059..921582d4fe05 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -735,6 +735,14 @@ static void set_pin_eapd(struct hda_codec *codec, hda_nid_t pin, bool enable) enable ? 0x02 : 0x00); } +/* re-initialize the path specified by the given path index */ +static void resume_path_from_idx(struct hda_codec *codec, int path_idx) +{ + struct nid_path *path = snd_hda_get_path_from_idx(codec, path_idx); + if (path) + snd_hda_activate_path(codec, path, path->active, false); +} + /* * Helper functions for creating mixer ctl elements @@ -4684,16 +4692,8 @@ static void init_analog_input(struct hda_codec *codec) /* init loopback inputs */ if (spec->mixer_nid) { - struct nid_path *path; - path = snd_hda_get_path_from_idx(codec, spec->loopback_paths[i]); - if (path) - snd_hda_activate_path(codec, path, - path->active, false); - path = snd_hda_get_path_from_idx(codec, - spec->loopback_merge_path); - if (path) - snd_hda_activate_path(codec, path, path->active, - false); + resume_path_from_idx(codec, spec->loopback_paths[i]); + resume_path_from_idx(codec, spec->loopback_merge_path); } } } @@ -4741,11 +4741,8 @@ static void init_digital(struct hda_codec *codec) set_output_and_unmute(codec, spec->digout_paths[i]); pin = spec->autocfg.dig_in_pin; if (pin) { - struct nid_path *path; restore_pin_ctl(codec, pin); - path = snd_hda_get_path_from_idx(codec, spec->digin_path); - if (path) - snd_hda_activate_path(codec, path, path->active, false); + resume_path_from_idx(codec, spec->digin_path); } } -- cgit v1.2.3 From 1f57825077dc2fa4e3d4a4e6b3af6c75f81112e7 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 23 Jan 2013 18:10:10 +0100 Subject: ALSA: hda - Add chained_before flag to the fixup entry Sometimes we want to call a fixup after applying other existing fixups, but currently the fixup chain mechanism allows only the call the others after the target fixup. This patch adds a new flag, chained_before, to struct hda_fixup, for allowing the chained call before the current execution. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_auto_parser.c | 18 +++++++++++------- sound/pci/hda/hda_local.h | 3 ++- 2 files changed, 13 insertions(+), 8 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_auto_parser.c b/sound/pci/hda/hda_auto_parser.c index 0088bb0b04de..96a05c4c28e1 100644 --- a/sound/pci/hda/hda_auto_parser.c +++ b/sound/pci/hda/hda_auto_parser.c @@ -696,20 +696,18 @@ static void set_pin_targets(struct hda_codec *codec, snd_hda_set_pin_ctl_cache(codec, cfg->nid, cfg->val); } -void snd_hda_apply_fixup(struct hda_codec *codec, int action) +static void apply_fixup(struct hda_codec *codec, int id, int action, int depth) { - int id = codec->fixup_id; #ifdef CONFIG_SND_DEBUG_VERBOSE const char *modelname = codec->fixup_name; #endif - int depth = 0; - - if (!codec->fixup_list) - return; while (id >= 0) { const struct hda_fixup *fix = codec->fixup_list + id; + if (fix->chained_before) + apply_fixup(codec, fix->chain_id, action, depth + 1); + switch (fix->type) { case HDA_FIXUP_PINS: if (action != HDA_FIXUP_ACT_PRE_PROBE || !fix->v.pins) @@ -749,13 +747,19 @@ void snd_hda_apply_fixup(struct hda_codec *codec, int action) codec->chip_name, fix->type); break; } - if (!fix->chained) + if (!fix->chained || fix->chained_before) break; if (++depth > 10) break; id = fix->chain_id; } } + +void snd_hda_apply_fixup(struct hda_codec *codec, int action) +{ + if (codec->fixup_list) + apply_fixup(codec, codec->fixup_id, action, 0); +} EXPORT_SYMBOL_HDA(snd_hda_apply_fixup); void snd_hda_pick_fixup(struct hda_codec *codec, diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index f92979c6b023..2ff62dcf2fcb 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -401,7 +401,8 @@ struct hda_model_fixup { struct hda_fixup { int type; - bool chained; + bool chained:1; /* call the chained fixup(s) after this */ + bool chained_before:1; /* call the chained fixup(s) before this */ int chain_id; union { const struct hda_pintbl *pins; -- cgit v1.2.3 From 5397145f4faeabbb7937ac784f734b06b20ed921 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 23 Jan 2013 18:21:37 +0100 Subject: ALSA: hda - Add auto-mute support to PB desktop Using the new chained_before flag, we can correct the headphone jack detection capability easily over the existing ALC880 6stack model (which disables the jack detection intentionally for compatibility reason). Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=901846 Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 446ed114443a..dc75607fc05b 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -1034,6 +1034,7 @@ enum { ALC880_FIXUP_6ST_BASE, ALC880_FIXUP_6ST, ALC880_FIXUP_6ST_DIG, + ALC880_FIXUP_6ST_AUTOMUTE, }; /* enable the volume-knob widget support on NID 0x21 */ @@ -1294,6 +1295,15 @@ static const struct hda_fixup alc880_fixups[] = { .chained = true, .chain_id = ALC880_FIXUP_6ST_BASE, }, + [ALC880_FIXUP_6ST_AUTOMUTE] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x1b, 0x0121401f }, /* HP with jack detect */ + { } + }, + .chained_before = true, + .chain_id = ALC880_FIXUP_6ST_BASE, + }, }; static const struct snd_pci_quirk alc880_fixup_tbl[] = { @@ -1308,7 +1318,7 @@ static const struct snd_pci_quirk alc880_fixup_tbl[] = { SND_PCI_QUIRK(0x1584, 0x9077, "Uniwill P53", ALC880_FIXUP_VOL_KNOB), SND_PCI_QUIRK(0x161f, 0x203d, "W810", ALC880_FIXUP_W810), SND_PCI_QUIRK(0x161f, 0x205d, "Medion Rim 2150", ALC880_FIXUP_MEDION_RIM), - SND_PCI_QUIRK(0x1631, 0xe011, "PB 13201056", ALC880_FIXUP_6ST), + SND_PCI_QUIRK(0x1631, 0xe011, "PB 13201056", ALC880_FIXUP_6ST_AUTOMUTE), SND_PCI_QUIRK(0x1734, 0x107c, "FSC F1734", ALC880_FIXUP_F1734), SND_PCI_QUIRK(0x1734, 0x1094, "FSC Amilo M1451G", ALC880_FIXUP_FUJITSU), SND_PCI_QUIRK(0x1734, 0x10ac, "FSC AMILO Xi 1526", ALC880_FIXUP_F1734), @@ -1371,6 +1381,7 @@ static const struct hda_model_fixup alc880_fixup_models[] = { {.id = ALC880_FIXUP_5ST_DIG, .name = "5stack-digout"}, {.id = ALC880_FIXUP_6ST, .name = "6stack"}, {.id = ALC880_FIXUP_6ST_DIG, .name = "6stack-digout"}, + {.id = ALC880_FIXUP_6ST_AUTOMUTE, .name = "6stack-automute"}, {} }; -- cgit v1.2.3 From 7dddf2aed8ffc1fa30df92b57981dea146cdfc0f Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 24 Jan 2013 16:31:35 +0100 Subject: ALSA: hda - Fix wrong arguments for path deactivation checks The arguments to call is_active_nid() in activate_amp() were swapped, and this resulted in the muted amp on some SPDIF output pins. Also, the index to be passed to is_active_nid() must be idx_to_check. Otherwise it checks the wrong connection in the case of implicit aamix connection paths. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 921582d4fe05..31ffd663f7d1 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -569,7 +569,7 @@ static bool has_amp_out(struct hda_codec *codec, struct nid_path *path, int idx) /* check whether the given (nid,dir,idx) is active */ static bool is_active_nid(struct hda_codec *codec, hda_nid_t nid, - unsigned int idx, unsigned int dir) + unsigned int dir, unsigned int idx) { struct hda_gen_spec *spec = codec->spec; int i, n; @@ -642,7 +642,7 @@ static void activate_amp(struct hda_codec *codec, hda_nid_t nid, int dir, unsigned int caps; unsigned int mask, val; - if (!enable && is_active_nid(codec, nid, dir, idx)) + if (!enable && is_active_nid(codec, nid, dir, idx_to_check)) return; caps = query_amp_caps(codec, nid, dir); -- cgit v1.2.3 From 25368c47aee6d909923001918041f2e94bfa02ef Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 24 Jan 2013 17:12:09 +0100 Subject: ALSA: hda/via - Fix wrong checks of power state bits AC_VERB_GET_POWER_STATE returns the combined bits of the actual state and the target state. Thus, comparing the obtained value directly with the target value can't work. The value has to be shifted and masked properly. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_via.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 9d9583ca5f5b..5bd4b0c10003 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -240,8 +240,10 @@ static void set_widgets_power_state(struct hda_codec *codec) static void update_power_state(struct hda_codec *codec, hda_nid_t nid, unsigned int parm) { - if (snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_POWER_STATE, 0) == parm) + unsigned int state = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_POWER_STATE, 0); + state = (state >> 4) & 0x0f; + if (state == parm) return; snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm); } @@ -251,8 +253,10 @@ static void update_conv_power_state(struct hda_codec *codec, hda_nid_t nid, { struct via_spec *spec = codec->spec; unsigned int format; - if (snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_POWER_STATE, 0) == parm) + unsigned int state = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_POWER_STATE, 0); + state = (state >> 4) & 0x0f; + if (state == parm) return; format = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0); if (format && (spec->dac_stream_tag[index] != format)) -- cgit v1.2.3 From 9419ab6b72325e20789a61004cf68dc9e909a009 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 24 Jan 2013 17:23:35 +0100 Subject: ALSA: hda - Add power state filtering Add a hook to struct hda_codec for filtering the target power state of each widget when powering up/down. The current hackish EAPD check is implemented as the default hook pointer, too. This allows codec drivers to implement own power filter. In the upcoming changes, the generic parser will have the better power filter based on the active paths. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_codec.c | 39 +++++++++++++++++++++++++-------------- sound/pci/hda/hda_codec.h | 7 +++++-- sound/pci/hda/patch_conexant.c | 2 +- sound/pci/hda/patch_sigmatel.c | 2 +- 4 files changed, 32 insertions(+), 18 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index e4e0501ab84a..19ff923b2431 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -1276,6 +1276,8 @@ static bool snd_hda_codec_get_supported_ps(struct hda_codec *codec, static unsigned int hda_set_power_state(struct hda_codec *codec, unsigned int power_state); +static unsigned int default_power_filter(struct hda_codec *codec, hda_nid_t nid, + unsigned int power_state); /** * snd_hda_codec_new - create a HDA codec @@ -1396,6 +1398,7 @@ int snd_hda_codec_new(struct hda_bus *bus, #endif codec->epss = snd_hda_codec_get_supported_ps(codec, fg, AC_PWRST_EPSS); + codec->power_filter = default_power_filter; /* power-up all before initialization */ hda_set_power_state(codec, AC_PWRST_D0); @@ -3649,29 +3652,23 @@ void snd_hda_codec_flush_cache(struct hda_codec *codec) EXPORT_SYMBOL_HDA(snd_hda_codec_flush_cache); void snd_hda_codec_set_power_to_all(struct hda_codec *codec, hda_nid_t fg, - unsigned int power_state, - bool eapd_workaround) + unsigned int power_state) { hda_nid_t nid = codec->start_nid; int i; for (i = 0; i < codec->num_nodes; i++, nid++) { unsigned int wcaps = get_wcaps(codec, nid); + unsigned int state = power_state; if (!(wcaps & AC_WCAP_POWER)) continue; - /* don't power down the widget if it controls eapd and - * EAPD_BTLENABLE is set. - */ - if (eapd_workaround && power_state == AC_PWRST_D3 && - get_wcaps_type(wcaps) == AC_WID_PIN && - (snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_EAPD)) { - int eapd = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_EAPD_BTLENABLE, 0); - if (eapd & 0x02) + if (codec->power_filter) { + state = codec->power_filter(codec, nid, power_state); + if (state != power_state && power_state == AC_PWRST_D3) continue; } snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, - power_state); + state); } } EXPORT_SYMBOL_HDA(snd_hda_codec_set_power_to_all); @@ -3718,6 +3715,21 @@ static unsigned int hda_sync_power_state(struct hda_codec *codec, return state; } +/* don't power down the widget if it controls eapd and EAPD_BTLENABLE is set */ +static unsigned int default_power_filter(struct hda_codec *codec, hda_nid_t nid, + unsigned int power_state) +{ + if (power_state == AC_PWRST_D3 && + get_wcaps_type(get_wcaps(codec, nid)) == AC_WID_PIN && + (snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_EAPD)) { + int eapd = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_EAPD_BTLENABLE, 0); + if (eapd & 0x02) + return AC_PWRST_D0; + } + return power_state; +} + /* * set power state of the codec, and return the power state */ @@ -3743,8 +3755,7 @@ static unsigned int hda_set_power_state(struct hda_codec *codec, snd_hda_codec_read(codec, fg, 0, AC_VERB_SET_POWER_STATE, power_state); - snd_hda_codec_set_power_to_all(codec, fg, power_state, - true); + snd_hda_codec_set_power_to_all(codec, fg, power_state); } state = hda_sync_power_state(codec, fg, power_state); if (!(state & AC_PWRST_ERROR)) diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index cc73287341de..fbedcf3c9d0b 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -886,6 +886,10 @@ struct hda_codec { spinlock_t power_lock; #endif + /* filter the requested power state per nid */ + unsigned int (*power_filter)(struct hda_codec *codec, hda_nid_t nid, + unsigned int power_state); + /* codec-specific additional proc output */ void (*proc_widget_hook)(struct snd_info_buffer *buffer, struct hda_codec *codec, hda_nid_t nid); @@ -1047,8 +1051,7 @@ extern const struct snd_pcm_chmap_elem snd_pcm_2_1_chmaps[]; void snd_hda_get_codec_name(struct hda_codec *codec, char *name, int namelen); void snd_hda_bus_reboot_notify(struct hda_bus *bus); void snd_hda_codec_set_power_to_all(struct hda_codec *codec, hda_nid_t fg, - unsigned int power_state, - bool eapd_workaround); + unsigned int power_state); int snd_hda_lock_devices(struct hda_bus *bus); void snd_hda_unlock_devices(struct hda_bus *bus); diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index d98d470b0f26..7d941ef54172 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -435,7 +435,7 @@ static void conexant_set_power(struct hda_codec *codec, hda_nid_t fg, /* partial workaround for "azx_get_response timeout" */ if (power_state == AC_PWRST_D0) msleep(10); - snd_hda_codec_set_power_to_all(codec, fg, power_state, true); + snd_hda_codec_set_power_to_all(codec, fg, power_state); } static int conexant_init(struct hda_codec *codec) diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 0aa0ceba0806..5895d8f9a546 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -3724,7 +3724,7 @@ static void stac_set_power_state(struct hda_codec *codec, hda_nid_t fg, } snd_hda_codec_read(codec, fg, 0, AC_VERB_SET_POWER_STATE, afg_power_state); - snd_hda_codec_set_power_to_all(codec, fg, power_state, true); + snd_hda_codec_set_power_to_all(codec, fg, power_state); } #else #define stac_suspend NULL -- cgit v1.2.3 From b9c590bbf1d7621c3f9feb6ac0992d638244d0b1 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 24 Jan 2013 17:27:32 +0100 Subject: ALSA: hda - Synchronize the power state at the end of codec init Put the power state synchronization at the end of the parsing of codec. This is necessary when the power filter is changed during the codec probe. Since the first power-up sequence is performed without the special filter, all widgets are supposed to be ON at this point. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_codec.c | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 19ff923b2431..2311114bf52c 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -3765,6 +3765,37 @@ static unsigned int hda_set_power_state(struct hda_codec *codec, return state; } +/* sync power states of all widgets; + * this is called at the end of codec parsing + */ +static void sync_power_up_states(struct hda_codec *codec) +{ + hda_nid_t nid = codec->start_nid; + int i; + + /* don't care if no or standard filter is used */ + if (!codec->power_filter || codec->power_filter == default_power_filter) + return; + + for (i = 0; i < codec->num_nodes; i++, nid++) { + unsigned int wcaps = get_wcaps(codec, nid); + unsigned int state, target; + if (!(wcaps & AC_WCAP_POWER)) + continue; + target = codec->power_filter(codec, nid, AC_PWRST_D0); + if (target == AC_PWRST_D0) + continue; + state = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_POWER_STATE, 0); + if (state & AC_PWRST_ERROR) + continue; + state = (state >> 4) & 0x0f; + if (state != target) + snd_hda_codec_write(codec, nid, 0, + AC_VERB_SET_POWER_STATE, target); + } +} + #ifdef CONFIG_SND_HDA_HWDEP /* execute additional init verbs */ static void hda_exec_init_verbs(struct hda_codec *codec) @@ -3952,6 +3983,7 @@ int snd_hda_codec_build_controls(struct hda_codec *codec) hda_jackpoll_work(&codec->jackpoll_work.work); else snd_hda_jack_report_sync(codec); /* call at the last init point */ + sync_power_up_states(codec); return 0; } -- cgit v1.2.3 From 9040d102da5635abc306372bb4dbffaba92c478e Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 24 Jan 2013 17:47:17 +0100 Subject: ALSA: hda - Add snd_hda_check_power_state() helper function ... for small refactoring. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_codec.c | 9 ++------- sound/pci/hda/hda_local.h | 13 +++++++++++++ sound/pci/hda/patch_via.c | 11 +++-------- 3 files changed, 18 insertions(+), 15 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 2311114bf52c..f82a64da2f1b 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -3779,18 +3779,13 @@ static void sync_power_up_states(struct hda_codec *codec) for (i = 0; i < codec->num_nodes; i++, nid++) { unsigned int wcaps = get_wcaps(codec, nid); - unsigned int state, target; + unsigned int target; if (!(wcaps & AC_WCAP_POWER)) continue; target = codec->power_filter(codec, nid, AC_PWRST_D0); if (target == AC_PWRST_D0) continue; - state = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_POWER_STATE, 0); - if (state & AC_PWRST_ERROR) - continue; - state = (state >> 4) & 0x0f; - if (state != target) + if (!snd_hda_check_power_state(codec, nid, target)) snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, target); } diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index 2ff62dcf2fcb..05f1d594d17b 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -657,6 +657,19 @@ int snd_hda_check_amp_list_power(struct hda_codec *codec, struct hda_loopback_check *check, hda_nid_t nid); +/* check whether the actual power state matches with the target state */ +static inline bool +snd_hda_check_power_state(struct hda_codec *codec, hda_nid_t nid, + unsigned int target_state) +{ + unsigned int state = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_POWER_STATE, 0); + if (state & AC_PWRST_ERROR) + return true; + state = (state >> 4) & 0x0f; + return (state != target_state); +} + /* * AMP control callbacks */ diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 5bd4b0c10003..9641c0e487bb 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -240,10 +240,7 @@ static void set_widgets_power_state(struct hda_codec *codec) static void update_power_state(struct hda_codec *codec, hda_nid_t nid, unsigned int parm) { - unsigned int state = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_POWER_STATE, 0); - state = (state >> 4) & 0x0f; - if (state == parm) + if (snd_hda_check_power_state(codec, nid, parm)) return; snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm); } @@ -253,10 +250,8 @@ static void update_conv_power_state(struct hda_codec *codec, hda_nid_t nid, { struct via_spec *spec = codec->spec; unsigned int format; - unsigned int state = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_POWER_STATE, 0); - state = (state >> 4) & 0x0f; - if (state == parm) + + if (snd_hda_check_power_state(codec, nid, parm)) return; format = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0); if (format && (spec->dac_stream_tag[index] != format)) -- cgit v1.2.3 From 55196fffc951059bb89f97ba53203acb9f87a6f1 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 24 Jan 2013 17:32:56 +0100 Subject: ALSA: hda - Implement path-based power filter to the generic parser This patch adds a better power filter hook for powering down unused widgets in the generic parser. The feature is enabled by setting hda_gen_spec.power_down_unused flag. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 73 +++++++++++++++++++++++++++++++++++++++++---- sound/pci/hda/hda_generic.h | 1 + 2 files changed, 68 insertions(+), 6 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 31ffd663f7d1..19d014a6a40e 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -153,6 +154,9 @@ static void parse_user_hints(struct hda_codec *codec) val = snd_hda_get_bool_hint(codec, "add_in_jack_modes"); if (val >= 0) spec->add_in_jack_modes = !!val; + val = snd_hda_get_bool_hint(codec, "power_down_unused"); + if (val >= 0) + spec->power_down_unused = !!val; if (!snd_hda_get_int_hint(codec, "mixer_nid", &val)) spec->mixer_nid = val; @@ -700,14 +704,23 @@ static void activate_amp_in(struct hda_codec *codec, struct nid_path *path, void snd_hda_activate_path(struct hda_codec *codec, struct nid_path *path, bool enable, bool add_aamix) { + struct hda_gen_spec *spec = codec->spec; int i; if (!enable) path->active = false; for (i = path->depth - 1; i >= 0; i--) { + hda_nid_t nid = path->path[i]; + if (enable && spec->power_down_unused) { + /* make sure the widget is powered up */ + if (!snd_hda_check_power_state(codec, nid, AC_PWRST_D0)) + snd_hda_codec_write(codec, nid, 0, + AC_VERB_SET_POWER_STATE, + AC_PWRST_D0); + } if (enable && path->multi[i]) - snd_hda_codec_write_cache(codec, path->path[i], 0, + snd_hda_codec_write_cache(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, path->idx[i]); if (has_amp_in(codec, path, i)) @@ -721,6 +734,33 @@ void snd_hda_activate_path(struct hda_codec *codec, struct nid_path *path, } EXPORT_SYMBOL_HDA(snd_hda_activate_path); +/* if the given path is inactive, put widgets into D3 (only if suitable) */ +static void path_power_down_sync(struct hda_codec *codec, struct nid_path *path) +{ + struct hda_gen_spec *spec = codec->spec; + bool changed; + int i; + + if (!spec->power_down_unused || path->active) + return; + + for (i = 0; i < path->depth; i++) { + hda_nid_t nid = path->path[i]; + if (!snd_hda_check_power_state(codec, nid, AC_PWRST_D3)) { + snd_hda_codec_write(codec, nid, 0, + AC_VERB_SET_POWER_STATE, + AC_PWRST_D3); + changed = true; + } + } + + if (changed) { + msleep(10); + snd_hda_codec_read(codec, path->path[0], 0, + AC_VERB_GET_POWER_STATE, 0); + } +} + /* turn on/off EAPD on the given pin */ static void set_pin_eapd(struct hda_codec *codec, hda_nid_t pin, bool enable) { @@ -2007,6 +2047,7 @@ static int set_multi_io(struct hda_codec *codec, int idx, bool output) set_pin_eapd(codec, nid, false); snd_hda_activate_path(codec, path, false, true); set_pin_target(codec, nid, spec->multi_io[idx].ctl_in, true); + path_power_down_sync(codec, path); } /* update jack retasking in case it modifies any of them */ @@ -2093,9 +2134,11 @@ static void update_aamix_paths(struct hda_codec *codec, bool do_mix, if (do_mix) { snd_hda_activate_path(codec, nomix_path, false, true); snd_hda_activate_path(codec, mix_path, true, true); + path_power_down_sync(codec, nomix_path); } else { snd_hda_activate_path(codec, mix_path, false, true); snd_hda_activate_path(codec, nomix_path, true, true); + path_power_down_sync(codec, mix_path); } } @@ -3356,7 +3399,7 @@ static int mux_select(struct hda_codec *codec, unsigned int adc_idx, { struct hda_gen_spec *spec = codec->spec; const struct hda_input_mux *imux; - struct nid_path *path; + struct nid_path *old_path, *path; imux = &spec->input_mux; if (!imux->num_items) @@ -3367,11 +3410,11 @@ static int mux_select(struct hda_codec *codec, unsigned int adc_idx, if (spec->cur_mux[adc_idx] == idx) return 0; - path = get_input_path(codec, adc_idx, spec->cur_mux[adc_idx]); - if (!path) + old_path = get_input_path(codec, adc_idx, spec->cur_mux[adc_idx]); + if (!old_path) return 0; - if (path->active) - snd_hda_activate_path(codec, path, false, false); + if (old_path->active) + snd_hda_activate_path(codec, old_path, false, false); spec->cur_mux[adc_idx] = idx; @@ -3389,6 +3432,7 @@ static int mux_select(struct hda_codec *codec, unsigned int adc_idx, snd_hda_activate_path(codec, path, true, false); if (spec->cap_sync_hook) spec->cap_sync_hook(codec, NULL); + path_power_down_sync(codec, old_path); return 1; } @@ -3853,6 +3897,20 @@ static int check_auto_mic_availability(struct hda_codec *codec) return 0; } +/* power_filter hook; make inactive widgets into power down */ +static unsigned int snd_hda_gen_path_power_filter(struct hda_codec *codec, + hda_nid_t nid, + unsigned int power_state) +{ + if (power_state != AC_PWRST_D0) + return power_state; + if (get_wcaps_type(get_wcaps(codec, nid)) >= AC_WID_POWER) + return power_state; + if (is_active_nid(codec, nid, HDA_OUTPUT, 0)) + return power_state; + return AC_PWRST_D3; +} + /* * Parse the given BIOS configuration and set up the hda_gen_spec @@ -3980,6 +4038,9 @@ int snd_hda_gen_parse_auto_config(struct hda_codec *codec, dig_only: parse_digital(codec); + if (spec->power_down_unused) + codec->power_filter = snd_hda_gen_path_power_filter; + return 1; } EXPORT_SYMBOL_HDA(snd_hda_gen_parse_auto_config); diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h index d226856b2dfa..065fcc77b860 100644 --- a/sound/pci/hda/hda_generic.h +++ b/sound/pci/hda/hda_generic.h @@ -211,6 +211,7 @@ struct hda_gen_spec { unsigned int add_stereo_mix_input:1; /* add aamix as a capture src */ unsigned int add_out_jack_modes:1; /* add output jack mode enum ctls */ unsigned int add_in_jack_modes:1; /* add input jack mode enum ctls */ + unsigned int power_down_unused:1; /* power down unused widgets */ /* other internal flags */ unsigned int no_analog:1; /* digital I/O only */ -- cgit v1.2.3 From f4f678d22219f5821f46bb78eb4da7f76f1899bc Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 24 Jan 2013 18:02:43 +0100 Subject: ALSA: hda - Enable power down of unused widgets for IDT codecs IDT codecs can work well with this new feature, so let's enable it. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_sigmatel.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 5895d8f9a546..1c49861b994f 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -3609,14 +3609,6 @@ static int stac_init(struct hda_codec *codec) } } - /* power down unused DACs */ - for (i = 0; i < spec->gen.num_all_dacs; i++) { - if (!snd_hda_get_nid_path(codec, spec->gen.all_dacs[i], 0)) - snd_hda_codec_write(codec, spec->gen.all_dacs[i], 0, - AC_VERB_SET_POWER_STATE, - AC_PWRST_D3); - } - return 0; } @@ -3871,6 +3863,7 @@ static int patch_stac92hd73xx(struct hda_codec *codec) spec->pwr_nids = stac92hd73xx_pwr_nids; spec->gen.own_eapd_ctl = 1; + spec->gen.power_down_unused = 1; codec->patch_ops = stac_patch_ops; @@ -3933,6 +3926,7 @@ static int patch_stac92hd83xxx(struct hda_codec *codec) spec = codec->spec; spec->linear_tone_beep = 0; spec->gen.own_eapd_ctl = 1; + spec->gen.power_down_unused = 1; spec->gen.mixer_nid = 0x1b; spec->digbeep_nid = 0x21; @@ -3976,6 +3970,7 @@ static int patch_stac92hd71bxx(struct hda_codec *codec) spec = codec->spec; spec->linear_tone_beep = 0; spec->gen.own_eapd_ctl = 1; + spec->gen.power_down_unused = 1; spec->gen.mixer_nid = 0x17; spec->have_spdif_mux = 1; -- cgit v1.2.3 From 86b2723725a2e186f5699d97cb20014fa893931f Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 25 Jan 2013 10:54:07 +0100 Subject: ALSA: Make snd_printd() and snd_printdd() inline MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Because currently snd_printd() and snd_printdd() macros are expanded to empty when CONFIG_SND_DEBUG=n, a compile warning like below appears sometimes, and we had to covert it by ugly ifdefs: sound/pci/hda/patch_sigmatel.c: In function ‘stac92hd71bxx_fixup_hp’: sound/pci/hda/patch_sigmatel.c:2434:24: warning: unused variable ‘spec’ [-Wunused-variable] For "fixing" these issues better, this patch replaces snd_printd() and snd_printdd() definitions with empty inline functions instead of macros. This should have the same effect but shut up warnings like above. But since we had already put ifdefs, changing to inline functions would trigger compile errors. So, such ifdefs is removed in this patch. In addition, snd_pci_quirk name field is defined only when CONFIG_SND_DEBUG_VERBOSE is set, and the reference to it in snd_printdd() argument triggers the build errors, too. For avoiding these errors, introduce a new macro snd_pci_quirk_name() that is defined no matter how the debug option is set. Reported-by: Stratos Karafotis Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_auto_parser.c | 2 -- sound/pci/hda/hda_generic.c | 2 -- 2 files changed, 4 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_auto_parser.c b/sound/pci/hda/hda_auto_parser.c index 96a05c4c28e1..a3ea76a4c9d2 100644 --- a/sound/pci/hda/hda_auto_parser.c +++ b/sound/pci/hda/hda_auto_parser.c @@ -698,9 +698,7 @@ static void set_pin_targets(struct hda_codec *codec, static void apply_fixup(struct hda_codec *codec, int id, int action, int depth) { -#ifdef CONFIG_SND_DEBUG_VERBOSE const char *modelname = codec->fixup_name; -#endif while (id >= 0) { const struct hda_fixup *fix = codec->fixup_list + id; diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 19d014a6a40e..c4ba3066a013 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -1579,9 +1579,7 @@ static void debug_show_configs(struct hda_codec *codec, struct auto_pin_cfg *cfg) { struct hda_gen_spec *spec = codec->spec; -#ifdef CONFIG_SND_DEBUG_VERBOSE static const char * const lo_type[3] = { "LO", "SP", "HP" }; -#endif int i; debug_badness("multi_outs = %x/%x/%x/%x : %x/%x/%x/%x (type %s)\n", -- cgit v1.2.3 From 664389dbd59eba1af2904c4dcbb7af774e6a3324 Mon Sep 17 00:00:00 2001 From: David Henningsson Date: Mon, 28 Jan 2013 11:29:17 +0100 Subject: ALSA: hda - Fix powermap for external mics on IDT codecs This patch fixes a regression of the external mic not working on HP Probook 4520s. Signed-off-by: David Henningsson Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_sigmatel.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 1c49861b994f..951fc0ddf75a 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -502,6 +502,13 @@ static void stac_line_automute(struct hda_codec *codec, jack_update_power(codec, jack); } +static void stac_mic_autoswitch(struct hda_codec *codec, + struct hda_jack_tbl *jack) +{ + snd_hda_gen_mic_autoswitch(codec, jack); + jack_update_power(codec, jack); +} + static void stac_vref_event(struct hda_codec *codec, struct hda_jack_tbl *event) { unsigned int data; @@ -3509,6 +3516,7 @@ static int stac_parse_auto_config(struct hda_codec *codec) spec->gen.automute_hook = stac_update_outputs; spec->gen.hp_automute_hook = stac_hp_automute; spec->gen.line_automute_hook = stac_line_automute; + spec->gen.mic_autoswitch_hook = stac_mic_autoswitch; err = snd_hda_gen_parse_auto_config(codec, &spec->gen.autocfg); if (err < 0) -- cgit v1.2.3 From 4e637c6e09fde6c1984a820621b5a77fad1acd34 Mon Sep 17 00:00:00 2001 From: Vitaliy Kulikov Date: Thu, 31 Jan 2013 17:58:59 -0600 Subject: ALSA: hda - add support for IDT 92HD95 HDA codec Signed-off-by: Vitaliy Kulikov Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_sigmatel.c | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 951fc0ddf75a..617ac1f542fb 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -3965,6 +3965,44 @@ static int patch_stac92hd83xxx(struct hda_codec *codec) return 0; } +static const hda_nid_t stac92hd95_pwr_nids[] = { + 0x0a, 0x0b, 0x0c, 0x0d +}; + +static int patch_stac92hd95(struct hda_codec *codec) +{ + struct sigmatel_spec *spec; + int err; + + err = alloc_stac_spec(codec); + if (err < 0) + return err; + + codec->epss = 0; /* longer delay needed for D3 */ + + spec = codec->spec; + spec->linear_tone_beep = 0; + spec->gen.own_eapd_ctl = 1; + spec->gen.power_down_unused = 1; + + spec->digbeep_nid = 0x19; + spec->pwr_nids = stac92hd95_pwr_nids; + spec->num_pwrs = ARRAY_SIZE(stac92hd95_pwr_nids); + spec->default_polarity = -1; /* no default cfg */ + + codec->patch_ops = stac_patch_ops; + + err = stac_parse_auto_config(codec); + if (err < 0) { + stac_free(codec); + return err; + } + + codec->proc_widget_hook = stac92hd_proc_hook; + + return 0; +} + static int patch_stac92hd71bxx(struct hda_codec *codec) { struct sigmatel_spec *spec; @@ -4337,6 +4375,7 @@ static const struct hda_codec_preset snd_hda_preset_sigmatel[] = { { .id = 0x111d7674, .name = "92HD73D1X5", .patch = patch_stac92hd73xx }, { .id = 0x111d7675, .name = "92HD73C1X5", .patch = patch_stac92hd73xx }, { .id = 0x111d7676, .name = "92HD73E1X5", .patch = patch_stac92hd73xx }, + { .id = 0x111d7695, .name = "92HD95", .patch = patch_stac92hd95 }, { .id = 0x111d76b0, .name = "92HD71B8X", .patch = patch_stac92hd71bxx }, { .id = 0x111d76b1, .name = "92HD71B8X", .patch = patch_stac92hd71bxx }, { .id = 0x111d76b2, .name = "92HD71B7X", .patch = patch_stac92hd71bxx }, -- cgit v1.2.3 From 4a7c516bf0cd697dbbee11db6258e3b3146e41a6 Mon Sep 17 00:00:00 2001 From: Wang Xingchao Date: Fri, 1 Feb 2013 22:42:19 +0800 Subject: ALSA - HDA: New PCI ID for Haswell ULT Add new PCI ID 0x0a0c for Haswell ULT platform. Signed-off-by: Wang Xingchao Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_intel.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index a85b3aa987fe..3d8df7c6a3b9 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -3615,6 +3615,8 @@ static DEFINE_PCI_DEVICE_TABLE(azx_ids) = { { PCI_DEVICE(0x8086, 0x9c21), .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH }, /* Haswell */ + { PCI_DEVICE(0x8086, 0x0a0c), + .driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH }, { PCI_DEVICE(0x8086, 0x0c0c), .driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH }, { PCI_DEVICE(0x8086, 0x0d0c), -- cgit v1.2.3 From 2ad779b7329d6894a80df94e693e72eaa0d56790 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 1 Feb 2013 14:01:27 +0100 Subject: ALSA: hda - Release assigned pin/cvt at error path of hdmi_pcm_open() If the driver detects and invalid ELD, it gives an open error. But it forgot to release the assigned pin, converter and spdif ctls before returning. Cc: Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_hdmi.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 807a2aa1ff38..1b3b4ee3e8a7 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -1100,8 +1100,12 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo, if (!static_hdmi_pcm && eld->eld_valid) { snd_hdmi_eld_update_pcm_info(eld, hinfo); if (hinfo->channels_min > hinfo->channels_max || - !hinfo->rates || !hinfo->formats) + !hinfo->rates || !hinfo->formats) { + per_cvt->assigned = 0; + hinfo->nid = 0; + snd_hda_spdif_ctls_unassign(codec, pin_idx); return -ENODEV; + } } /* Store the updated parameters */ -- cgit v1.2.3 From 20608731f479d48be6bcb88e727f360ddf98ddaf Mon Sep 17 00:00:00 2001 From: Anssi Hannula Date: Sun, 3 Feb 2013 17:55:45 +0200 Subject: ALSA: hda - Fix default multichannel HDMI mapping regression Commit d45e6889ee69456a4d5b1bbb32252f460cd48fa9 ("ALSA: hda - Provide the proper channel mapping for generic HDMI driver") added support for custom channel maps in the HDA HDMI driver. Due to a mistake in an 'if' condition the custom map is always used even when no such map has been set. This causes incorrect channel mapping for multichannel audio by default. Pass per_pin->chmap_set to hdmi_setup_channel_mapping() as a parameter so that it can use it for detecting if a custom map has been set instead of checking if map is NULL (which is never the case). Reported-by: Staffan Lindberg Signed-off-by: Anssi Hannula Cc: stable@vger.kernel.org Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_hdmi.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 807a2aa1ff38..e85959f72047 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -714,9 +714,10 @@ static void hdmi_setup_fake_chmap(unsigned char *map, int ca) static void hdmi_setup_channel_mapping(struct hda_codec *codec, hda_nid_t pin_nid, bool non_pcm, int ca, - int channels, unsigned char *map) + int channels, unsigned char *map, + bool chmap_set) { - if (!non_pcm && map) { + if (!non_pcm && chmap_set) { hdmi_manual_setup_channel_mapping(codec, pin_nid, channels, map); } else { @@ -905,7 +906,8 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec, int pin_idx, pin_nid, channels); hdmi_setup_channel_mapping(codec, pin_nid, non_pcm, ca, - channels, per_pin->chmap); + channels, per_pin->chmap, + per_pin->chmap_set); hdmi_stop_infoframe_trans(codec, pin_nid); hdmi_fill_audio_infoframe(codec, pin_nid, ai.bytes, sizeof(ai)); @@ -915,7 +917,8 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec, int pin_idx, * accordingly */ if (per_pin->non_pcm != non_pcm) hdmi_setup_channel_mapping(codec, pin_nid, non_pcm, ca, - channels, per_pin->chmap); + channels, per_pin->chmap, + per_pin->chmap_set); } per_pin->non_pcm = non_pcm; -- cgit v1.2.3 From 24f3cede590b2e072c64361867d220418a5df1a8 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 4 Feb 2013 18:25:51 +0100 Subject: ALSA: hda - Add new Kconfig CONFIG_SND_HDA_CODEC_CA0132_DSP ... to be less confusing for the update path. This new kconfig will choose CONFIG_SND_HDA_DSP_LOADER, which is basically a device-independent feature in hda_intel.c. Signed-off-by: Takashi Iwai --- sound/pci/hda/Kconfig | 22 ++++++++++++++-------- sound/pci/hda/patch_ca0132.c | 4 ++-- 2 files changed, 16 insertions(+), 10 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig index ba1dbd8e00a0..11b4b77a8215 100644 --- a/sound/pci/hda/Kconfig +++ b/sound/pci/hda/Kconfig @@ -15,6 +15,9 @@ menuconfig SND_HDA_INTEL if SND_HDA_INTEL +config SND_HDA_DSP_LOADER + bool + config SND_HDA_PREALLOC_SIZE int "Pre-allocated buffer size for HD-audio driver" range 0 32768 @@ -197,6 +200,17 @@ config SND_HDA_CODEC_CA0132 snd-hda-codec-ca0132. This module is automatically loaded at probing. +config SND_HDA_CODEC_CA0132_DSP + bool "Support new DSP code for CA0132 codec" + depends on SND_HDA_CODEC_CA0132 && FW_LOADER + select SND_HDA_DSP_LOADER + help + Say Y here to enable the DSP for Creative CA0132 for extended + features like equalizer or echo cancellation. + + Note that this option requires the external firmware file + (ctefx.bin). + config SND_HDA_CODEC_CMEDIA bool "Build C-Media HD-audio codec support" default y @@ -236,12 +250,4 @@ config SND_HDA_POWER_SAVE_DEFAULT The default time-out value in seconds for HD-audio automatic power-save mode. 0 means to disable the power-save mode. -config SND_HDA_DSP_LOADER - bool "Enable DSP firmware loader" - depends on FW_LOADER - default y - help - Say Y here to enable the DSP firmware loader, used by certain - codecs (e.g. CA0132) to transfer their DSP binaries to the hardware. - endif diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index 483850f64150..9d9040bbfb68 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -75,7 +75,7 @@ #define EFX_FILE "ctefx.bin" -#ifdef CONFIG_SND_HDA_DSP_LOADER +#ifdef CONFIG_SND_HDA_CODEC_CA0132_DSP MODULE_FIRMWARE(EFX_FILE); #endif @@ -4530,7 +4530,7 @@ static int ca0132_init(struct hda_codec *codec) ca0132_init_params(codec); ca0132_init_flags(codec); snd_hda_sequence_write(codec, spec->base_init_verbs); -#ifdef CONFIG_SND_HDA_DSP_LOADER +#ifdef CONFIG_SND_HDA_CODEC_CA0132_DSP ca0132_download_dsp(codec); #endif ca0132_refresh_widget_caps(codec); -- cgit v1.2.3 From f672f65a1c15b04e09d25701a8b5be47bad9376a Mon Sep 17 00:00:00 2001 From: David Henningsson Date: Tue, 5 Feb 2013 12:06:02 +0100 Subject: ALSA: hda - Fix phantom jacks on VT1708 The VT1708 has no unsol event capability, and polling is set using the "Jack Detect" alsamixer control. In order not to create phantom Jack controls, temporary enable jackpoll during build_controls. Signed-off-by: David Henningsson Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_via.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 9641c0e487bb..e934c498b95d 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -661,6 +661,18 @@ static int via_init(struct hda_codec *codec) return 0; } +static int vt1708_build_controls(struct hda_codec *codec) +{ + /* In order not to create "Phantom Jack" controls, + temporary enable jackpoll */ + int err; + int old_interval = codec->jackpoll_interval; + codec->jackpoll_interval = msecs_to_jiffies(100); + err = via_build_controls(codec); + codec->jackpoll_interval = old_interval; + return err; +} + static int vt1708_build_pcms(struct hda_codec *codec) { struct via_spec *spec = codec->spec; @@ -723,6 +735,7 @@ static int patch_vt1708(struct hda_codec *codec) spec->init_verbs[spec->num_iverbs++] = vt1708_init_verbs; codec->patch_ops = via_patch_ops; + codec->patch_ops.build_controls = vt1708_build_controls; codec->patch_ops.build_pcms = vt1708_build_pcms; /* clear jackpoll_interval again; it's set dynamically */ -- cgit v1.2.3 From 05dc0fc9d01537a66d9a0cffe2e96296d8f4c7ac Mon Sep 17 00:00:00 2001 From: David Henningsson Date: Tue, 5 Feb 2013 12:06:03 +0100 Subject: ALSA: hda - detect jacks on VT1708 even when no streams are active These days, GUIs such as Gnome sound settings want to be able to show the correct jack status even when no streams are currently running. I doubt this gives any measurable difference in power, but if it does, the "Jack Detect" control can still be used to turn polling off. Signed-off-by: David Henningsson Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_via.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index e934c498b95d..ca7d962a08a6 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -217,8 +217,7 @@ static void vt1708_update_hp_work(struct hda_codec *codec) struct via_spec *spec = codec->spec; if (spec->codec_type != VT1708 || !spec->gen.autocfg.hp_outs) return; - if (spec->vt1708_jack_detect && - (spec->gen.active_streams || hp_detect_with_aa(codec))) { + if (spec->vt1708_jack_detect) { if (!spec->hp_work_active) { codec->jackpoll_interval = msecs_to_jiffies(100); snd_hda_codec_write(codec, 0x1, 0, 0xf81, 0); -- cgit v1.2.3 From f9afed1f7fcadcad2b91f79aa81adf9456864117 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 6 Feb 2013 12:39:06 +0100 Subject: ALSA: hda - Apply mic-mute LED fixup for new HP laptops It's mostly harmless to apply it for new models even if they have no mic mute LED (just toggling an unused GPIO pin). Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_sigmatel.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 617ac1f542fb..83d5335ac348 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -2209,6 +2209,8 @@ static const struct snd_pci_quirk stac92hd83xxx_fixup_tbl[] = { "HP Envy Spectre", STAC_HP_ENVY_BASS), SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x18df, "HP Folio", STAC_92HD83XXX_HP_MIC_LED), + SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xff00, 0x1900, + "HP", STAC_92HD83XXX_HP_MIC_LED), SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3388, "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD), SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3389, -- cgit v1.2.3 From 46a144818acd8b340bdc6ccf12255c7fb301f0ee Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 7 Feb 2013 09:58:11 +0100 Subject: ALSA: hda - Enable loopback accounts for CONFIG_PM=n, too The loopback list is referred by the VIA codec driver no matter whether CONFIG_PM is set or not, thus we need to enable it always. Otherwise it gets compile errors. Reported-by: Randy Dunlap Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 4 ---- sound/pci/hda/hda_generic.h | 3 +-- 2 files changed, 1 insertion(+), 6 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index c4ba3066a013..c2cd3d6b6003 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -2483,7 +2483,6 @@ static int create_in_jack_mode(struct hda_codec *codec, hda_nid_t pin) * Parse input paths */ -#ifdef CONFIG_PM /* add the powersave loopback-list entry */ static void add_loopback_list(struct hda_gen_spec *spec, hda_nid_t mix, int idx) { @@ -2498,9 +2497,6 @@ static void add_loopback_list(struct hda_gen_spec *spec, hda_nid_t mix, int idx) spec->num_loopbacks++; spec->loopback.amplist = spec->loopback_list; } -#else -#define add_loopback_list(spec, mix, idx) /* NOP */ -#endif /* create input playback/capture controls for the given pin */ static int new_analog_input(struct hda_codec *codec, int input_idx, diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h index 065fcc77b860..d5348dd7a14f 100644 --- a/sound/pci/hda/hda_generic.h +++ b/sound/pci/hda/hda_generic.h @@ -226,11 +226,10 @@ struct hda_gen_spec { hda_nid_t vmaster_nid; unsigned int vmaster_tlv[4]; struct hda_vmaster_mute_hook vmaster_mute; -#ifdef CONFIG_PM + struct hda_loopback_check loopback; int num_loopbacks; struct hda_amp_list loopback_list[8]; -#endif /* multi-io */ int multi_ios; -- cgit v1.2.3 From 0186f4f4f248d00a2bfcd7c305cfec12fa8e5e30 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 7 Feb 2013 10:45:11 +0100 Subject: ALSA: hda - Use generic array for loopback list management Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 18 +++++++++++------- sound/pci/hda/hda_generic.h | 3 +-- sound/pci/hda/patch_via.c | 8 +++++--- 3 files changed, 17 insertions(+), 12 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index c2cd3d6b6003..6af5aded1ed5 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -42,6 +42,7 @@ int snd_hda_gen_spec_init(struct hda_gen_spec *spec) { snd_array_init(&spec->kctls, sizeof(struct snd_kcontrol_new), 32); snd_array_init(&spec->paths, sizeof(struct nid_path), 8); + snd_array_init(&spec->loopback_list, sizeof(struct hda_amp_list), 8); mutex_init(&spec->pcm_mutex); return 0; } @@ -82,6 +83,7 @@ void snd_hda_gen_spec_free(struct hda_gen_spec *spec) return; free_kctls(spec); snd_array_free(&spec->paths); + snd_array_free(&spec->loopback_list); } EXPORT_SYMBOL_HDA(snd_hda_gen_spec_free); @@ -2484,18 +2486,18 @@ static int create_in_jack_mode(struct hda_codec *codec, hda_nid_t pin) */ /* add the powersave loopback-list entry */ -static void add_loopback_list(struct hda_gen_spec *spec, hda_nid_t mix, int idx) +static int add_loopback_list(struct hda_gen_spec *spec, hda_nid_t mix, int idx) { struct hda_amp_list *list; - if (spec->num_loopbacks >= ARRAY_SIZE(spec->loopback_list) - 1) - return; - list = spec->loopback_list + spec->num_loopbacks; + list = snd_array_new(&spec->loopback_list); + if (!list) + return -ENOMEM; list->nid = mix; list->dir = HDA_INPUT; list->idx = idx; - spec->num_loopbacks++; - spec->loopback.amplist = spec->loopback_list; + spec->loopback.amplist = spec->loopback_list.list; + return 0; } /* create input playback/capture controls for the given pin */ @@ -2536,7 +2538,9 @@ static int new_analog_input(struct hda_codec *codec, int input_idx, } path->active = true; - add_loopback_list(spec, mix_nid, idx); + err = add_loopback_list(spec, mix_nid, idx); + if (err < 0) + return err; if (spec->mixer_nid != spec->mixer_merge_nid && !spec->loopback_merge_path) { diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h index d5348dd7a14f..009b57be96d3 100644 --- a/sound/pci/hda/hda_generic.h +++ b/sound/pci/hda/hda_generic.h @@ -228,8 +228,7 @@ struct hda_gen_spec { struct hda_vmaster_mute_hook vmaster_mute; struct hda_loopback_check loopback; - int num_loopbacks; - struct hda_amp_list loopback_list[8]; + struct snd_array loopback_list; /* multi-io */ int multi_ios; diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index ca7d962a08a6..c35338a8771d 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -355,10 +355,12 @@ static bool is_aa_path_mute(struct hda_codec *codec) { struct via_spec *spec = codec->spec; const struct hda_amp_list *p; - int i, ch, v; + int ch, v; - for (i = 0; i < spec->gen.num_loopbacks; i++) { - p = &spec->gen.loopback_list[i]; + p = spec->gen.loopback.amplist; + if (!p) + return true; + for (; p->nid; p++) { for (ch = 0; ch < 2; ch++) { v = snd_hda_codec_amp_read(codec, p->nid, ch, p->dir, p->idx); -- cgit v1.2.3 From 9a0869f4028916a164b1ba600e819c1dcd7c17a2 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 7 Feb 2013 12:41:40 +0100 Subject: ALSA: hda - Fix misc compile warnings in patch_ca0132.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit sound/pci/hda/patch_ca0132.c: In function ‘ca0132_is_vnode_effective’: sound/pci/hda/patch_ca0132.c:3331:15: warning: ‘nid’ may be used uninitialized in this function [-Wmaybe-uninitialized] sound/pci/hda/patch_ca0132.c:4345:13: warning: ‘ca0132_download_dsp’ defined but not used [-Wunused-function] Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_ca0132.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index 9d9040bbfb68..639a2829112f 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -3312,25 +3312,22 @@ static bool ca0132_is_vnode_effective(struct hda_codec *codec, { struct ca0132_spec *spec = codec->spec; hda_nid_t nid; - bool effective = false; switch (vnid) { case VNID_SPK: nid = spec->shared_out_nid; - effective = true; break; case VNID_MIC: nid = spec->shared_mic_nid; - effective = true; break; default: - break; + return false; } - if (effective && shared_nid) + if (shared_nid) *shared_nid = nid; - return effective; + return true; } /* @@ -4346,6 +4343,9 @@ static void ca0132_download_dsp(struct hda_codec *codec) { struct ca0132_spec *spec = codec->spec; +#ifndef CONFIG_SND_HDA_CODEC_CA0132_DSP + return; /* NOP */ +#endif spec->dsp_state = DSP_DOWNLOAD_INIT; if (spec->dsp_state == DSP_DOWNLOAD_INIT) { @@ -4530,9 +4530,7 @@ static int ca0132_init(struct hda_codec *codec) ca0132_init_params(codec); ca0132_init_flags(codec); snd_hda_sequence_write(codec, spec->base_init_verbs); -#ifdef CONFIG_SND_HDA_CODEC_CA0132_DSP ca0132_download_dsp(codec); -#endif ca0132_refresh_widget_caps(codec); ca0132_setup_defaults(codec); ca0132_init_analog_mic2(codec); -- cgit v1.2.3 From c1279f8787f9cddd2f4a7d6abc15375b30b80501 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 7 Feb 2013 17:36:22 +0100 Subject: ALSA: hda - Set non-snoop for Creative HD-audio controllers ... looks like we need this for stable operations. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_intel.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 4b099c603fe1..b5d5b209babe 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -3263,6 +3263,9 @@ static void azx_check_snoop_available(struct azx *chip) /* new ATI HDMI requires non-snoop */ snoop = false; break; + case AZX_DRIVER_CTHDA: + snoop = false; + break; } if (snoop != chip->snoop) { -- cgit v1.2.3 From 4eea30914facd2c99061cd70e5b05d3c76c743a2 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 7 Feb 2013 18:18:19 +0100 Subject: ALSA: hda - Remove limit of widget connections Currently we set the max number of connections to be 32, but there seems codec that gives longer connection lists like AD1988, and we see errors in proc output and else. (Though, in the case of AD1988, it's a list of all codecs connected to a single vendor widget, so this must be something fishy, but it's still valid from the h/w design POV.) This patch tries to remove this restriction. For efficiency, we still use the fixed size array in the parser, but takes a dynamic array when the size is reported to be greater than that. Now the fixed array size is found only in patch_hdmi.c, but it should be fine, as the codec itself can't support so many pins. Reported-by: Raymond Yau Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_codec.c | 62 ++++++++++++++++++++++++++++++---------------- sound/pci/hda/hda_codec.h | 4 +-- sound/pci/hda/hda_proc.c | 20 ++++++++++++--- sound/pci/hda/patch_hdmi.c | 3 +++ 4 files changed, 61 insertions(+), 28 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index f82a64da2f1b..3c925145c119 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -382,13 +382,23 @@ static void remove_conn_list(struct hda_codec *codec) /* read the connection and add to the cache */ static int read_and_add_raw_conns(struct hda_codec *codec, hda_nid_t nid) { - hda_nid_t list[HDA_MAX_CONNECTIONS]; + hda_nid_t list[32]; + hda_nid_t *result = list; int len; len = snd_hda_get_raw_connections(codec, nid, list, ARRAY_SIZE(list)); - if (len < 0) - return len; - return snd_hda_override_conn_list(codec, nid, len, list); + if (len == -ENOSPC) { + len = snd_hda_get_num_raw_conns(codec, nid); + result = kmalloc(sizeof(hda_nid_t) * len, GFP_KERNEL); + if (!result) + return -ENOMEM; + len = snd_hda_get_raw_connections(codec, nid, result, len); + } + if (len >= 0) + len = snd_hda_override_conn_list(codec, nid, len, result); + if (result != list) + kfree(result); + return len; } /** @@ -466,6 +476,27 @@ int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid, } EXPORT_SYMBOL_HDA(snd_hda_get_connections); +/* return CONNLIST_LEN parameter of the given widget */ +static unsigned int get_num_conns(struct hda_codec *codec, hda_nid_t nid) +{ + unsigned int wcaps = get_wcaps(codec, nid); + unsigned int parm; + + if (!(wcaps & AC_WCAP_CONN_LIST) && + get_wcaps_type(wcaps) != AC_WID_VOL_KNB) + return 0; + + parm = snd_hda_param_read(codec, nid, AC_PAR_CONNLIST_LEN); + if (parm == -1) + parm = 0; + return parm; +} + +int snd_hda_get_num_raw_conns(struct hda_codec *codec, hda_nid_t nid) +{ + return get_num_conns(codec, nid) & AC_CLIST_LENGTH; +} + /** * snd_hda_get_raw_connections - copy connection list without cache * @codec: the HDA codec @@ -483,19 +514,16 @@ int snd_hda_get_raw_connections(struct hda_codec *codec, hda_nid_t nid, unsigned int parm; int i, conn_len, conns; unsigned int shift, num_elems, mask; - unsigned int wcaps; hda_nid_t prev_nid; int null_count = 0; if (snd_BUG_ON(!conn_list || max_conns <= 0)) return -EINVAL; - wcaps = get_wcaps(codec, nid); - if (!(wcaps & AC_WCAP_CONN_LIST) && - get_wcaps_type(wcaps) != AC_WID_VOL_KNB) + parm = get_num_conns(codec, nid); + if (!parm) return 0; - parm = snd_hda_param_read(codec, nid, AC_PAR_CONNLIST_LEN); if (parm & AC_CLIST_LONG) { /* long form */ shift = 16; @@ -552,21 +580,13 @@ int snd_hda_get_raw_connections(struct hda_codec *codec, hda_nid_t nid, continue; } for (n = prev_nid + 1; n <= val; n++) { - if (conns >= max_conns) { - snd_printk(KERN_ERR "hda_codec: " - "Too many connections %d for NID 0x%x\n", - conns, nid); - return -EINVAL; - } + if (conns >= max_conns) + return -ENOSPC; conn_list[conns++] = n; } } else { - if (conns >= max_conns) { - snd_printk(KERN_ERR "hda_codec: " - "Too many connections %d for NID 0x%x\n", - conns, nid); - return -EINVAL; - } + if (conns >= max_conns) + return -ENOSPC; conn_list[conns++] = val; } prev_nid = val; diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index 6c592722efcd..0be18263801b 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -551,9 +551,6 @@ enum { AC_JACK_PORT_BOTH, }; -/* max. connections to a widget */ -#define HDA_MAX_CONNECTIONS 32 - /* max. codec address */ #define HDA_MAX_CODEC_ADDRESS 0x0f @@ -958,6 +955,7 @@ snd_hda_get_num_conns(struct hda_codec *codec, hda_nid_t nid) { return snd_hda_get_connections(codec, nid, NULL, 0); } +int snd_hda_get_num_raw_conns(struct hda_codec *codec, hda_nid_t nid); int snd_hda_get_raw_connections(struct hda_codec *codec, hda_nid_t nid, hda_nid_t *conn_list, int max_conns); int snd_hda_get_conn_list(struct hda_codec *codec, hda_nid_t nid, diff --git a/sound/pci/hda/hda_proc.c b/sound/pci/hda/hda_proc.c index 5e02f26606b6..0fee8fae590a 100644 --- a/sound/pci/hda/hda_proc.c +++ b/sound/pci/hda/hda_proc.c @@ -22,6 +22,7 @@ */ #include +#include #include #include "hda_codec.h" #include "hda_local.h" @@ -623,7 +624,7 @@ static void print_codec_info(struct snd_info_entry *entry, snd_hda_param_read(codec, nid, AC_PAR_AUDIO_WIDGET_CAP); unsigned int wid_type = get_wcaps_type(wid_caps); - hda_nid_t conn[HDA_MAX_CONNECTIONS]; + hda_nid_t *conn = NULL; int conn_len = 0; snd_iprintf(buffer, "Node 0x%02x [%s] wcaps 0x%x:", nid, @@ -660,9 +661,18 @@ static void print_codec_info(struct snd_info_entry *entry, if (wid_type == AC_WID_VOL_KNB) wid_caps |= AC_WCAP_CONN_LIST; - if (wid_caps & AC_WCAP_CONN_LIST) - conn_len = snd_hda_get_raw_connections(codec, nid, conn, - HDA_MAX_CONNECTIONS); + if (wid_caps & AC_WCAP_CONN_LIST) { + conn_len = snd_hda_get_num_raw_conns(codec, nid); + if (conn_len > 0) { + conn = kmalloc(sizeof(hda_nid_t) * conn_len, + GFP_KERNEL); + if (!conn) + return; + if (snd_hda_get_raw_connections(codec, nid, conn, + conn_len) < 0) + conn_len = 0; + } + } if (wid_caps & AC_WCAP_IN_AMP) { snd_iprintf(buffer, " Amp-In caps: "); @@ -735,6 +745,8 @@ static void print_codec_info(struct snd_info_entry *entry, if (codec->proc_widget_hook) codec->proc_widget_hook(buffer, codec, nid); + + kfree(conn); } snd_hda_power_down(codec); } diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 85236da79046..899c4fbbfd83 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -64,6 +64,9 @@ struct hdmi_spec_per_cvt { unsigned int maxbps; }; +/* max. connections to a widget */ +#define HDA_MAX_CONNECTIONS 32 + struct hdmi_spec_per_pin { hda_nid_t pin_nid; int num_mux_nids; -- cgit v1.2.3 From a15d05db41b1d5c9f2c3af11dfbfd46818cc64ba Mon Sep 17 00:00:00 2001 From: Mengdong Lin Date: Fri, 8 Feb 2013 17:09:31 -0500 Subject: ALSA: hda - Support rereading widgets under the function group A codec may allow software to hide some unused pin/cvt widgets. Sometimes BIOS does not enable the hidden widgets properly although they are needed for the board. Thus the driver need to enable them as a board-specific fixup and the whole tree will change. This patch implements a common code for rereading codec widgets. So the fixup code can call it after enabling the hidden widgets. Signed-off-by: Mengdong Lin Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_codec.c | 24 ++++++++++++++++++++++++ sound/pci/hda/hda_codec.h | 1 + 2 files changed, 25 insertions(+) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 3c925145c119..e80f83568291 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -1441,6 +1441,30 @@ int snd_hda_codec_new(struct hda_bus *bus, } EXPORT_SYMBOL_HDA(snd_hda_codec_new); +int snd_hda_codec_update_widgets(struct hda_codec *codec) +{ + hda_nid_t fg; + int err; + + /* Assume the function group node does not change, + * only the widget nodes may change. + */ + kfree(codec->wcaps); + fg = codec->afg ? codec->afg : codec->mfg; + err = read_widget_caps(codec, fg); + if (err < 0) { + snd_printk(KERN_ERR "hda_codec: cannot malloc\n"); + return err; + } + + snd_array_free(&codec->init_pins); + err = read_pin_defaults(codec); + + return err; +} +EXPORT_SYMBOL_HDA(snd_hda_codec_update_widgets); + + /** * snd_hda_codec_configure - (Re-)configure the HD-audio codec * @codec: the HDA codec diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index 0be18263801b..e8c9442d27ad 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -935,6 +935,7 @@ int snd_hda_bus_new(struct snd_card *card, const struct hda_bus_template *temp, int snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr, struct hda_codec **codecp); int snd_hda_codec_configure(struct hda_codec *codec); +int snd_hda_codec_update_widgets(struct hda_codec *codec); /* * low level functions -- cgit v1.2.3 From 1611a9c931e95fab871a33beba49cc9ea39bbba8 Mon Sep 17 00:00:00 2001 From: Mengdong Lin Date: Fri, 8 Feb 2013 17:09:52 -0500 Subject: ALSA: hda - Add fixup for Haswell to enable all pin and convertor widgets Some Haswell machines support more than one display outputs (HDMI or DP), but its BIOS may not enable the codec's 2nd and 3rd pin and output cvt widgets. This patch implements a board-specific fixup for Intel Haswell Machines: If the hidden pins are not enabled by BIOS, the driver will enable them and call common code to update the codec tree. Signed-off-by: Mengdong Lin Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_hdmi.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 899c4fbbfd83..54243c4a4e9d 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -1714,6 +1714,57 @@ static void intel_haswell_fixup_connect_list(struct hda_codec *codec) snd_hda_override_conn_list(codec, 0x07, 3, list); } +#define INTEL_VENDOR_NID 0x08 +#define INTEL_GET_VENDOR_VERB 0xf81 +#define INTEL_SET_VENDOR_VERB 0x781 +#define INTEL_EN_DP12 0x02 /* enable DP 1.2 features */ +#define INTEL_EN_ALL_PIN_CVTS 0x01 /* enable 2nd & 3rd pins and convertors */ + +static void intel_haswell_enable_all_pins(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + unsigned int vendor_param; + + if (action != HDA_FIXUP_ACT_PRE_PROBE) + return; + vendor_param = snd_hda_codec_read(codec, INTEL_VENDOR_NID, 0, + INTEL_GET_VENDOR_VERB, 0); + if (vendor_param == -1 || vendor_param & INTEL_EN_ALL_PIN_CVTS) + return; + + vendor_param |= INTEL_EN_ALL_PIN_CVTS; + vendor_param = snd_hda_codec_read(codec, INTEL_VENDOR_NID, 0, + INTEL_SET_VENDOR_VERB, vendor_param); + if (vendor_param == -1) + return; + + snd_hda_codec_update_widgets(codec); + return; +} + + +/* available models for fixup */ +enum { + INTEL_HASWELL, +}; + +static const struct hda_model_fixup hdmi_models[] = { + {.id = INTEL_HASWELL, .name = "Haswell"}, + {} +}; + +static const struct snd_pci_quirk hdmi_fixup_tbl[] = { + SND_PCI_QUIRK(0x8086, 0x2010, "Haswell", INTEL_HASWELL), + {} /* terminator */ +}; + +static const struct hda_fixup hdmi_fixups[] = { + [INTEL_HASWELL] = { + .type = HDA_FIXUP_FUNC, + .v.func = intel_haswell_enable_all_pins, + }, +}; + static int patch_generic_hdmi(struct hda_codec *codec) { @@ -1725,6 +1776,9 @@ static int patch_generic_hdmi(struct hda_codec *codec) codec->spec = spec; + snd_hda_pick_fixup(codec, hdmi_models, hdmi_fixup_tbl, hdmi_fixups); + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); + if (codec->vendor_id == 0x80862807) intel_haswell_fixup_connect_list(codec); -- cgit v1.2.3 From c88d4e84e639df9a9640ecff71de2501a84d1f48 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 8 Feb 2013 17:10:04 -0500 Subject: ALSA: hda - Yet another fix for broken HSW HDMI pin connections A Haswell test machine showed that the invalid connection list, but this time it has only a single pin on the codec, thus the former fixup code doesn't work as it assumes the three pins blindly. This patch splits the former fixup code to two parts: - Enable eDP 1.2 for Haswell codec - Fix the connection list of pins on Haswell codec; the converter list is recorded dynamically in hdmi_add_cvt(), and applied in hdmi_add_pin() Signed-off-by: Mengdong Lin Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_hdmi.c | 56 ++++++++++++++++++++++++++++++---------------- 1 file changed, 37 insertions(+), 19 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 54243c4a4e9d..b9af281b2ba4 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -84,6 +84,7 @@ struct hdmi_spec_per_pin { struct hdmi_spec { int num_cvts; struct hdmi_spec_per_cvt cvts[MAX_HDMI_CVTS]; + hda_nid_t cvt_nids[MAX_HDMI_CVTS]; int num_pins; struct hdmi_spec_per_pin pins[MAX_HDMI_PINS]; @@ -1197,6 +1198,9 @@ static void hdmi_repoll_eld(struct work_struct *work) hdmi_present_sense(per_pin, per_pin->repoll_count); } +static void intel_haswell_fixup_connect_list(struct hda_codec *codec, + hda_nid_t nid); + static int hdmi_add_pin(struct hda_codec *codec, hda_nid_t pin_nid) { struct hdmi_spec *spec = codec->spec; @@ -1216,6 +1220,9 @@ static int hdmi_add_pin(struct hda_codec *codec, hda_nid_t pin_nid) if (snd_BUG_ON(spec->num_pins >= MAX_HDMI_PINS)) return -E2BIG; + if (codec->vendor_id == 0x80862807) + intel_haswell_fixup_connect_list(codec, pin_nid); + pin_idx = spec->num_pins; per_pin = &spec->pins[pin_idx]; @@ -1263,7 +1270,7 @@ static int hdmi_add_cvt(struct hda_codec *codec, hda_nid_t cvt_nid) if (err < 0) return err; - spec->num_cvts++; + spec->cvt_nids[spec->num_cvts++] = cvt_nid; return 0; } @@ -1691,27 +1698,22 @@ static const struct hda_codec_ops generic_hdmi_patch_ops = { .unsol_event = hdmi_unsol_event, }; -static void intel_haswell_fixup_connect_list(struct hda_codec *codec) -{ - unsigned int vendor_param; - hda_nid_t list[3] = {0x2, 0x3, 0x4}; - vendor_param = snd_hda_codec_read(codec, 0x08, 0, 0xf81, 0); - if (vendor_param == -1 || vendor_param & 0x02) - return; - - /* enable DP1.2 mode */ - vendor_param |= 0x02; - snd_hda_codec_read(codec, 0x08, 0, 0x781, vendor_param); +static void intel_haswell_fixup_connect_list(struct hda_codec *codec, + hda_nid_t nid) +{ + struct hdmi_spec *spec = codec->spec; + hda_nid_t conns[4]; + int nconns; - vendor_param = snd_hda_codec_read(codec, 0x08, 0, 0xf81, 0); - if (vendor_param == -1 || !(vendor_param & 0x02)) + nconns = snd_hda_get_connections(codec, nid, conns, ARRAY_SIZE(conns)); + if (nconns == spec->num_cvts && + !memcmp(conns, spec->cvt_nids, spec->num_cvts * sizeof(hda_nid_t))) return; - /* override 3 pins connection list */ - snd_hda_override_conn_list(codec, 0x05, 3, list); - snd_hda_override_conn_list(codec, 0x06, 3, list); - snd_hda_override_conn_list(codec, 0x07, 3, list); + /* override pins connection list */ + snd_printdd("hdmi: haswell: override pin connection 0x%x\n", nid); + snd_hda_override_conn_list(codec, nid, spec->num_cvts, spec->cvt_nids); } #define INTEL_VENDOR_NID 0x08 @@ -1742,6 +1744,22 @@ static void intel_haswell_enable_all_pins(struct hda_codec *codec, return; } +static void intel_haswell_fixup_enable_dp12(struct hda_codec *codec) +{ + unsigned int vendor_param; + + vendor_param = snd_hda_codec_read(codec, INTEL_VENDOR_NID, 0, + INTEL_GET_VENDOR_VERB, 0); + if (vendor_param == -1 || vendor_param & INTEL_EN_DP12) + return; + + /* enable DP1.2 mode */ + vendor_param |= INTEL_EN_DP12; + snd_hda_codec_write_cache(codec, INTEL_VENDOR_NID, 0, + INTEL_SET_VENDOR_VERB, vendor_param); +} + + /* available models for fixup */ enum { @@ -1780,7 +1798,7 @@ static int patch_generic_hdmi(struct hda_codec *codec) snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); if (codec->vendor_id == 0x80862807) - intel_haswell_fixup_connect_list(codec); + intel_haswell_fixup_enable_dp12(codec); if (hdmi_parse_codec(codec) < 0) { codec->spec = NULL; -- cgit v1.2.3 From 884b088f61b64c22a9a34d4ef960ab9c807d8efd Mon Sep 17 00:00:00 2001 From: James Ralston Date: Fri, 8 Feb 2013 17:29:40 -0800 Subject: ALSA: hda_intel: Add Device IDs for Intel Wellsburg PCH This patch adds the HD Audio Device IDs for the Intel Wellsburg PCH Signed-off-by: James Ralston Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_intel.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index b5d5b209babe..1f8ce216edbb 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -3727,6 +3727,11 @@ static DEFINE_PCI_DEVICE_TABLE(azx_ids) = { /* Lynx Point */ { PCI_DEVICE(0x8086, 0x8c20), .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH }, + /* Wellsburg */ + { PCI_DEVICE(0x8086, 0x8d20), + .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH }, + { PCI_DEVICE(0x8086, 0x8d21), + .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH }, /* Lynx Point-LP */ { PCI_DEVICE(0x8086, 0x9c20), .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH }, -- cgit v1.2.3 From 6d67530e2c73e375b9204eba10ee2d589ba353ae Mon Sep 17 00:00:00 2001 From: Ian Minett Date: Fri, 8 Feb 2013 18:31:43 -0800 Subject: ALSA: CA0132: Improve the DSP transfer timeout calculations Base the DSP firmware transfer and communication timeouts on jiffy values. Signed-off-by: Ian Minett Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_ca0132.c | 41 +++++++++++++++++++++++++---------------- 1 file changed, 25 insertions(+), 16 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index 639a2829112f..710dae81fc8e 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -783,7 +783,7 @@ static int chipio_send(struct hda_codec *codec, unsigned int data) { unsigned int res; - int retry = 50; + unsigned long timeout = jiffies + msecs_to_jiffies(1000); /* send bits of data specified by reg */ do { @@ -791,7 +791,9 @@ static int chipio_send(struct hda_codec *codec, reg, data); if (res == VENDOR_STATUS_CHIPIO_OK) return 0; - } while (--retry); + msleep(20); + } while (time_before(jiffies, timeout)); + return -EIO; } @@ -1057,14 +1059,15 @@ static int dspio_send(struct hda_codec *codec, unsigned int reg, unsigned int data) { int res; - int retry = 50; + unsigned long timeout = jiffies + msecs_to_jiffies(1000); /* send bits of data specified by reg to dsp */ do { res = snd_hda_codec_read(codec, WIDGET_DSP_CTRL, 0, reg, data); if ((res >= 0) && (res != VENDOR_STATUS_DSPIO_BUSY)) return res; - } while (--retry); + msleep(20); + } while (time_before(jiffies, timeout)); return -EIO; } @@ -1296,7 +1299,6 @@ static int dspio_send_scp_message(struct hda_codec *codec, unsigned int *bytes_returned) { struct ca0132_spec *spec = codec->spec; - int retry; int status = -1; unsigned int scp_send_size = 0; unsigned int total_size; @@ -1343,13 +1345,13 @@ static int dspio_send_scp_message(struct hda_codec *codec, } if (waiting_for_resp) { + unsigned long timeout = jiffies + msecs_to_jiffies(1000); memset(return_buf, 0, return_buf_size); - retry = 50; do { msleep(20); - } while (spec->wait_scp && (--retry != 0)); + } while (spec->wait_scp && time_before(jiffies, timeout)); waiting_for_resp = false; - if (retry != 0) { + if (!spec->wait_scp) { ret_msg = (struct scp_msg *)return_buf; memcpy(&ret_msg->hdr, &spec->scp_resp_header, 4); memcpy(&ret_msg->data, spec->scp_resp_data, @@ -2242,7 +2244,8 @@ static int dspxfr_one_seg(struct hda_codec *codec, u32 chip_addx_remainder; unsigned int run_size_words; const struct dsp_image_seg *hci_write = NULL; - int retry; + unsigned long timeout; + bool dma_active; if (fls == NULL) return -EINVAL; @@ -2360,11 +2363,17 @@ static int dspxfr_one_seg(struct hda_codec *codec, status = dspxfr_hci_write(codec, hci_write); hci_write = NULL; } - retry = 5000; - while (dsp_is_dma_active(codec, dma_chan)) { - if (--retry <= 0) + + timeout = jiffies + msecs_to_jiffies(2000); + do { + dma_active = dsp_is_dma_active(codec, dma_chan); + if (!dma_active) break; - } + msleep(20); + } while (time_before(jiffies, timeout)); + if (dma_active) + break; + snd_printdd(KERN_INFO "+++++ DMA complete"); dma_set_state(dma_engine, DMA_STATE_STOP); dma_reset(dma_engine); @@ -2616,15 +2625,15 @@ static bool dspload_is_loaded(struct hda_codec *codec) static bool dspload_wait_loaded(struct hda_codec *codec) { - int retry = 100; + unsigned long timeout = jiffies + msecs_to_jiffies(2000); do { - msleep(20); if (dspload_is_loaded(codec)) { pr_info("ca0132 DOWNLOAD OK :-) DSP IS RUNNING.\n"); return true; } - } while (--retry); + msleep(20); + } while (time_before(jiffies, timeout)); pr_err("ca0132 DOWNLOAD FAILED!!! DSP IS NOT RUNNING.\n"); return false; -- cgit v1.2.3 From b3667bd7579e6d4dfe709315f13cff9bc9ee9053 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Sun, 10 Feb 2013 11:58:40 +0100 Subject: ALSA: hda - Fix memory leak and error handling in CA0132 DSP loader This patch fixes a few obvious bugs in DSP loader stuff: - Fix possible memory leaks in the error path - Avoid double-free calls in dma_reset() - Properly set/unset WC bits for DMA buffers - Add missing error status checks Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_intel.c | 11 ++++++++++- sound/pci/hda/patch_ca0132.c | 10 +++++++--- 2 files changed, 17 insertions(+), 4 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 1f8ce216edbb..bb9179e46796 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -2628,8 +2628,9 @@ static int azx_load_dsp_prepare(struct hda_bus *bus, unsigned int format, snd_dma_pci_data(chip->pci), byte_size, bufp); if (err < 0) - goto error; + goto unlock; + mark_pages_wc(chip, bufp, true); azx_dev = azx_get_dsp_loader_dev(chip); azx_dev->bufsize = byte_size; azx_dev->period_bytes = byte_size; @@ -2651,6 +2652,9 @@ static int azx_load_dsp_prepare(struct hda_bus *bus, unsigned int format, return azx_dev->stream_tag; error: + mark_pages_wc(chip, bufp, false); + snd_dma_free_pages(bufp); +unlock: snd_hda_unlock_devices(bus); return err; } @@ -2673,6 +2677,9 @@ static void azx_load_dsp_cleanup(struct hda_bus *bus, struct azx *chip = bus->private_data; struct azx_dev *azx_dev = azx_get_dsp_loader_dev(chip); + if (!dmab->area) + return; + /* reset BDL address */ azx_sd_writel(azx_dev, SD_BDLPL, 0); azx_sd_writel(azx_dev, SD_BDLPU, 0); @@ -2681,7 +2688,9 @@ static void azx_load_dsp_cleanup(struct hda_bus *bus, azx_dev->period_bytes = 0; azx_dev->format_val = 0; + mark_pages_wc(chip, dmab, false); snd_dma_free_pages(dmab); + dmab->area = NULL; snd_hda_unlock_devices(bus); } diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index 710dae81fc8e..fb7a32e730af 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -2065,7 +2065,7 @@ static int dma_reset(struct dma_engine *dma) struct ca0132_spec *spec = codec->spec; int status; - if (dma->dmab) + if (dma->dmab->area) snd_hda_codec_load_dsp_cleanup(codec, dma->dmab); status = snd_hda_codec_load_dsp_prepare(codec, @@ -2357,10 +2357,14 @@ static int dspxfr_one_seg(struct hda_codec *codec, chip_addx_remainder, data_remainder, remainder_words); + if (status < 0) + return status; remainder_words = 0; } if (hci_write) { status = dspxfr_hci_write(codec, hci_write); + if (status < 0) + return status; hci_write = NULL; } @@ -2376,7 +2380,7 @@ static int dspxfr_one_seg(struct hda_codec *codec, snd_printdd(KERN_INFO "+++++ DMA complete"); dma_set_state(dma_engine, DMA_STATE_STOP); - dma_reset(dma_engine); + status = dma_reset(dma_engine); if (status < 0) return status; @@ -2517,7 +2521,7 @@ exit: if (ovly && (dma_chan != INVALID_DMA_CHANNEL)) dspio_free_dma_chan(codec, dma_chan); - if (dma_engine->dmab) + if (dma_engine->dmab->area) snd_hda_codec_load_dsp_cleanup(codec, dma_engine->dmab); kfree(dma_engine->dmab); kfree(dma_engine); -- cgit v1.2.3 From f664417e23192087bb9bdafdff80e04104994cc0 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 11 Feb 2013 14:18:29 +0100 Subject: ALSA: hda/ca0132 - Slight optimization for build with DSP This reduces the resultant binary size. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_ca0132.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index fb7a32e730af..b1e099a3369c 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -2615,6 +2615,7 @@ static int dspload_image(struct hda_codec *codec, return status; } +#ifdef CONFIG_SND_HDA_CODEC_CA0132_DSP static bool dspload_is_loaded(struct hda_codec *codec) { unsigned int data = 0; @@ -2626,6 +2627,9 @@ static bool dspload_is_loaded(struct hda_codec *codec) return true; } +#else +#define dspload_is_loaded(codec) false +#endif static bool dspload_wait_loaded(struct hda_codec *codec) { -- cgit v1.2.3 From 12e31a78c70dc12897fda2489113f445c0e94a18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fernando=20Luis=20V=C3=A1zquez=20Cao?= Date: Tue, 12 Feb 2013 16:47:44 +0900 Subject: ALSA: hda - Workaround for silent output on Sony Vaio VGC-LN51JGB with ALC889 Some Vaio all-in-one desktop PCs (for example VGC-LN51JGB) are affected by the same issue that caused Vaio Z laptops to become silent: the speaker pin must be connected to the first DAC even though the codec itself advertises flexible routing through any of the DACs. Use the no-primary-hp fixup for choosing the speaker pin as the primary so that the right DAC is assigned on this device. Cc: stable@vger.kernel.org Signed-off-by: Fernando Luis Vazquez Cao Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index de512c52fa96..9eaa8b163952 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -2037,6 +2037,7 @@ static const struct snd_pci_quirk alc882_fixup_tbl[] = { SND_PCI_QUIRK(0x1043, 0x835f, "Asus Eee 1601", ALC888_FIXUP_EEE1601), SND_PCI_QUIRK(0x104d, 0x9047, "Sony Vaio TT", ALC889_FIXUP_VAIO_TT), SND_PCI_QUIRK(0x104d, 0x905a, "Sony Vaio Z", ALC882_FIXUP_NO_PRIMARY_HP), + SND_PCI_QUIRK(0x104d, 0x9043, "Sony Vaio VGC-LN51JGB", ALC882_FIXUP_NO_PRIMARY_HP), /* All Apple entries are in codec SSIDs */ SND_PCI_QUIRK(0x106b, 0x00a0, "MacBookPro 3,1", ALC889_FIXUP_MBP_VREF), -- cgit v1.2.3 From d911149625e64ec3cbc92725a2c2c5d940b62ffb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fernando=20Luis=20V=C3=A1zquez=20Cao?= Date: Tue, 12 Feb 2013 16:49:46 +0900 Subject: ALSA: hda - update documentation for no-primary-hp fixup The problem addressed by this fixup is not specific to Vaio Z, affecting some Vaio all-in-one desktop PCs too. Update the code comments accordingly. Signed-off-by: Fernando Luis Vazquez Cao Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 9eaa8b163952..48c9d10301b7 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -1802,7 +1802,8 @@ static void alc889_fixup_imac91_vref(struct hda_codec *codec, } /* Don't take HP output as primary - * strangely, the speaker output doesn't work on VAIO Z through DAC 0x05 + * Strangely, the speaker output doesn't work on Vaio Z and some Vaio + * all-in-one desktop PCs (for example VGC-LN51JGB) through DAC 0x05 */ static void alc882_fixup_no_primary_hp(struct hda_codec *codec, const struct hda_fixup *fix, int action) -- cgit v1.2.3 From 4a8b89f99534af1deaae73679797c27e77661ef0 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 12 Feb 2013 10:15:15 +0100 Subject: ALSA: hda/ca0132 - Fix type of INVALID_CHIP_ADDRESS The chip address is 32bit long but INVALID_CHIP_ADDRESS is defined as an unsigned long. This makes dsp_chip_to_dsp_addx() misbehaving on 64bit architectures. Fix the INVALID_CHIP_ADDRESS definition to be 32bit. Reported-by: Dan Carpenter Signed-off-by: Takashi Iwai --- sound/pci/hda/ca0132_regs.h | 2 +- sound/pci/hda/patch_ca0132.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/ca0132_regs.h b/sound/pci/hda/ca0132_regs.h index 831ca9c47992..07e760937d3c 100644 --- a/sound/pci/hda/ca0132_regs.h +++ b/sound/pci/hda/ca0132_regs.h @@ -337,7 +337,7 @@ #define DSPDMAC_ACTIVE_WFR_MASK 0xFFF000 #define DSP_AUX_MEM_BASE 0xE000 -#define INVALID_CHIP_ADDRESS (~0UL) +#define INVALID_CHIP_ADDRESS (~0U) #define X_SIZE (XRAM_XRAM_CHANNEL_COUNT * XRAM_XRAM_CHAN_INCR) #define Y_SIZE (YRAM_YRAM_CHANNEL_COUNT * YRAM_YRAM_CHAN_INCR) diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index b1e099a3369c..fe07664b44b6 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -1598,7 +1598,7 @@ static unsigned int dsp_chip_to_dsp_addx(unsigned int chip_addx, return Y_OFF(chip_addx); } - return (unsigned int)INVALID_CHIP_ADDRESS; + return INVALID_CHIP_ADDRESS; } /* @@ -4540,7 +4540,7 @@ static int ca0132_init(struct hda_codec *codec) int i; spec->dsp_state = DSP_DOWNLOAD_INIT; - spec->curr_chip_addx = (unsigned int)INVALID_CHIP_ADDRESS; + spec->curr_chip_addx = INVALID_CHIP_ADDRESS; snd_hda_power_up(codec); -- cgit v1.2.3 From 9958922a320d6ee9e9f96b30110bc3765b3e8299 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 12 Feb 2013 12:10:29 +0100 Subject: ALSA: hda/ca0132 - Add missing \n to debug prints Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_ca0132.c | 150 +++++++++++++++++++++---------------------- 1 file changed, 75 insertions(+), 75 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index fe07664b44b6..db02c1e96b08 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -858,7 +858,7 @@ static int chipio_write_data_multiple(struct hda_codec *codec, int status = 0; if (data == NULL) { - snd_printdd(KERN_ERR "chipio_write_data null ptr"); + snd_printdd(KERN_ERR "chipio_write_data null ptr\n"); return -EINVAL; } @@ -1397,12 +1397,12 @@ static int dspio_scp(struct hda_codec *codec, return -EINVAL; if (dir == SCP_GET && reply == NULL) { - snd_printdd(KERN_ERR "dspio_scp get but has no buffer"); + snd_printdd(KERN_ERR "dspio_scp get but has no buffer\n"); return -EINVAL; } if (reply != NULL && (reply_len == NULL || (*reply_len == 0))) { - snd_printdd(KERN_ERR "dspio_scp bad resp buf len parms"); + snd_printdd(KERN_ERR "dspio_scp bad resp buf len parms\n"); return -EINVAL; } @@ -1420,7 +1420,7 @@ static int dspio_scp(struct hda_codec *codec, sizeof(scp_reply), &ret_bytes); if (status < 0) { - snd_printdd(KERN_ERR "dspio_scp: send scp msg failed"); + snd_printdd(KERN_ERR "dspio_scp: send scp msg failed\n"); return status; } @@ -1439,17 +1439,17 @@ static int dspio_scp(struct hda_codec *codec, / sizeof(unsigned int); if (*reply_len < ret_size*sizeof(unsigned int)) { - snd_printdd(KERN_ERR "reply too long for buf"); + snd_printdd(KERN_ERR "reply too long for buf\n"); return -EINVAL; } else if (ret_size != reply_data_size) { - snd_printdd(KERN_ERR "RetLen and HdrLen .NE."); + snd_printdd(KERN_ERR "RetLen and HdrLen .NE.\n"); return -EINVAL; } else { *reply_len = ret_size*sizeof(unsigned int); memcpy(reply, scp_reply.data, *reply_len); } } else { - snd_printdd(KERN_ERR "reply ill-formed or errflag set"); + snd_printdd(KERN_ERR "reply ill-formed or errflag set\n"); return -EIO; } @@ -1479,22 +1479,22 @@ static int dspio_alloc_dma_chan(struct hda_codec *codec, unsigned int *dma_chan) int status = 0; unsigned int size = sizeof(dma_chan); - snd_printdd(KERN_INFO " dspio_alloc_dma_chan() -- begin"); + snd_printdd(KERN_INFO " dspio_alloc_dma_chan() -- begin\n"); status = dspio_scp(codec, MASTERCONTROL, MASTERCONTROL_ALLOC_DMA_CHAN, SCP_GET, NULL, 0, dma_chan, &size); if (status < 0) { - snd_printdd(KERN_INFO "dspio_alloc_dma_chan: SCP Failed"); + snd_printdd(KERN_INFO "dspio_alloc_dma_chan: SCP Failed\n"); return status; } if ((*dma_chan + 1) == 0) { - snd_printdd(KERN_INFO "no free dma channels to allocate"); + snd_printdd(KERN_INFO "no free dma channels to allocate\n"); return -EBUSY; } snd_printdd("dspio_alloc_dma_chan: chan=%d\n", *dma_chan); - snd_printdd(KERN_INFO " dspio_alloc_dma_chan() -- complete"); + snd_printdd(KERN_INFO " dspio_alloc_dma_chan() -- complete\n"); return status; } @@ -1507,18 +1507,18 @@ static int dspio_free_dma_chan(struct hda_codec *codec, unsigned int dma_chan) int status = 0; unsigned int dummy = 0; - snd_printdd(KERN_INFO " dspio_free_dma_chan() -- begin"); + snd_printdd(KERN_INFO " dspio_free_dma_chan() -- begin\n"); snd_printdd("dspio_free_dma_chan: chan=%d\n", dma_chan); status = dspio_scp(codec, MASTERCONTROL, MASTERCONTROL_ALLOC_DMA_CHAN, SCP_SET, &dma_chan, sizeof(dma_chan), NULL, &dummy); if (status < 0) { - snd_printdd(KERN_INFO "dspio_free_dma_chan: SCP Failed"); + snd_printdd(KERN_INFO "dspio_free_dma_chan: SCP Failed\n"); return status; } - snd_printdd(KERN_INFO " dspio_free_dma_chan() -- complete"); + snd_printdd(KERN_INFO " dspio_free_dma_chan() -- complete\n"); return status; } @@ -1626,39 +1626,39 @@ static int dsp_dma_setup_common(struct hda_codec *codec, unsigned int active; bool code, yram; - snd_printdd(KERN_INFO "-- dsp_dma_setup_common() -- Begin ---------"); + snd_printdd(KERN_INFO "-- dsp_dma_setup_common() -- Begin ---------\n"); if (dma_chan >= DSPDMAC_DMA_CFG_CHANNEL_COUNT) { - snd_printdd(KERN_ERR "dma chan num invalid"); + snd_printdd(KERN_ERR "dma chan num invalid\n"); return -EINVAL; } if (dsp_is_dma_active(codec, dma_chan)) { - snd_printdd(KERN_ERR "dma already active"); + snd_printdd(KERN_ERR "dma already active\n"); return -EBUSY; } dsp_addx = dsp_chip_to_dsp_addx(chip_addx, &code, &yram); if (dsp_addx == INVALID_CHIP_ADDRESS) { - snd_printdd(KERN_ERR "invalid chip addr"); + snd_printdd(KERN_ERR "invalid chip addr\n"); return -ENXIO; } chnl_prop = DSPDMAC_CHNLPROP_AC_MASK; active = 0; - snd_printdd(KERN_INFO " dsp_dma_setup_common() start reg pgm"); + snd_printdd(KERN_INFO " dsp_dma_setup_common() start reg pgm\n"); if (ovly) { status = chipio_read(codec, DSPDMAC_CHNLPROP_INST_OFFSET, &chnl_prop); if (status < 0) { - snd_printdd(KERN_ERR "read CHNLPROP Reg fail"); + snd_printdd(KERN_ERR "read CHNLPROP Reg fail\n"); return status; } - snd_printdd(KERN_INFO "dsp_dma_setup_common() Read CHNLPROP"); + snd_printdd(KERN_INFO "dsp_dma_setup_common() Read CHNLPROP\n"); } if (!code) @@ -1670,20 +1670,20 @@ static int dsp_dma_setup_common(struct hda_codec *codec, status = chipio_write(codec, DSPDMAC_CHNLPROP_INST_OFFSET, chnl_prop); if (status < 0) { - snd_printdd(KERN_ERR "write CHNLPROP Reg fail"); + snd_printdd(KERN_ERR "write CHNLPROP Reg fail\n"); return status; } - snd_printdd(KERN_INFO " dsp_dma_setup_common() Write CHNLPROP"); + snd_printdd(KERN_INFO " dsp_dma_setup_common() Write CHNLPROP\n"); if (ovly) { status = chipio_read(codec, DSPDMAC_ACTIVE_INST_OFFSET, &active); if (status < 0) { - snd_printdd(KERN_ERR "read ACTIVE Reg fail"); + snd_printdd(KERN_ERR "read ACTIVE Reg fail\n"); return status; } - snd_printdd(KERN_INFO "dsp_dma_setup_common() Read ACTIVE"); + snd_printdd(KERN_INFO "dsp_dma_setup_common() Read ACTIVE\n"); } active &= (~(1 << (DSPDMAC_ACTIVE_AAR_LOBIT + dma_chan))) & @@ -1691,27 +1691,27 @@ static int dsp_dma_setup_common(struct hda_codec *codec, status = chipio_write(codec, DSPDMAC_ACTIVE_INST_OFFSET, active); if (status < 0) { - snd_printdd(KERN_ERR "write ACTIVE Reg fail"); + snd_printdd(KERN_ERR "write ACTIVE Reg fail\n"); return status; } - snd_printdd(KERN_INFO " dsp_dma_setup_common() Write ACTIVE"); + snd_printdd(KERN_INFO " dsp_dma_setup_common() Write ACTIVE\n"); status = chipio_write(codec, DSPDMAC_AUDCHSEL_INST_OFFSET(dma_chan), port_map_mask); if (status < 0) { - snd_printdd(KERN_ERR "write AUDCHSEL Reg fail"); + snd_printdd(KERN_ERR "write AUDCHSEL Reg fail\n"); return status; } - snd_printdd(KERN_INFO " dsp_dma_setup_common() Write AUDCHSEL"); + snd_printdd(KERN_INFO " dsp_dma_setup_common() Write AUDCHSEL\n"); status = chipio_write(codec, DSPDMAC_IRQCNT_INST_OFFSET(dma_chan), DSPDMAC_IRQCNT_BICNT_MASK | DSPDMAC_IRQCNT_CICNT_MASK); if (status < 0) { - snd_printdd(KERN_ERR "write IRQCNT Reg fail"); + snd_printdd(KERN_ERR "write IRQCNT Reg fail\n"); return status; } - snd_printdd(KERN_INFO " dsp_dma_setup_common() Write IRQCNT"); + snd_printdd(KERN_INFO " dsp_dma_setup_common() Write IRQCNT\n"); snd_printdd( "ChipA=0x%x,DspA=0x%x,dmaCh=%u, " @@ -1719,7 +1719,7 @@ static int dsp_dma_setup_common(struct hda_codec *codec, chip_addx, dsp_addx, dma_chan, port_map_mask, chnl_prop, active); - snd_printdd(KERN_INFO "-- dsp_dma_setup_common() -- Complete ------"); + snd_printdd(KERN_INFO "-- dsp_dma_setup_common() -- Complete ------\n"); return 0; } @@ -1745,20 +1745,20 @@ static int dsp_dma_setup(struct hda_codec *codec, const unsigned int max_dma_count = 1 << (DSPDMAC_XFRCNT_BCNT_HIBIT - DSPDMAC_XFRCNT_BCNT_LOBIT + 1); - snd_printdd(KERN_INFO "-- dsp_dma_setup() -- Begin ---------"); + snd_printdd(KERN_INFO "-- dsp_dma_setup() -- Begin ---------\n"); if (count > max_dma_count) { - snd_printdd(KERN_ERR "count too big"); + snd_printdd(KERN_ERR "count too big\n"); return -EINVAL; } dsp_addx = dsp_chip_to_dsp_addx(chip_addx, &code, &yram); if (dsp_addx == INVALID_CHIP_ADDRESS) { - snd_printdd(KERN_ERR "invalid chip addr"); + snd_printdd(KERN_ERR "invalid chip addr\n"); return -ENXIO; } - snd_printdd(KERN_INFO " dsp_dma_setup() start reg pgm"); + snd_printdd(KERN_INFO " dsp_dma_setup() start reg pgm\n"); addr_field = dsp_addx << DSPDMAC_DMACFG_DBADR_LOBIT; incr_field = 0; @@ -1775,10 +1775,10 @@ static int dsp_dma_setup(struct hda_codec *codec, status = chipio_write(codec, DSPDMAC_DMACFG_INST_OFFSET(dma_chan), dma_cfg); if (status < 0) { - snd_printdd(KERN_ERR "write DMACFG Reg fail"); + snd_printdd(KERN_ERR "write DMACFG Reg fail\n"); return status; } - snd_printdd(KERN_INFO " dsp_dma_setup() Write DMACFG"); + snd_printdd(KERN_INFO " dsp_dma_setup() Write DMACFG\n"); adr_ofs = (count - 1) << (DSPDMAC_DSPADROFS_BOFS_LOBIT + (code ? 0 : 1)); @@ -1786,10 +1786,10 @@ static int dsp_dma_setup(struct hda_codec *codec, status = chipio_write(codec, DSPDMAC_DSPADROFS_INST_OFFSET(dma_chan), adr_ofs); if (status < 0) { - snd_printdd(KERN_ERR "write DSPADROFS Reg fail"); + snd_printdd(KERN_ERR "write DSPADROFS Reg fail\n"); return status; } - snd_printdd(KERN_INFO " dsp_dma_setup() Write DSPADROFS"); + snd_printdd(KERN_INFO " dsp_dma_setup() Write DSPADROFS\n"); base_cnt = (count - 1) << DSPDMAC_XFRCNT_BCNT_LOBIT; @@ -1800,17 +1800,17 @@ static int dsp_dma_setup(struct hda_codec *codec, status = chipio_write(codec, DSPDMAC_XFRCNT_INST_OFFSET(dma_chan), xfr_cnt); if (status < 0) { - snd_printdd(KERN_ERR "write XFRCNT Reg fail"); + snd_printdd(KERN_ERR "write XFRCNT Reg fail\n"); return status; } - snd_printdd(KERN_INFO " dsp_dma_setup() Write XFRCNT"); + snd_printdd(KERN_INFO " dsp_dma_setup() Write XFRCNT\n"); snd_printdd( "ChipA=0x%x, cnt=0x%x, DMACFG=0x%x, " "ADROFS=0x%x, XFRCNT=0x%x\n", chip_addx, count, dma_cfg, adr_ofs, xfr_cnt); - snd_printdd(KERN_INFO "-- dsp_dma_setup() -- Complete ---------"); + snd_printdd(KERN_INFO "-- dsp_dma_setup() -- Complete ---------\n"); return 0; } @@ -1824,17 +1824,17 @@ static int dsp_dma_start(struct hda_codec *codec, unsigned int reg = 0; int status = 0; - snd_printdd(KERN_INFO "-- dsp_dma_start() -- Begin ---------"); + snd_printdd(KERN_INFO "-- dsp_dma_start() -- Begin ---------\n"); if (ovly) { status = chipio_read(codec, DSPDMAC_CHNLSTART_INST_OFFSET, ®); if (status < 0) { - snd_printdd(KERN_ERR "read CHNLSTART reg fail"); + snd_printdd(KERN_ERR "read CHNLSTART reg fail\n"); return status; } - snd_printdd(KERN_INFO "-- dsp_dma_start() Read CHNLSTART"); + snd_printdd(KERN_INFO "-- dsp_dma_start() Read CHNLSTART\n"); reg &= ~(DSPDMAC_CHNLSTART_EN_MASK | DSPDMAC_CHNLSTART_DIS_MASK); @@ -1843,10 +1843,10 @@ static int dsp_dma_start(struct hda_codec *codec, status = chipio_write(codec, DSPDMAC_CHNLSTART_INST_OFFSET, reg | (1 << (dma_chan + DSPDMAC_CHNLSTART_EN_LOBIT))); if (status < 0) { - snd_printdd(KERN_ERR "write CHNLSTART reg fail"); + snd_printdd(KERN_ERR "write CHNLSTART reg fail\n"); return status; } - snd_printdd(KERN_INFO "-- dsp_dma_start() -- Complete ---------"); + snd_printdd(KERN_INFO "-- dsp_dma_start() -- Complete ---------\n"); return status; } @@ -1860,17 +1860,17 @@ static int dsp_dma_stop(struct hda_codec *codec, unsigned int reg = 0; int status = 0; - snd_printdd(KERN_INFO "-- dsp_dma_stop() -- Begin ---------"); + snd_printdd(KERN_INFO "-- dsp_dma_stop() -- Begin ---------\n"); if (ovly) { status = chipio_read(codec, DSPDMAC_CHNLSTART_INST_OFFSET, ®); if (status < 0) { - snd_printdd(KERN_ERR "read CHNLSTART reg fail"); + snd_printdd(KERN_ERR "read CHNLSTART reg fail\n"); return status; } - snd_printdd(KERN_INFO "-- dsp_dma_stop() Read CHNLSTART"); + snd_printdd(KERN_INFO "-- dsp_dma_stop() Read CHNLSTART\n"); reg &= ~(DSPDMAC_CHNLSTART_EN_MASK | DSPDMAC_CHNLSTART_DIS_MASK); } @@ -1878,10 +1878,10 @@ static int dsp_dma_stop(struct hda_codec *codec, status = chipio_write(codec, DSPDMAC_CHNLSTART_INST_OFFSET, reg | (1 << (dma_chan + DSPDMAC_CHNLSTART_DIS_LOBIT))); if (status < 0) { - snd_printdd(KERN_ERR "write CHNLSTART reg fail"); + snd_printdd(KERN_ERR "write CHNLSTART reg fail\n"); return status; } - snd_printdd(KERN_INFO "-- dsp_dma_stop() -- Complete ---------"); + snd_printdd(KERN_INFO "-- dsp_dma_stop() -- Complete ---------\n"); return status; } @@ -1964,17 +1964,17 @@ static int dsp_allocate_ports(struct hda_codec *codec, { int status; - snd_printdd(KERN_INFO " dsp_allocate_ports() -- begin"); + snd_printdd(KERN_INFO " dsp_allocate_ports() -- begin\n"); if ((rate_multi != 1) && (rate_multi != 2) && (rate_multi != 4)) { - snd_printdd(KERN_ERR "bad rate multiple"); + snd_printdd(KERN_ERR "bad rate multiple\n"); return -EINVAL; } status = dsp_allocate_router_ports(codec, num_chans, rate_multi, 0, port_map); - snd_printdd(KERN_INFO " dsp_allocate_ports() -- complete"); + snd_printdd(KERN_INFO " dsp_allocate_ports() -- complete\n"); return status; } @@ -1991,7 +1991,7 @@ static int dsp_allocate_ports_format(struct hda_codec *codec, unsigned int rate_multi = sample_rate_mul / sample_rate_div; if ((rate_multi != 1) && (rate_multi != 2) && (rate_multi != 4)) { - snd_printdd(KERN_ERR "bad rate multiple"); + snd_printdd(KERN_ERR "bad rate multiple\n"); return -EINVAL; } @@ -2009,14 +2009,14 @@ static int dsp_free_ports(struct hda_codec *codec) { int status; - snd_printdd(KERN_INFO " dsp_free_ports() -- begin"); + snd_printdd(KERN_INFO " dsp_free_ports() -- begin\n"); status = dsp_free_router_ports(codec); if (status < 0) { - snd_printdd(KERN_ERR "free router ports fail"); + snd_printdd(KERN_ERR "free router ports fail\n"); return status; } - snd_printdd(KERN_INFO " dsp_free_ports() -- complete"); + snd_printdd(KERN_INFO " dsp_free_ports() -- complete\n"); return status; } @@ -2186,7 +2186,7 @@ static int dspxfr_hci_write(struct hda_codec *codec, unsigned int count; if (fls == NULL || fls->chip_addr != g_chip_addr_magic_value) { - snd_printdd(KERN_ERR "hci_write invalid params"); + snd_printdd(KERN_ERR "hci_write invalid params\n"); return -EINVAL; } @@ -2195,7 +2195,7 @@ static int dspxfr_hci_write(struct hda_codec *codec, while (count >= 2) { status = chipio_write(codec, data[0], data[1]); if (status < 0) { - snd_printdd(KERN_ERR "hci_write chipio failed"); + snd_printdd(KERN_ERR "hci_write chipio failed\n"); return status; } count -= 2; @@ -2346,7 +2346,7 @@ static int dspxfr_one_seg(struct hda_codec *codec, if (status < 0) return status; if (!dsp_is_dma_active(codec, dma_chan)) { - snd_printdd(KERN_ERR "dspxfr:DMA did not start"); + snd_printdd(KERN_ERR "dspxfr:DMA did not start\n"); return -EIO; } status = dma_set_state(dma_engine, DMA_STATE_RUN); @@ -2378,7 +2378,7 @@ static int dspxfr_one_seg(struct hda_codec *codec, if (dma_active) break; - snd_printdd(KERN_INFO "+++++ DMA complete"); + snd_printdd(KERN_INFO "+++++ DMA complete\n"); dma_set_state(dma_engine, DMA_STATE_STOP); status = dma_reset(dma_engine); @@ -2452,7 +2452,7 @@ static int dspxfr_image(struct hda_codec *codec, hda_format, &response); if (status < 0) { - snd_printdd(KERN_ERR "set converter format fail"); + snd_printdd(KERN_ERR "set converter format fail\n"); goto exit; } @@ -2467,7 +2467,7 @@ static int dspxfr_image(struct hda_codec *codec, if (ovly) { status = dspio_alloc_dma_chan(codec, &dma_chan); if (status < 0) { - snd_printdd(KERN_ERR "alloc dmachan fail"); + snd_printdd(KERN_ERR "alloc dmachan fail\n"); dma_chan = INVALID_DMA_CHANNEL; goto exit; } @@ -2477,7 +2477,7 @@ static int dspxfr_image(struct hda_codec *codec, status = dsp_allocate_ports_format(codec, hda_format, &port_map_mask); if (status < 0) { - snd_printdd(KERN_ERR "alloc ports fail"); + snd_printdd(KERN_ERR "alloc ports fail\n"); goto exit; } @@ -2485,13 +2485,13 @@ static int dspxfr_image(struct hda_codec *codec, status = codec_set_converter_stream_channel(codec, WIDGET_CHIP_CTRL, stream_id, 0, &response); if (status < 0) { - snd_printdd(KERN_ERR "set stream chan fail"); + snd_printdd(KERN_ERR "set stream chan fail\n"); goto exit; } while ((fls_data != NULL) && !is_last(fls_data)) { if (!is_valid(fls_data)) { - snd_printdd(KERN_ERR "FLS check fail"); + snd_printdd(KERN_ERR "FLS check fail\n"); status = -EINVAL; goto exit; } @@ -2534,7 +2534,7 @@ exit: */ static void dspload_post_setup(struct hda_codec *codec) { - snd_printdd(KERN_INFO "---- dspload_post_setup ------"); + snd_printdd(KERN_INFO "---- dspload_post_setup ------\n"); /*set DSP speaker to 2.0 configuration*/ chipio_write(codec, XRAM_XRAM_INST_OFFSET(0x18), 0x08080080); @@ -2572,7 +2572,7 @@ static int dspload_image(struct hda_codec *codec, unsigned int sample_rate; unsigned short channels; - snd_printdd(KERN_INFO "---- dspload_image begin ------"); + snd_printdd(KERN_INFO "---- dspload_image begin ------\n"); if (router_chans == 0) { if (!ovly) router_chans = DMA_TRANSFER_FRAME_SIZE_NWORDS; @@ -2589,27 +2589,27 @@ static int dspload_image(struct hda_codec *codec, } do { - snd_printdd(KERN_INFO "Ready to program DMA"); + snd_printdd(KERN_INFO "Ready to program DMA\n"); if (!ovly) status = dsp_reset(codec); if (status < 0) break; - snd_printdd(KERN_INFO "dsp_reset() complete"); + snd_printdd(KERN_INFO "dsp_reset() complete\n"); status = dspxfr_image(codec, fls, reloc, sample_rate, channels, ovly); if (status < 0) break; - snd_printdd(KERN_INFO "dspxfr_image() complete"); + snd_printdd(KERN_INFO "dspxfr_image() complete\n"); if (autostart && !ovly) { dspload_post_setup(codec); status = dsp_set_run_state(codec); } - snd_printdd(KERN_INFO "LOAD FINISHED"); + snd_printdd(KERN_INFO "LOAD FINISHED\n"); } while (0); return status; -- cgit v1.2.3 From ea9b43addc4d90ca5b029f47f85ca152320a1e8d Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 12 Feb 2013 17:02:41 +0100 Subject: ALSA: hda - Fix broken workaround for HDMI/SPDIF conflicts The commit [dcda58061: ALSA: hda - Add workaround for conflicting IEC958 controls] introduced a workaround for cards that have both SPDIF and HDMI devices for giving device=1 to SPDIF control elements. It turned out, however, that this workaround doesn't work well - - The workaround checks only conflicts in a single codec, but SPDIF and HDMI are provided by multiple codecs in many cases, and - ALSA mixer abstraction doesn't care about the device number in ctl elements, thus you'll get errors from amixer such as % amixer scontrols -c 0 ALSA lib simple_none.c:1551:(simple_add1) helem (MIXER,'IEC958 Playback Switch',0,1,0) appears twice or more amixer: Mixer hw:0 load error: Invalid argument This patch fixes the previous broken workaround. Instead of changing the device number of SPDIF ctl elements, shift the element indices of such controls up to 16. Also, the conflict check is performed over all codecs found on the bus. HDMI devices will be put to dev=0,index=0 as before. Only the conflicting SPDIF device is moved to a different place. The new place of SPDIF device is supposed by the updated alsa-lib HDA-Intel.conf, respectively. Reported-by: Stephan Raue Reported-by: Anssi Hannula Cc: [v3.8] Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_codec.c | 43 +++++++++++++++++++++---------------------- sound/pci/hda/hda_codec.h | 3 ++- 2 files changed, 23 insertions(+), 23 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index e80f83568291..04b57383e8cb 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -2332,11 +2332,12 @@ struct snd_kcontrol *snd_hda_find_mixer_ctl(struct hda_codec *codec, EXPORT_SYMBOL_HDA(snd_hda_find_mixer_ctl); static int find_empty_mixer_ctl_idx(struct hda_codec *codec, const char *name, - int dev) + int start_idx) { - int idx; - for (idx = 0; idx < 16; idx++) { /* 16 ctlrs should be large enough */ - if (!find_mixer_ctl(codec, name, dev, idx)) + int i, idx; + /* 16 ctlrs should be large enough */ + for (i = 0, idx = start_idx; i < 16; i++, idx++) { + if (!find_mixer_ctl(codec, name, 0, idx)) return idx; } return -EBUSY; @@ -3305,30 +3306,29 @@ int snd_hda_create_dig_out_ctls(struct hda_codec *codec, int err; struct snd_kcontrol *kctl; struct snd_kcontrol_new *dig_mix; - int idx, dev = 0; - const int spdif_pcm_dev = 1; + int idx = 0; + const int spdif_index = 16; struct hda_spdif_out *spdif; + struct hda_bus *bus = codec->bus; - if (codec->primary_dig_out_type == HDA_PCM_TYPE_HDMI && + if (bus->primary_dig_out_type == HDA_PCM_TYPE_HDMI && type == HDA_PCM_TYPE_SPDIF) { - dev = spdif_pcm_dev; - } else if (codec->primary_dig_out_type == HDA_PCM_TYPE_SPDIF && + idx = spdif_index; + } else if (bus->primary_dig_out_type == HDA_PCM_TYPE_SPDIF && type == HDA_PCM_TYPE_HDMI) { - for (idx = 0; idx < codec->spdif_out.used; idx++) { - spdif = snd_array_elem(&codec->spdif_out, idx); - for (dig_mix = dig_mixes; dig_mix->name; dig_mix++) { - kctl = find_mixer_ctl(codec, dig_mix->name, 0, idx); - if (!kctl) - break; - kctl->id.device = spdif_pcm_dev; - } + /* suppose a single SPDIF device */ + for (dig_mix = dig_mixes; dig_mix->name; dig_mix++) { + kctl = find_mixer_ctl(codec, dig_mix->name, 0, 0); + if (!kctl) + break; + kctl->id.index = spdif_index; } - codec->primary_dig_out_type = HDA_PCM_TYPE_HDMI; + bus->primary_dig_out_type = HDA_PCM_TYPE_HDMI; } - if (!codec->primary_dig_out_type) - codec->primary_dig_out_type = type; + if (!bus->primary_dig_out_type) + bus->primary_dig_out_type = type; - idx = find_empty_mixer_ctl_idx(codec, "IEC958 Playback Switch", dev); + idx = find_empty_mixer_ctl_idx(codec, "IEC958 Playback Switch", idx); if (idx < 0) { printk(KERN_ERR "hda_codec: too many IEC958 outputs\n"); return -EBUSY; @@ -3338,7 +3338,6 @@ int snd_hda_create_dig_out_ctls(struct hda_codec *codec, kctl = snd_ctl_new1(dig_mix, codec); if (!kctl) return -ENOMEM; - kctl->id.device = dev; kctl->id.index = idx; kctl->private_value = codec->spdif_out.used - 1; err = snd_hda_ctl_add(codec, associated_nid, kctl); diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index e8c9442d27ad..23ca1722aff1 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -679,6 +679,8 @@ struct hda_bus { unsigned int response_reset:1; /* controller was reset */ unsigned int in_reset:1; /* during reset operation */ unsigned int power_keep_link_on:1; /* don't power off HDA link */ + + int primary_dig_out_type; /* primary digital out PCM type */ }; /* @@ -846,7 +848,6 @@ struct hda_codec { struct mutex hash_mutex; struct snd_array spdif_out; unsigned int spdif_in_enable; /* SPDIF input enable? */ - int primary_dig_out_type; /* primary digital out PCM type */ const hda_nid_t *slave_dig_outs; /* optional digital out slave widgets */ struct snd_array init_pins; /* initial (BIOS) pin configurations */ struct snd_array driver_pins; /* pin configs set by codec parser */ -- cgit v1.2.3 From 1d739066a034c163ece55dd522e8b5659e1aa1a7 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 13 Feb 2013 14:17:32 +0100 Subject: ALSA: hda - Increase badness for missing multi-io The current badness value used for the missing multi-io seems too weak, and the multi-io tends to be skipped for desktop configurations when no enough DACs are available. It's because the total badness of the multi-io becomes often larger than the badness with assigning an individual DAC to a headphone jack. This is good for one side, but it seems that the surround outputs are more demanded by that. This patch increases the badness value for the missing multi-io slightly so that the multi-io would be preferred than the individual headphone DAC if they conflict. Through the tests with hda-emu, mostly only desktop configurations with ALC662/663 and CMI codecs are affected by this change, and all look reasonable. Reported-by: Raymond Yau Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 6af5aded1ed5..78897d05d80f 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -988,7 +988,7 @@ enum { /* No DAC is found for the extra output */ BAD_NO_DAC = 0x4000, /* No possible multi-ios */ - BAD_MULTI_IO = 0x103, + BAD_MULTI_IO = 0x120, /* No individual DAC for extra output */ BAD_NO_EXTRA_DAC = 0x102, /* No individual DAC for extra surrounds */ -- cgit v1.2.3 From 2c1350fdeaefefe1a149d3b083383409f43f0daa Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 14 Feb 2013 09:44:55 +0100 Subject: ALSA: hda - Disable runtime PM for Intel 5 Series/3400 We've got a regression report wrt the IRQ issue related with the power-save on a Dell machine, and disabling runtime PM works around. Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=53441 Cc: [v3.7+] Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_intel.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index bb9179e46796..4cea6bb6fade 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -3756,7 +3756,7 @@ static DEFINE_PCI_DEVICE_TABLE(azx_ids) = { .driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH }, /* 5 Series/3400 */ { PCI_DEVICE(0x8086, 0x3b56), - .driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH }, + .driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH_NOPM }, /* Poulsbo */ { PCI_DEVICE(0x8086, 0x811b), .driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH_NOPM }, -- cgit v1.2.3 From 8be69efacdc73fc110624f847bdf04b83decfc70 Mon Sep 17 00:00:00 2001 From: David Henningsson Date: Thu, 14 Feb 2013 11:36:39 +0100 Subject: ALSA: hda - Remove speaker clicks on CX20549 This chip needs the speaker pin to go to D3 to avoid clicks, so default_power_filter does not work here. This was found on Thinkpad R61i/T61i but I guess it applies to the entire chip. If not, quirks should be set for at least PCI SSID 17aa:20ac. Thanks to c4pp4 for testing. BugLink: https://bugs.launchpad.net/bugs/886975 Signed-off-by: David Henningsson Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_conexant.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index 7d941ef54172..941bf6c766ec 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -3350,6 +3350,7 @@ static int patch_conexant_auto(struct hda_codec *codec) switch (codec->vendor_id) { case 0x14f15045: codec->single_adc_amp = 1; + codec->power_filter = NULL; /* Needs speaker amp to D3 to avoid click */ break; case 0x14f15047: codec->pin_amp_workaround = 1; -- cgit v1.2.3 From d06ac14399a0af7e4da0c67cff30d30d7e02df46 Mon Sep 17 00:00:00 2001 From: David Henningsson Date: Mon, 18 Feb 2013 11:41:55 +0100 Subject: ALSA: hda - add quirks for mute LED on two HP machines These two machines have no mute LED string in BIOS. BugLink: https://bugs.launchpad.net/bugs/1128934 Tested-by: Tammy Yang Signed-off-by: David Henningsson Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 48c9d10301b7..e575685cca99 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -2726,6 +2726,18 @@ static void alc269_fixup_hp_mute_led(struct hda_codec *codec, } } +static void alc269_fixup_hp_mute_led_mic1(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct alc_spec *spec = codec->spec; + if (action == HDA_FIXUP_ACT_PRE_PROBE) { + spec->mute_led_polarity = 0; + spec->mute_led_nid = 0x18; + spec->gen.vmaster_mute.hook = alc269_fixup_mic_mute_hook; + spec->gen.vmaster_mute_enum = 1; + } +} + static void alc269_fixup_hp_mute_led_mic2(struct hda_codec *codec, const struct hda_fixup *fix, int action) { @@ -2771,6 +2783,7 @@ enum { ALC269VB_FIXUP_AMIC, ALC269VB_FIXUP_DMIC, ALC269_FIXUP_HP_MUTE_LED, + ALC269_FIXUP_HP_MUTE_LED_MIC1, ALC269_FIXUP_HP_MUTE_LED_MIC2, ALC269_FIXUP_INV_DMIC, ALC269_FIXUP_LENOVO_DOCK, @@ -2903,6 +2916,10 @@ static const struct hda_fixup alc269_fixups[] = { .type = HDA_FIXUP_FUNC, .v.func = alc269_fixup_hp_mute_led, }, + [ALC269_FIXUP_HP_MUTE_LED_MIC1] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc269_fixup_hp_mute_led_mic1, + }, [ALC269_FIXUP_HP_MUTE_LED_MIC2] = { .type = HDA_FIXUP_FUNC, .v.func = alc269_fixup_hp_mute_led_mic2, @@ -2947,6 +2964,8 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1025, 0x029b, "Acer 1810TZ", ALC269_FIXUP_INV_DMIC), SND_PCI_QUIRK(0x1025, 0x0349, "Acer AOD260", ALC269_FIXUP_INV_DMIC), SND_PCI_QUIRK(0x103c, 0x1586, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC2), + SND_PCI_QUIRK(0x103c, 0x1973, "HP Pavilion", ALC269_FIXUP_HP_MUTE_LED_MIC1), + SND_PCI_QUIRK(0x103c, 0x1983, "HP Pavilion", ALC269_FIXUP_HP_MUTE_LED_MIC1), SND_PCI_QUIRK_VENDOR(0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED), SND_PCI_QUIRK(0x1043, 0x1427, "Asus Zenbook UX31E", ALC269VB_FIXUP_DMIC), SND_PCI_QUIRK(0x1043, 0x1517, "Asus Zenbook UX31A", ALC269VB_FIXUP_DMIC), -- cgit v1.2.3 From e6e0ee507f630350d883bb3343e78cab30cad5e2 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 18 Feb 2013 17:04:20 +0100 Subject: ALSA: hda - Fix the silent speaker output on Fujitsu S7020 laptop In the recent update, Fujitsu S7020 laptop with ALC260 codec lost the speaker output, no matter how the amps and the pins are set. After a long debugging session, we found out that the default codec init code is harmful for this machine, and we have to reset it to ALC_INIT_NONE. Reported-and-tested-by: Jonathan Woithe Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index e575685cca99..61478fd82565 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -1516,8 +1516,14 @@ static void alc260_fixup_fsc_s7020(struct hda_codec *codec, { struct alc_spec *spec = codec->spec; - if (action == HDA_FIXUP_ACT_PRE_PROBE) + switch (action) { + case HDA_FIXUP_ACT_PRE_PROBE: spec->gen.add_out_jack_modes = 1; + break; + case HDA_FIXUP_ACT_PROBE: + spec->init_amp = ALC_INIT_NONE; + break; + } } static const struct hda_fixup alc260_fixups[] = { -- cgit v1.2.3 From bbfd8a19b6913f50a362457c34d49bfafe5e456e Mon Sep 17 00:00:00 2001 From: David Henningsson Date: Tue, 19 Feb 2013 16:11:22 +0100 Subject: ALSA: hda - hdmi: ELD shouldn't be valid after unplug Currently, eld_valid is never set to false, except at kernel module load time. This patch makes sure that eld is no longer valid when the cable is (hot-)unplugged. Cc: stable@kernel.org Signed-off-by: David Henningsson Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_hdmi.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index b9af281b2ba4..32adaa6c5627 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -1176,6 +1176,7 @@ static void hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll) "HDMI status: Codec=%d Pin=%d Presence_Detect=%d ELD_Valid=%d\n", codec->addr, pin_nid, eld->monitor_present, eld_valid); + eld->eld_valid = false; if (eld_valid) { if (!snd_hdmi_get_eld(eld, codec, pin_nid)) snd_hdmi_show_eld(eld); -- cgit v1.2.3 From 68e03de98507065bb5fd1958388974c9bc2cd480 Mon Sep 17 00:00:00 2001 From: David Henningsson Date: Tue, 19 Feb 2013 16:11:23 +0100 Subject: ALSA: hda - hdmi: Do not expose eld data when eld is invalid Previously, it was possible to read the eld data of the previous monitor connected. This should not be allowed. Also refactor the function slightly. Signed-off-by: David Henningsson Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_hdmi.c | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 32adaa6c5627..6bcdd667f514 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -343,14 +343,16 @@ static int hdmi_eld_ctl_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct hdmi_spec *spec; + struct hdmi_spec *spec = codec->spec; + struct hdmi_eld *eld; int pin_idx; - spec = codec->spec; uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; pin_idx = kcontrol->private_value; - uinfo->count = spec->pins[pin_idx].sink_eld.eld_size; + eld = &spec->pins[pin_idx].sink_eld; + + uinfo->count = eld->eld_valid ? eld->eld_size : 0; return 0; } @@ -359,14 +361,23 @@ static int hdmi_eld_ctl_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct hdmi_spec *spec; + struct hdmi_spec *spec = codec->spec; + struct hdmi_eld *eld; int pin_idx; - spec = codec->spec; pin_idx = kcontrol->private_value; + eld = &spec->pins[pin_idx].sink_eld; + + if (eld->eld_size > ARRAY_SIZE(ucontrol->value.bytes.data)) { + snd_BUG(); + return -EINVAL; + } - memcpy(ucontrol->value.bytes.data, - spec->pins[pin_idx].sink_eld.eld_buffer, ELD_MAX_SIZE); + memset(ucontrol->value.bytes.data, 0, + ARRAY_SIZE(ucontrol->value.bytes.data)); + if (eld->eld_valid) + memcpy(ucontrol->value.bytes.data, eld->eld_buffer, + eld->eld_size); return 0; } -- cgit v1.2.3 From 1613d6b46b433f07f1d2703e4bd102802dcd75a4 Mon Sep 17 00:00:00 2001 From: David Henningsson Date: Tue, 19 Feb 2013 16:11:24 +0100 Subject: ALSA: hda - hdmi: Refactor hdmi_eld into parsed_hdmi_eld For better readability, the information that is parsed out of the ELD data is now put into a separate parsed_hdmi_eld struct. Signed-off-by: David Henningsson Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_eld.c | 46 +++++++++++++++++++++------------------------- sound/pci/hda/hda_local.h | 27 +++++++++++++++++---------- sound/pci/hda/patch_hdmi.c | 28 +++++++++++++++++++--------- 3 files changed, 57 insertions(+), 44 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_eld.c b/sound/pci/hda/hda_eld.c index 4c054f4486b9..16066d7763ec 100644 --- a/sound/pci/hda/hda_eld.c +++ b/sound/pci/hda/hda_eld.c @@ -246,8 +246,8 @@ static void hdmi_update_short_audio_desc(struct cea_sad *a, /* * Be careful, ELD buf could be totally rubbish! */ -static int hdmi_update_eld(struct hdmi_eld *e, - const unsigned char *buf, int size) +int snd_hdmi_parse_eld(struct parsed_hdmi_eld *e, + const unsigned char *buf, int size) { int mnl; int i; @@ -260,7 +260,6 @@ static int hdmi_update_eld(struct hdmi_eld *e, goto out_fail; } - e->eld_size = size; e->baseline_len = GRAB_BITS(buf, 2, 0, 8); mnl = GRAB_BITS(buf, 4, 0, 5); e->cea_edid_ver = GRAB_BITS(buf, 4, 5, 3); @@ -305,7 +304,6 @@ static int hdmi_update_eld(struct hdmi_eld *e, if (!e->spk_alloc) e->spk_alloc = 0xffff; - e->eld_valid = true; return 0; out_fail: @@ -318,17 +316,16 @@ int snd_hdmi_get_eld_size(struct hda_codec *codec, hda_nid_t nid) AC_DIPSIZE_ELD_BUF); } -int snd_hdmi_get_eld(struct hdmi_eld *eld, - struct hda_codec *codec, hda_nid_t nid) +int snd_hdmi_get_eld(struct hda_codec *codec, hda_nid_t nid, + unsigned char *buf, int *eld_size) { int i; int ret; int size; - unsigned char *buf; /* * ELD size is initialized to zero in caller function. If no errors and - * ELD is valid, actual eld_size is assigned in hdmi_update_eld() + * ELD is valid, actual eld_size is assigned. */ size = snd_hdmi_get_eld_size(codec, nid); @@ -343,8 +340,6 @@ int snd_hdmi_get_eld(struct hdmi_eld *eld, } /* set ELD buffer */ - buf = eld->eld_buffer; - for (i = 0; i < size; i++) { unsigned int val = hdmi_get_eld_data(codec, nid, i); /* @@ -372,8 +367,7 @@ int snd_hdmi_get_eld(struct hdmi_eld *eld, buf[i] = val; } - ret = hdmi_update_eld(eld, buf, size); - + *eld_size = size; error: return ret; } @@ -438,7 +432,7 @@ void snd_print_channel_allocation(int spk_alloc, char *buf, int buflen) buf[j] = '\0'; /* necessary when j == 0 */ } -void snd_hdmi_show_eld(struct hdmi_eld *e) +void snd_hdmi_show_eld(struct parsed_hdmi_eld *e) { int i; @@ -487,10 +481,11 @@ static void hdmi_print_sad_info(int i, struct cea_sad *a, static void hdmi_print_eld_info(struct snd_info_entry *entry, struct snd_info_buffer *buffer) { - struct hdmi_eld *e = entry->private_data; + struct hdmi_eld *eld = entry->private_data; + struct parsed_hdmi_eld *e = &eld->info; char buf[SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE]; int i; - static char *eld_versoin_names[32] = { + static char *eld_version_names[32] = { "reserved", "reserved", "CEA-861D or below", @@ -505,15 +500,15 @@ static void hdmi_print_eld_info(struct snd_info_entry *entry, [4 ... 7] = "reserved" }; - snd_iprintf(buffer, "monitor_present\t\t%d\n", e->monitor_present); - snd_iprintf(buffer, "eld_valid\t\t%d\n", e->eld_valid); - if (!e->eld_valid) + snd_iprintf(buffer, "monitor_present\t\t%d\n", eld->monitor_present); + snd_iprintf(buffer, "eld_valid\t\t%d\n", eld->eld_valid); + if (!eld->eld_valid) return; snd_iprintf(buffer, "monitor_name\t\t%s\n", e->monitor_name); snd_iprintf(buffer, "connection_type\t\t%s\n", eld_connection_type_names[e->conn_type]); snd_iprintf(buffer, "eld_version\t\t[0x%x] %s\n", e->eld_ver, - eld_versoin_names[e->eld_ver]); + eld_version_names[e->eld_ver]); snd_iprintf(buffer, "edid_version\t\t[0x%x] %s\n", e->cea_edid_ver, cea_edid_version_names[e->cea_edid_ver]); snd_iprintf(buffer, "manufacture_id\t\t0x%x\n", e->manufacture_id); @@ -535,7 +530,8 @@ static void hdmi_print_eld_info(struct snd_info_entry *entry, static void hdmi_write_eld_info(struct snd_info_entry *entry, struct snd_info_buffer *buffer) { - struct hdmi_eld *e = entry->private_data; + struct hdmi_eld *eld = entry->private_data; + struct parsed_hdmi_eld *e = &eld->info; char line[64]; char name[64]; char *sname; @@ -551,9 +547,9 @@ static void hdmi_write_eld_info(struct snd_info_entry *entry, * eld_version edid_version */ if (!strcmp(name, "monitor_present")) - e->monitor_present = val; + eld->monitor_present = val; else if (!strcmp(name, "eld_valid")) - e->eld_valid = val; + eld->eld_valid = val; else if (!strcmp(name, "connection_type")) e->conn_type = val; else if (!strcmp(name, "port_id")) @@ -627,7 +623,7 @@ void snd_hda_eld_proc_free(struct hda_codec *codec, struct hdmi_eld *eld) #endif /* CONFIG_PROC_FS */ /* update PCM info based on ELD */ -void snd_hdmi_eld_update_pcm_info(struct hdmi_eld *eld, +void snd_hdmi_eld_update_pcm_info(struct parsed_hdmi_eld *e, struct hda_pcm_stream *hinfo) { u32 rates; @@ -644,8 +640,8 @@ void snd_hdmi_eld_update_pcm_info(struct hdmi_eld *eld, formats = SNDRV_PCM_FMTBIT_S16_LE; maxbps = 16; channels_max = 2; - for (i = 0; i < eld->sad_count; i++) { - struct cea_sad *a = &eld->sad[i]; + for (i = 0; i < e->sad_count; i++) { + struct cea_sad *a = &e->sad[i]; rates |= a->rates; if (a->channels > channels_max) channels_max = a->channels; diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index 05f1d594d17b..363cd487266b 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -713,10 +713,10 @@ struct cea_sad { /* * ELD: EDID Like Data */ -struct hdmi_eld { - bool monitor_present; - bool eld_valid; - int eld_size; +struct parsed_hdmi_eld { + /* + * all fields will be cleared before updating ELD + */ int baseline_len; int eld_ver; int cea_edid_ver; @@ -731,19 +731,26 @@ struct hdmi_eld { int spk_alloc; int sad_count; struct cea_sad sad[ELD_MAX_SAD]; - /* - * all fields above eld_buffer will be cleared before updating ELD - */ +}; + +struct hdmi_eld { + bool monitor_present; + bool eld_valid; + int eld_size; char eld_buffer[ELD_MAX_SIZE]; + struct parsed_hdmi_eld info; #ifdef CONFIG_PROC_FS struct snd_info_entry *proc_entry; #endif }; int snd_hdmi_get_eld_size(struct hda_codec *codec, hda_nid_t nid); -int snd_hdmi_get_eld(struct hdmi_eld *, struct hda_codec *, hda_nid_t); -void snd_hdmi_show_eld(struct hdmi_eld *eld); -void snd_hdmi_eld_update_pcm_info(struct hdmi_eld *eld, +int snd_hdmi_get_eld(struct hda_codec *codec, hda_nid_t nid, + unsigned char *buf, int *eld_size); +int snd_hdmi_parse_eld(struct parsed_hdmi_eld *e, + const unsigned char *buf, int size); +void snd_hdmi_show_eld(struct parsed_hdmi_eld *e); +void snd_hdmi_eld_update_pcm_info(struct parsed_hdmi_eld *e, struct hda_pcm_stream *hinfo); #ifdef CONFIG_PROC_FS diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 6bcdd667f514..1e381918eb82 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -531,7 +531,7 @@ static int hdmi_channel_allocation(struct hdmi_eld *eld, int channels) * expand ELD's notions to match the ones used by Audio InfoFrame. */ for (i = 0; i < ARRAY_SIZE(eld_speaker_allocation_bits); i++) { - if (eld->spk_alloc & (1 << i)) + if (eld->info.spk_alloc & (1 << i)) spk_mask |= eld_speaker_allocation_bits[i]; } @@ -545,7 +545,7 @@ static int hdmi_channel_allocation(struct hdmi_eld *eld, int channels) } } - snd_print_channel_allocation(eld->spk_alloc, buf, sizeof(buf)); + snd_print_channel_allocation(eld->info.spk_alloc, buf, sizeof(buf)); snd_printdd("HDMI: select CA 0x%x for %d-channel allocation: %s\n", ca, channels, buf); @@ -886,7 +886,7 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec, int pin_idx, ca = 0; memset(&ai, 0, sizeof(ai)); - if (eld->conn_type == 0) { /* HDMI */ + if (eld->info.conn_type == 0) { /* HDMI */ struct hdmi_audio_infoframe *hdmi_ai = &ai.hdmi; hdmi_ai->type = 0x84; @@ -895,7 +895,7 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec, int pin_idx, hdmi_ai->CC02_CT47 = channels - 1; hdmi_ai->CA = ca; hdmi_checksum_audio_infoframe(hdmi_ai); - } else if (eld->conn_type == 1) { /* DisplayPort */ + } else if (eld->info.conn_type == 1) { /* DisplayPort */ struct dp_audio_infoframe *dp_ai = &ai.dp; dp_ai->type = 0x84; @@ -1116,7 +1116,7 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo, /* Restrict capabilities by ELD if this isn't disabled */ if (!static_hdmi_pcm && eld->eld_valid) { - snd_hdmi_eld_update_pcm_info(eld, hinfo); + snd_hdmi_eld_update_pcm_info(&eld->info, hinfo); if (hinfo->channels_min > hinfo->channels_max || !hinfo->rates || !hinfo->formats) { per_cvt->assigned = 0; @@ -1177,8 +1177,6 @@ static void hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll) int present = snd_hda_pin_sense(codec, pin_nid); bool eld_valid = false; - memset(eld, 0, offsetof(struct hdmi_eld, eld_buffer)); - eld->monitor_present = !!(present & AC_PINSENSE_PRESENCE); if (eld->monitor_present) eld_valid = !!(present & AC_PINSENSE_ELDV); @@ -1189,8 +1187,20 @@ static void hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll) eld->eld_valid = false; if (eld_valid) { - if (!snd_hdmi_get_eld(eld, codec, pin_nid)) - snd_hdmi_show_eld(eld); + if (snd_hdmi_get_eld(codec, pin_nid, eld->eld_buffer, + &eld->eld_size) < 0) + eld_valid = false; + else { + memset(&eld->info, 0, sizeof(struct parsed_hdmi_eld)); + if (snd_hdmi_parse_eld(&eld->info, eld->eld_buffer, + eld->eld_size) < 0) + eld_valid = false; + } + + if (eld_valid) { + snd_hdmi_show_eld(&eld->info); + eld->eld_valid = true; + } else if (repoll) { queue_delayed_work(codec->bus->workq, &per_pin->work, -- cgit v1.2.3 From 4bd038f9d80216a6e95c5c36fae5054a83ea75d7 Mon Sep 17 00:00:00 2001 From: David Henningsson Date: Tue, 19 Feb 2013 16:11:25 +0100 Subject: ALSA: hda - hdmi: Protect ELD buffer Because the eld buffer can be simultaneously accessed from both workqueue context (updating) and process context (kcontrol read), we need to protect it with a mutex to guarantee consistency. To avoid holding the mutex while reading the ELD info from the codec, we introduce a temporary eld buffer. Signed-off-by: David Henningsson Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_eld.c | 8 +++++++- sound/pci/hda/hda_local.h | 1 + sound/pci/hda/patch_hdmi.c | 51 +++++++++++++++++++++++++++++++++++----------- 3 files changed, 47 insertions(+), 13 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_eld.c b/sound/pci/hda/hda_eld.c index 16066d7763ec..7dd846380a50 100644 --- a/sound/pci/hda/hda_eld.c +++ b/sound/pci/hda/hda_eld.c @@ -500,10 +500,13 @@ static void hdmi_print_eld_info(struct snd_info_entry *entry, [4 ... 7] = "reserved" }; + mutex_lock(&eld->lock); snd_iprintf(buffer, "monitor_present\t\t%d\n", eld->monitor_present); snd_iprintf(buffer, "eld_valid\t\t%d\n", eld->eld_valid); - if (!eld->eld_valid) + if (!eld->eld_valid) { + mutex_unlock(&eld->lock); return; + } snd_iprintf(buffer, "monitor_name\t\t%s\n", e->monitor_name); snd_iprintf(buffer, "connection_type\t\t%s\n", eld_connection_type_names[e->conn_type]); @@ -525,6 +528,7 @@ static void hdmi_print_eld_info(struct snd_info_entry *entry, for (i = 0; i < e->sad_count; i++) hdmi_print_sad_info(i, e->sad + i, buffer); + mutex_unlock(&eld->lock); } static void hdmi_write_eld_info(struct snd_info_entry *entry, @@ -538,6 +542,7 @@ static void hdmi_write_eld_info(struct snd_info_entry *entry, long long val; unsigned int n; + mutex_lock(&eld->lock); while (!snd_info_get_line(buffer, line, sizeof(line))) { if (sscanf(line, "%s %llx", name, &val) != 2) continue; @@ -589,6 +594,7 @@ static void hdmi_write_eld_info(struct snd_info_entry *entry, e->sad_count = n + 1; } } + mutex_unlock(&eld->lock); } diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index 363cd487266b..83b7486c8eff 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -739,6 +739,7 @@ struct hdmi_eld { int eld_size; char eld_buffer[ELD_MAX_SIZE]; struct parsed_hdmi_eld info; + struct mutex lock; #ifdef CONFIG_PROC_FS struct snd_info_entry *proc_entry; #endif diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 1e381918eb82..e77735d804a3 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -91,6 +91,7 @@ struct hdmi_spec { struct hda_pcm pcm_rec[MAX_HDMI_PINS]; unsigned int channels_max; /* max over all cvts */ + struct hdmi_eld temp_eld; /* * Non-generic ATI/NVIDIA specific */ @@ -352,7 +353,9 @@ static int hdmi_eld_ctl_info(struct snd_kcontrol *kcontrol, pin_idx = kcontrol->private_value; eld = &spec->pins[pin_idx].sink_eld; + mutex_lock(&eld->lock); uinfo->count = eld->eld_valid ? eld->eld_size : 0; + mutex_unlock(&eld->lock); return 0; } @@ -368,7 +371,9 @@ static int hdmi_eld_ctl_get(struct snd_kcontrol *kcontrol, pin_idx = kcontrol->private_value; eld = &spec->pins[pin_idx].sink_eld; + mutex_lock(&eld->lock); if (eld->eld_size > ARRAY_SIZE(ucontrol->value.bytes.data)) { + mutex_unlock(&eld->lock); snd_BUG(); return -EINVAL; } @@ -378,6 +383,7 @@ static int hdmi_eld_ctl_get(struct snd_kcontrol *kcontrol, if (eld->eld_valid) memcpy(ucontrol->value.bytes.data, eld->eld_buffer, eld->eld_size); + mutex_unlock(&eld->lock); return 0; } @@ -1164,7 +1170,9 @@ static int hdmi_read_pin_conn(struct hda_codec *codec, int pin_idx) static void hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll) { struct hda_codec *codec = per_pin->codec; - struct hdmi_eld *eld = &per_pin->sink_eld; + struct hdmi_spec *spec = codec->spec; + struct hdmi_eld *eld = &spec->temp_eld; + struct hdmi_eld *pin_eld = &per_pin->sink_eld; hda_nid_t pin_nid = per_pin->pin_nid; /* * Always execute a GetPinSense verb here, even when called from @@ -1175,38 +1183,56 @@ static void hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll) * the unsolicited response to avoid custom WARs. */ int present = snd_hda_pin_sense(codec, pin_nid); - bool eld_valid = false; + bool update_eld = false; + bool eld_changed = false; - eld->monitor_present = !!(present & AC_PINSENSE_PRESENCE); - if (eld->monitor_present) - eld_valid = !!(present & AC_PINSENSE_ELDV); + pin_eld->monitor_present = !!(present & AC_PINSENSE_PRESENCE); + if (pin_eld->monitor_present) + eld->eld_valid = !!(present & AC_PINSENSE_ELDV); + else + eld->eld_valid = false; _snd_printd(SND_PR_VERBOSE, "HDMI status: Codec=%d Pin=%d Presence_Detect=%d ELD_Valid=%d\n", - codec->addr, pin_nid, eld->monitor_present, eld_valid); + codec->addr, pin_nid, eld->monitor_present, eld->eld_valid); - eld->eld_valid = false; - if (eld_valid) { + if (eld->eld_valid) { if (snd_hdmi_get_eld(codec, pin_nid, eld->eld_buffer, &eld->eld_size) < 0) - eld_valid = false; + eld->eld_valid = false; else { memset(&eld->info, 0, sizeof(struct parsed_hdmi_eld)); if (snd_hdmi_parse_eld(&eld->info, eld->eld_buffer, eld->eld_size) < 0) - eld_valid = false; + eld->eld_valid = false; } - if (eld_valid) { + if (eld->eld_valid) { snd_hdmi_show_eld(&eld->info); - eld->eld_valid = true; + update_eld = true; } else if (repoll) { queue_delayed_work(codec->bus->workq, &per_pin->work, msecs_to_jiffies(300)); + return; } } + + mutex_lock(&pin_eld->lock); + if (pin_eld->eld_valid && !eld->eld_valid) + update_eld = true; + if (update_eld) { + pin_eld->eld_valid = eld->eld_valid; + eld_changed = memcmp(pin_eld->eld_buffer, eld->eld_buffer, + eld->eld_size) != 0; + if (eld_changed) + memcpy(pin_eld->eld_buffer, eld->eld_buffer, + eld->eld_size); + pin_eld->eld_size = eld->eld_size; + pin_eld->info = eld->info; + } + mutex_unlock(&pin_eld->lock); } static void hdmi_repoll_eld(struct work_struct *work) @@ -1674,6 +1700,7 @@ static int generic_hdmi_init_per_pins(struct hda_codec *codec) struct hdmi_eld *eld = &per_pin->sink_eld; per_pin->codec = codec; + mutex_init(&eld->lock); INIT_DELAYED_WORK(&per_pin->work, hdmi_repoll_eld); snd_hda_eld_proc_new(codec, eld, pin_idx); } -- cgit v1.2.3 From 92c69e796b83f922c81a5cf7968cb114a7fa14c6 Mon Sep 17 00:00:00 2001 From: David Henningsson Date: Tue, 19 Feb 2013 16:11:26 +0100 Subject: ALSA: hda - hdmi: Notify userspace when ELD control changes ELD validity can change during the lifetime of a presence detect, so we need to be able to listen for changes on the ELD control. Signed-off-by: David Henningsson Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_hdmi.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index e77735d804a3..21425fb51fe0 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -75,6 +75,7 @@ struct hdmi_spec_per_pin { struct hda_codec *codec; struct hdmi_eld sink_eld; struct delayed_work work; + struct snd_kcontrol *eld_ctl; int repoll_count; bool non_pcm; bool chmap_set; /* channel-map override by ALSA API? */ @@ -413,6 +414,7 @@ static int hdmi_create_eld_ctl(struct hda_codec *codec, int pin_idx, if (err < 0) return err; + spec->pins[pin_idx].eld_ctl = kctl; return 0; } @@ -1220,11 +1222,14 @@ static void hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll) } mutex_lock(&pin_eld->lock); - if (pin_eld->eld_valid && !eld->eld_valid) + if (pin_eld->eld_valid && !eld->eld_valid) { update_eld = true; + eld_changed = true; + } if (update_eld) { pin_eld->eld_valid = eld->eld_valid; - eld_changed = memcmp(pin_eld->eld_buffer, eld->eld_buffer, + eld_changed = pin_eld->eld_size != eld->eld_size || + memcmp(pin_eld->eld_buffer, eld->eld_buffer, eld->eld_size) != 0; if (eld_changed) memcpy(pin_eld->eld_buffer, eld->eld_buffer, @@ -1233,6 +1238,11 @@ static void hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll) pin_eld->info = eld->info; } mutex_unlock(&pin_eld->lock); + + if (eld_changed) + snd_ctl_notify(codec->bus->card, + SNDRV_CTL_EVENT_MASK_VALUE | SNDRV_CTL_EVENT_MASK_INFO, + &per_pin->eld_ctl->id); } static void hdmi_repoll_eld(struct work_struct *work) -- cgit v1.2.3 From 30efd8debd1ef30be342d374f01e993509f5b76b Mon Sep 17 00:00:00 2001 From: David Henningsson Date: Fri, 22 Feb 2013 10:16:28 +0100 Subject: ALSA: hda - hdmi: Make jacks phantom, if they're not detectable Just as for analog codecs, a jack that isn't suitable for detection (in this case, NO_PRESENCE was set) should be a phantom Jack instead of a normal one. Thanks to Raymond Yau for spotting. Cc: stable@vger.kernel.org BugLink: https://bugs.launchpad.net/bugs/961286 BugLink: https://bugzilla.redhat.com/show_bug.cgi?id=903869 Signed-off-by: David Henningsson Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_hdmi.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 21425fb51fe0..78e1827d0a95 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -1640,6 +1640,9 @@ static int generic_hdmi_build_jack(struct hda_codec *codec, int pin_idx) if (pcmdev > 0) sprintf(hdmi_str + strlen(hdmi_str), ",pcm=%d", pcmdev); + if (!is_jack_detectable(codec, per_pin->pin_nid)) + strncat(hdmi_str, " Phantom", + sizeof(hdmi_str) - strlen(hdmi_str) - 1); return snd_hda_jack_add_kctl(codec, per_pin->pin_nid, hdmi_str, 0); } -- cgit v1.2.3 From 8554ee405713f8572ed2e8b7d711f4884f253181 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 25 Feb 2013 09:54:43 +0100 Subject: ALSA: hda - Enable beep for ASUS EeeBox EBP1501P Reported-by: Harald Becker Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 61478fd82565..2d4237bc0d8e 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -928,6 +928,7 @@ static int alc_codec_rename_from_preset(struct hda_codec *codec) static const struct snd_pci_quirk beep_white_list[] = { SND_PCI_QUIRK(0x1043, 0x103c, "ASUS", 1), SND_PCI_QUIRK(0x1043, 0x829f, "ASUS", 1), + SND_PCI_QUIRK(0x1043, 0x8376, "EeePC", 1), SND_PCI_QUIRK(0x1043, 0x83ce, "EeePC", 1), SND_PCI_QUIRK(0x1043, 0x831a, "EeePC", 1), SND_PCI_QUIRK(0x1043, 0x834a, "EeePC", 1), -- cgit v1.2.3 From 25336e8ae2d2fa64c9c4cc2c9c28f641134c9fa9 Mon Sep 17 00:00:00 2001 From: Mengdong Lin Date: Thu, 7 Mar 2013 14:10:25 -0500 Subject: ALSA: hda - check NULL pointer when creating SPDIF controls If the SPDIF control array cannot be reallocated, the function will return to avoid dereferencing a NULL pointer. Signed-off-by: Mengdong Lin Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_codec.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 04b57383e8cb..3dc656688c08 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -3334,6 +3334,8 @@ int snd_hda_create_dig_out_ctls(struct hda_codec *codec, return -EBUSY; } spdif = snd_array_new(&codec->spdif_out); + if (!spdif) + return -ENOMEM; for (dig_mix = dig_mixes; dig_mix->name; dig_mix++) { kctl = snd_ctl_new1(dig_mix, codec); if (!kctl) -- cgit v1.2.3 From 4c7a548a70a44269266858f65c3b5fc9c3ace057 Mon Sep 17 00:00:00 2001 From: Mengdong Lin Date: Thu, 7 Mar 2013 14:11:05 -0500 Subject: ALSA: hda - check NULL pointer when creating SPDIF PCM switch If the new control cannot be created, this function will return to avoid snd_hda_ctl_add dereferencing a NULL control pointer. Signed-off-by: Mengdong Lin Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_codec.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 3dc656688c08..97c68dd24ef5 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -3433,11 +3433,16 @@ static struct snd_kcontrol_new spdif_share_sw = { int snd_hda_create_spdif_share_sw(struct hda_codec *codec, struct hda_multi_out *mout) { + struct snd_kcontrol *kctl; + if (!mout->dig_out_nid) return 0; + + kctl = snd_ctl_new1(&spdif_share_sw, mout); + if (!kctl) + return -ENOMEM; /* ATTENTION: here mout is passed as private_data, instead of codec */ - return snd_hda_ctl_add(codec, mout->dig_out_nid, - snd_ctl_new1(&spdif_share_sw, mout)); + return snd_hda_ctl_add(codec, mout->dig_out_nid, kctl); } EXPORT_SYMBOL_HDA(snd_hda_create_spdif_share_sw); -- cgit v1.2.3 From 3bc085a12d8f9f3e45a4ac0cc24a34abd5b20657 Mon Sep 17 00:00:00 2001 From: Xi Wang Date: Thu, 7 Mar 2013 00:13:51 -0500 Subject: ALSA: hda/ca0132 - Avoid division by zero in dspxfr_one_seg() Move the zero check `hda_frame_size_words == 0' before the modulus `buffer_size_words % hda_frame_size_words'. Also remove the redundant null check `buffer_addx == NULL'. Signed-off-by: Xi Wang Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_ca0132.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index db02c1e96b08..eefc4563b2f9 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -2298,6 +2298,11 @@ static int dspxfr_one_seg(struct hda_codec *codec, hda_frame_size_words = ((sample_rate_div == 0) ? 0 : (num_chans * sample_rate_mul / sample_rate_div)); + if (hda_frame_size_words == 0) { + snd_printdd(KERN_ERR "frmsz zero\n"); + return -EINVAL; + } + buffer_size_words = min(buffer_size_words, (unsigned int)(UC_RANGE(chip_addx, 1) ? 65536 : 32768)); @@ -2308,8 +2313,7 @@ static int dspxfr_one_seg(struct hda_codec *codec, chip_addx, hda_frame_size_words, num_chans, sample_rate_mul, sample_rate_div, buffer_size_words); - if ((buffer_addx == NULL) || (hda_frame_size_words == 0) || - (buffer_size_words < hda_frame_size_words)) { + if (buffer_size_words < hda_frame_size_words) { snd_printdd(KERN_ERR "dspxfr_one_seg:failed\n"); return -EINVAL; } -- cgit v1.2.3 From 84dfd0ac231f69d70e100e712ad5e5f0092ad46b Mon Sep 17 00:00:00 2001 From: Kailang Yang Date: Thu, 7 Mar 2013 09:19:38 +0100 Subject: ALSA: hda - Add support of new codec ALC233 It's compatible with ALC282. Signed-off-by: Kailang Yang Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 2d4237bc0d8e..563c24df4d6f 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -3163,6 +3163,7 @@ static int patch_alc269(struct hda_codec *codec) case 0x10ec0290: spec->codec_variant = ALC269_TYPE_ALC280; break; + case 0x10ec0233: case 0x10ec0282: case 0x10ec0283: spec->codec_variant = ALC269_TYPE_ALC282; @@ -3862,6 +3863,7 @@ static int patch_alc680(struct hda_codec *codec) */ static const struct hda_codec_preset snd_hda_preset_realtek[] = { { .id = 0x10ec0221, .name = "ALC221", .patch = patch_alc269 }, + { .id = 0x10ec0233, .name = "ALC233", .patch = patch_alc269 }, { .id = 0x10ec0260, .name = "ALC260", .patch = patch_alc260 }, { .id = 0x10ec0262, .name = "ALC262", .patch = patch_alc262 }, { .id = 0x10ec0267, .name = "ALC267", .patch = patch_alc268 }, -- cgit v1.2.3 From b5f82b1044daef74059f454353a2ee97acbbe620 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 12 Mar 2013 16:47:30 +0100 Subject: ALSA: hda - Fix snd_hda_get_num_raw_conns() to return a correct value In the connection list expansion in hda_codec.c and hda_proc.c, the value returned from snd_hda_get_num_raw_conns() is used as the array size to store the connection list. However, the function returns simply a raw value of the AC_PAR_CONNLIST_LEN parameter, and the widget list with ranges isn't considered there. Thus it may return a smaller size than the actual list, which results in -ENOSPC in snd_hda_get_raw_conections(). This patch fixes the bug by parsing the connection list correctly also for snd_hda_get_num_raw_conns(). Reported-and-tested-by: David Henningsson Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_codec.c | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 97c68dd24ef5..a9ebcf9e3710 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -494,7 +494,7 @@ static unsigned int get_num_conns(struct hda_codec *codec, hda_nid_t nid) int snd_hda_get_num_raw_conns(struct hda_codec *codec, hda_nid_t nid) { - return get_num_conns(codec, nid) & AC_CLIST_LENGTH; + return snd_hda_get_raw_connections(codec, nid, NULL, 0); } /** @@ -517,9 +517,6 @@ int snd_hda_get_raw_connections(struct hda_codec *codec, hda_nid_t nid, hda_nid_t prev_nid; int null_count = 0; - if (snd_BUG_ON(!conn_list || max_conns <= 0)) - return -EINVAL; - parm = get_num_conns(codec, nid); if (!parm) return 0; @@ -545,7 +542,8 @@ int snd_hda_get_raw_connections(struct hda_codec *codec, hda_nid_t nid, AC_VERB_GET_CONNECT_LIST, 0); if (parm == -1 && codec->bus->rirb_error) return -EIO; - conn_list[0] = parm & mask; + if (conn_list) + conn_list[0] = parm & mask; return 1; } @@ -580,14 +578,20 @@ int snd_hda_get_raw_connections(struct hda_codec *codec, hda_nid_t nid, continue; } for (n = prev_nid + 1; n <= val; n++) { + if (conn_list) { + if (conns >= max_conns) + return -ENOSPC; + conn_list[conns] = n; + } + conns++; + } + } else { + if (conn_list) { if (conns >= max_conns) return -ENOSPC; - conn_list[conns++] = n; + conn_list[conns] = val; } - } else { - if (conns >= max_conns) - return -ENOSPC; - conn_list[conns++] = val; + conns++; } prev_nid = val; } -- cgit v1.2.3 From 303985f81019571db0b3a6f01fc7f03eb350657e Mon Sep 17 00:00:00 2001 From: David Henningsson Date: Thu, 14 Mar 2013 15:28:29 +0100 Subject: ALSA: hda - Disable IDT eapd_switch if there are no internal speakers If there are no internal speakers, we should not turn the eapd switch off, because it might be necessary to keep high for Headphone. BugLink: https://bugs.launchpad.net/bugs/1155016 Signed-off-by: David Henningsson Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_sigmatel.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 83d5335ac348..dafe04ae8c72 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -815,6 +815,29 @@ static int find_mute_led_cfg(struct hda_codec *codec, int default_polarity) return 0; } +/* check whether a built-in speaker is included in parsed pins */ +static bool has_builtin_speaker(struct hda_codec *codec) +{ + struct sigmatel_spec *spec = codec->spec; + hda_nid_t *nid_pin; + int nids, i; + + if (spec->gen.autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT) { + nid_pin = spec->gen.autocfg.line_out_pins; + nids = spec->gen.autocfg.line_outs; + } else { + nid_pin = spec->gen.autocfg.speaker_pins; + nids = spec->gen.autocfg.speaker_outs; + } + + for (i = 0; i < nids; i++) { + unsigned int def_conf = snd_hda_codec_get_pincfg(codec, nid_pin[i]); + if (snd_hda_get_input_pin_attr(def_conf) == INPUT_PIN_ATTR_INT) + return true; + } + return false; +} + /* * PC beep controls */ @@ -3890,6 +3913,12 @@ static int patch_stac92hd73xx(struct hda_codec *codec) return err; } + /* Don't GPIO-mute speakers if there are no internal speakers, because + * the GPIO might be necessary for Headphone + */ + if (spec->eapd_switch && !has_builtin_speaker(codec)) + spec->eapd_switch = 0; + codec->proc_widget_hook = stac92hd7x_proc_hook; snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); -- cgit v1.2.3 From d1d28500cccc269fdbf81ba33d7328d1d2c04b2f Mon Sep 17 00:00:00 2001 From: Dylan Reid Date: Thu, 14 Mar 2013 17:27:44 -0700 Subject: ALSA: hda/ca0132 - Check if dspload_image succeeded. If dspload_image() fails, it was ignored and dspload_wait_loaded() was still called. dsp_loaded should never be set to true in this case, skip it. The check in dspload_wait_loaded() return true if the DSP is loaded or if it never started. Signed-off-by: Dylan Reid Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_ca0132.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index eefc4563b2f9..cf24b75108d9 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -4351,12 +4351,16 @@ static bool ca0132_download_dsp_images(struct hda_codec *codec) return false; dsp_os_image = (struct dsp_image_seg *)(fw_entry->data); - dspload_image(codec, dsp_os_image, 0, 0, true, 0); + if (dspload_image(codec, dsp_os_image, 0, 0, true, 0)) { + pr_err("ca0132 dspload_image failed.\n"); + goto exit_download; + } + dsp_loaded = dspload_wait_loaded(codec); +exit_download: release_firmware(fw_entry); - return dsp_loaded; } -- cgit v1.2.3 From e8f1bd5d77484a1088797fd5689b1a37148a170e Mon Sep 17 00:00:00 2001 From: Dylan Reid Date: Thu, 14 Mar 2013 17:27:45 -0700 Subject: ALSA: hda/ca0132 - Check download state of DSP. Instead of using the dspload_is_loaded() function, check the dsp_state that is kept in the spec. The dspload_is_loaded() function returns true if the DSP transfer was never started. This false-positive leads to multiple second delays when ca0132_setup_efaults() times out on each write. Signed-off-by: Dylan Reid Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_ca0132.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index cf24b75108d9..225d1d52abad 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -3239,7 +3239,7 @@ static int ca0132_set_vipsource(struct hda_codec *codec, int val) struct ca0132_spec *spec = codec->spec; unsigned int tmp; - if (!dspload_is_loaded(codec)) + if (spec->dsp_state != DSP_DOWNLOADED) return 0; /* if CrystalVoice if off, vipsource should be 0 */ @@ -4267,11 +4267,12 @@ static void ca0132_refresh_widget_caps(struct hda_codec *codec) */ static void ca0132_setup_defaults(struct hda_codec *codec) { + struct ca0132_spec *spec = codec->spec; unsigned int tmp; int num_fx; int idx, i; - if (!dspload_is_loaded(codec)) + if (spec->dsp_state != DSP_DOWNLOADED) return; /* out, in effects + voicefx */ -- cgit v1.2.3 From b714a7106ba5423c418c25e6231116560f8a9ef8 Mon Sep 17 00:00:00 2001 From: Dylan Reid Date: Thu, 14 Mar 2013 17:27:46 -0700 Subject: ALSA: hda/ca0132 - Remove extra setting of dsp_state. spec->dsp_state is initialized to DSP_DOWNLOAD_INIT, no need to reset and check it in ca0132_download_dsp(). Signed-off-by: Dylan Reid Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_ca0132.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index 225d1d52abad..0792b5725f9c 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -4372,16 +4372,13 @@ static void ca0132_download_dsp(struct hda_codec *codec) #ifndef CONFIG_SND_HDA_CODEC_CA0132_DSP return; /* NOP */ #endif - spec->dsp_state = DSP_DOWNLOAD_INIT; - if (spec->dsp_state == DSP_DOWNLOAD_INIT) { - chipio_enable_clocks(codec); - spec->dsp_state = DSP_DOWNLOADING; - if (!ca0132_download_dsp_images(codec)) - spec->dsp_state = DSP_DOWNLOAD_FAILED; - else - spec->dsp_state = DSP_DOWNLOADED; - } + chipio_enable_clocks(codec); + spec->dsp_state = DSP_DOWNLOADING; + if (!ca0132_download_dsp_images(codec)) + spec->dsp_state = DSP_DOWNLOAD_FAILED; + else + spec->dsp_state = DSP_DOWNLOADED; if (spec->dsp_state == DSP_DOWNLOADED) ca0132_set_dsp_msr(codec, true); -- cgit v1.2.3 From 6d3073e124e1a6138b929479301d3a7ecde00f27 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 15 Mar 2013 14:23:32 +0100 Subject: ALSA: hda - Fix missing EAPD/GPIO setup for Cirrus codecs During the transition to the generic parser, the hook to the codec specific automute function was forgotten. This resulted in the silent output on some MacBooks. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_cirrus.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'sound/pci/hda') diff --git a/sound/pci/hda/patch_cirrus.c b/sound/pci/hda/patch_cirrus.c index 72ebb8a36b13..60d08f669f0c 100644 --- a/sound/pci/hda/patch_cirrus.c +++ b/sound/pci/hda/patch_cirrus.c @@ -506,6 +506,8 @@ static int patch_cs420x(struct hda_codec *codec) if (!spec) return -ENOMEM; + spec->gen.automute_hook = cs_automute; + snd_hda_pick_fixup(codec, cs420x_models, cs420x_fixup_tbl, cs420x_fixups); snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); @@ -893,6 +895,8 @@ static int patch_cs4210(struct hda_codec *codec) if (!spec) return -ENOMEM; + spec->gen.automute_hook = cs_automute; + snd_hda_pick_fixup(codec, cs421x_models, cs421x_fixup_tbl, cs421x_fixups); snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); -- cgit v1.2.3