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 diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig index 7105c3d..ba1dbd8 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 507fe8a..c218bf4 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 cd2dbaf..99e61b9 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 v0.10.2 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 diff --git a/sound/pci/hda/ca0132_regs.h b/sound/pci/hda/ca0132_regs.h new file mode 100644 index 0000000..831ca9c --- /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 49750a9..da65535 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 v0.10.2 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 diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index da65535..846826d 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 v0.10.2 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 diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index 846826d..f5aea78 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 v0.10.2 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 diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index f5aea78..4d8a7ed 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 v0.10.2 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 diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index 4d8a7ed..a7b216e 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) @@ -1495,6 +1570,25 @@ static int dsp_allocate_ports_format(struct hda_codec *codec, } /* + * 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 */ struct dma_engine { @@ -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 v0.10.2 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 diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index c218bf4..c9f53bd 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 v0.10.2 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 diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index a7b216e..7a0425f 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 v0.10.2 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 diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index 7a0425f..5c6a056 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 v0.10.2 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 diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index 5c6a056..03f57c9 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 v0.10.2 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 diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index 03f57c9..2fd3121 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 v0.10.2 From 8afd0ef2639ea2b7bc1b3a0f927ab14e0df034df Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 7 Dec 2012 17:10:05 +0900 Subject: ASoC: wm8994: Fix variable double use Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c index 8241100..f2e63ac 100644 --- a/sound/soc/codecs/wm8994.c +++ b/sound/soc/codecs/wm8994.c @@ -3721,7 +3721,7 @@ static irqreturn_t wm8958_mic_irq(int irq, void *data) { struct wm8994_priv *wm8994 = data; struct snd_soc_codec *codec = wm8994->hubs.codec; - int reg, count; + int reg, count, ret; /* * Jack detection may have detected a removal simulataneously @@ -3767,11 +3767,11 @@ static irqreturn_t wm8958_mic_irq(int irq, void *data) /* Avoid a transient report when the accessory is being removed */ if (wm8994->jackdet) { - reg = snd_soc_read(codec, WM1811_JACKDET_CTRL); - if (reg < 0) { + ret = snd_soc_read(codec, WM1811_JACKDET_CTRL); + if (ret < 0) { dev_err(codec->dev, "Failed to read jack status: %d\n", - reg); - } else if (!(reg & WM1811_JACKDET_LVL)) { + ret); + } else if (!(ret & WM1811_JACKDET_LVL)) { dev_dbg(codec->dev, "Ignoring removed jack\n"); return IRQ_HANDLED; } -- cgit v0.10.2 From e4cc6153401fc59d6d4ca0317be5774bdbd399ba Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Wed, 19 Dec 2012 11:39:05 -0600 Subject: ALSA: usb-audio: support delay calculation on capture streams Enable delay report on capture path. The delay is reset when an URB is retired and increment at each call to .pointer based on frame counter changes. The precision of the delay information is limited to 1ms as in the playback case. This reverts commit 3f94fad09538ec988919ec3f371841182df71d04. Signed-off-by: Pierre-Louis Bossart Signed-off-by: Takashi Iwai diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index c659310..3a38447 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -59,7 +59,12 @@ snd_pcm_uframes_t snd_usb_pcm_delay(struct snd_usb_substream *subs, /* Approximation based on number of samples per USB frame (ms), some truncation for 44.1 but the estimate is good enough */ - est_delay = subs->last_delay - (frame_diff * rate / 1000); + est_delay = frame_diff * rate / 1000; + if (subs->direction == SNDRV_PCM_STREAM_PLAYBACK) + est_delay = subs->last_delay - est_delay; + else + est_delay = subs->last_delay + est_delay; + if (est_delay < 0) est_delay = 0; return est_delay; @@ -78,8 +83,7 @@ static snd_pcm_uframes_t snd_usb_pcm_pointer(struct snd_pcm_substream *substream return SNDRV_PCM_POS_XRUN; spin_lock(&subs->lock); hwptr_done = subs->hwptr_done; - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - substream->runtime->delay = snd_usb_pcm_delay(subs, + substream->runtime->delay = snd_usb_pcm_delay(subs, substream->runtime->rate); spin_unlock(&subs->lock); return hwptr_done / (substream->runtime->frame_bits >> 3); @@ -1147,6 +1151,10 @@ static void retire_capture_urb(struct snd_usb_substream *subs, int i, period_elapsed = 0; unsigned long flags; unsigned char *cp; + int current_frame_number; + + /* read frame number here, update pointer in critical section */ + current_frame_number = usb_get_current_frame_number(subs->dev); stride = runtime->frame_bits >> 3; @@ -1180,6 +1188,15 @@ static void retire_capture_urb(struct snd_usb_substream *subs, subs->transfer_done -= runtime->period_size; period_elapsed = 1; } + /* capture delay is by construction limited to one URB, + * reset delays here + */ + runtime->delay = subs->last_delay = 0; + + /* realign last_frame_number */ + subs->last_frame_number = current_frame_number; + subs->last_frame_number &= 0xFF; /* keep 8 LSBs */ + spin_unlock_irqrestore(&subs->lock, flags); /* copy a data chunk */ if (oldptr + bytes > runtime->buffer_size * stride) { -- cgit v0.10.2 From ff541f4b2a7546ffa8edf123f4b3b49bb24574e2 Mon Sep 17 00:00:00 2001 From: Chuansheng Liu Date: Fri, 21 Dec 2012 18:17:12 +0800 Subject: ASoC: core: giving WARN when device starting from non-off bias with idle_bias_off Just found some cases that some codec drivers set the bias to _STANDBY and set idle_bias_off to 1 during probing. It will cause unpaired runtime_get_sync/put() issue. Also as Mark suggested, there is no reason to start from _STANDBY bias with idle_bias_off == 1. So here giving one warning when detected (dapm.idle_bias_off == 1) and (dapm.bias_level != SND_SOC_BIAS_OFF) just after driver->probe(). Signed-off-by: liu chuansheng Signed-off-by: Mark Brown diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 91d592f..e0e8ce0 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -1107,6 +1107,10 @@ static int soc_probe_codec(struct snd_soc_card *card, "ASoC: failed to probe CODEC %d\n", ret); goto err_probe; } + WARN(codec->dapm.idle_bias_off && + codec->dapm.bias_level != SND_SOC_BIAS_OFF, + "codec %s can not start from non-off bias" + " with idle_bias_off==1\n", codec->name); } /* If the driver didn't set I/O up try regmap */ -- cgit v0.10.2 From 5ce568329e4fcf9e9050bff878f8157ca43bc882 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Wed, 12 Dec 2012 23:28:04 -0200 Subject: ASoC: wm8962: Add device tree support Add device tree support. Signed-off-by: Fabio Estevam Signed-off-by: Mark Brown diff --git a/Documentation/devicetree/bindings/sound/wm8962.txt b/Documentation/devicetree/bindings/sound/wm8962.txt new file mode 100644 index 0000000..dceb3b1 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/wm8962.txt @@ -0,0 +1,16 @@ +WM8962 audio CODEC + +This device supports I2C only. + +Required properties: + + - compatible : "wlf,wm8962" + + - reg : the I2C address of the device. + +Example: + +codec: wm8962@1a { + compatible = "wlf,wm8962"; + reg = <0x1a>; +}; diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c index bd4b0db..705d0a0 100644 --- a/sound/soc/codecs/wm8962.c +++ b/sound/soc/codecs/wm8962.c @@ -3758,10 +3758,17 @@ static const struct i2c_device_id wm8962_i2c_id[] = { }; MODULE_DEVICE_TABLE(i2c, wm8962_i2c_id); +static const struct of_device_id wm8962_of_match[] = { + { .compatible = "wlf,wm8962", }, + { } +}; +MODULE_DEVICE_TABLE(of, wm8962_of_match); + static struct i2c_driver wm8962_i2c_driver = { .driver = { .name = "wm8962", .owner = THIS_MODULE, + .of_match_table = wm8962_of_match, .pm = &wm8962_pm, }, .probe = wm8962_i2c_probe, -- cgit v0.10.2 From a2ce64750e07d74c51f02c5652edfc1aa2d8e894 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 20 Dec 2012 13:09:59 +0000 Subject: ASoC: wm8962: Convert to devm_input_allocate_device() Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c index 705d0a0..0b36803 100644 --- a/sound/soc/codecs/wm8962.c +++ b/sound/soc/codecs/wm8962.c @@ -3189,7 +3189,7 @@ static void wm8962_init_beep(struct snd_soc_codec *codec) struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec); int ret; - wm8962->beep = input_allocate_device(); + wm8962->beep = devm_input_allocate_device(codec->dev); if (!wm8962->beep) { dev_err(codec->dev, "Failed to allocate beep device\n"); return; @@ -3210,7 +3210,6 @@ static void wm8962_init_beep(struct snd_soc_codec *codec) ret = input_register_device(wm8962->beep); if (ret != 0) { - input_free_device(wm8962->beep); wm8962->beep = NULL; dev_err(codec->dev, "Failed to register beep device\n"); } @@ -3227,7 +3226,6 @@ static void wm8962_free_beep(struct snd_soc_codec *codec) struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec); device_remove_file(codec->dev, &dev_attr_beep); - input_unregister_device(wm8962->beep); cancel_work_sync(&wm8962->beep_work); wm8962->beep = NULL; -- cgit v0.10.2 From 346f1d40833c75ddb5e9e4b47fb00e20f8152762 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 12 Dec 2012 11:28:01 +0900 Subject: ASoC: wm8962: Unconditionally wait for the FLL to lock If the FLL is being shut down we will exit early so there is no need to check here and in fact we're checking the wrong thing anyway. Reported-by: Graeme Gregory Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c index 0b36803..e971028 100644 --- a/sound/soc/codecs/wm8962.c +++ b/sound/soc/codecs/wm8962.c @@ -2873,22 +2873,20 @@ static int wm8962_set_fll(struct snd_soc_codec *codec, int fll_id, int source, ret = 0; - if (fll1 & WM8962_FLL_ENA) { - /* This should be a massive overestimate but go even - * higher if we'll error out - */ - if (wm8962->irq) - timeout = msecs_to_jiffies(5); - else - timeout = msecs_to_jiffies(1); + /* This should be a massive overestimate but go even + * higher if we'll error out + */ + if (wm8962->irq) + timeout = msecs_to_jiffies(5); + else + timeout = msecs_to_jiffies(1); - timeout = wait_for_completion_timeout(&wm8962->fll_lock, - timeout); + timeout = wait_for_completion_timeout(&wm8962->fll_lock, + timeout); - if (timeout == 0 && wm8962->irq) { - dev_err(codec->dev, "FLL lock timed out"); - ret = -ETIMEDOUT; - } + if (timeout == 0 && wm8962->irq) { + dev_err(codec->dev, "FLL lock timed out"); + ret = -ETIMEDOUT; } wm8962->fll_fref = Fref; -- cgit v0.10.2 From c63f650c0dd7cee8c3684901b70cdb2d69acb1cd Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 14 Dec 2012 14:46:52 +0900 Subject: ASoC: wm5102: Split input PGA controls Though the controls are named as stereo controls in the part the main use case for the analogue inputs to the WM5102 is mono. Reflect this in the controls exposed to userspace, providing a series of mono controls rather than stereo ones. Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm5102.c b/sound/soc/codecs/wm5102.c index 688ade0..a16239f 100644 --- a/sound/soc/codecs/wm5102.c +++ b/sound/soc/codecs/wm5102.c @@ -608,32 +608,44 @@ SOC_SINGLE("IN2 High Performance Switch", ARIZONA_IN2L_CONTROL, SOC_SINGLE("IN3 High Performance Switch", ARIZONA_IN3L_CONTROL, ARIZONA_IN3_OSR_SHIFT, 1, 0), -SOC_DOUBLE_R_RANGE_TLV("IN1 Volume", ARIZONA_IN1L_CONTROL, - ARIZONA_IN1R_CONTROL, - ARIZONA_IN1L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), -SOC_DOUBLE_R_RANGE_TLV("IN2 Volume", ARIZONA_IN2L_CONTROL, - ARIZONA_IN2R_CONTROL, - ARIZONA_IN2L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), -SOC_DOUBLE_R_RANGE_TLV("IN3 Volume", ARIZONA_IN3L_CONTROL, - ARIZONA_IN3R_CONTROL, - ARIZONA_IN3L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), - -SOC_DOUBLE_R("IN1 Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_1L, - ARIZONA_ADC_DIGITAL_VOLUME_1R, ARIZONA_IN1L_MUTE_SHIFT, 1, 1), -SOC_DOUBLE_R("IN2 Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_2L, - ARIZONA_ADC_DIGITAL_VOLUME_2R, ARIZONA_IN2L_MUTE_SHIFT, 1, 1), -SOC_DOUBLE_R("IN3 Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_3L, - ARIZONA_ADC_DIGITAL_VOLUME_3R, ARIZONA_IN3L_MUTE_SHIFT, 1, 1), - -SOC_DOUBLE_R_TLV("IN1 Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_1L, - ARIZONA_ADC_DIGITAL_VOLUME_1R, ARIZONA_IN1L_DIG_VOL_SHIFT, - 0xbf, 0, digital_tlv), -SOC_DOUBLE_R_TLV("IN2 Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_2L, - ARIZONA_ADC_DIGITAL_VOLUME_2R, ARIZONA_IN2L_DIG_VOL_SHIFT, - 0xbf, 0, digital_tlv), -SOC_DOUBLE_R_TLV("IN3 Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_3L, - ARIZONA_ADC_DIGITAL_VOLUME_3R, ARIZONA_IN3L_DIG_VOL_SHIFT, - 0xbf, 0, digital_tlv), +SOC_SINGLE_RANGE_TLV("IN1L Volume", ARIZONA_IN1L_CONTROL, + ARIZONA_IN1L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), +SOC_SINGLE_RANGE_TLV("IN1R Volume", ARIZONA_IN1R_CONTROL, + ARIZONA_IN1R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), +SOC_SINGLE_RANGE_TLV("IN2L Volume", ARIZONA_IN2L_CONTROL, + ARIZONA_IN2L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), +SOC_SINGLE_RANGE_TLV("IN2R Volume", ARIZONA_IN2R_CONTROL, + ARIZONA_IN2R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), +SOC_SINGLE_RANGE_TLV("IN3L Volume", ARIZONA_IN3L_CONTROL, + ARIZONA_IN3L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), +SOC_SINGLE_RANGE_TLV("IN3R Volume", ARIZONA_IN3R_CONTROL, + ARIZONA_IN3R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), + +SOC_SINGLE("IN1L Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_1L, + ARIZONA_IN1L_MUTE_SHIFT, 1, 1), +SOC_SINGLE("IN1R Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_1R, + ARIZONA_IN1R_MUTE_SHIFT, 1, 1), +SOC_SINGLE("IN2L Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_2L, + ARIZONA_IN2L_MUTE_SHIFT, 1, 1), +SOC_SINGLE("IN2R Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_2R, + ARIZONA_IN2R_MUTE_SHIFT, 1, 1), +SOC_SINGLE("IN3L Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_3L, + ARIZONA_IN3L_MUTE_SHIFT, 1, 1), +SOC_SINGLE("IN3R Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_3R, + ARIZONA_IN3R_MUTE_SHIFT, 1, 1), + +SOC_SINGLE_TLV("IN1L Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_1L, + ARIZONA_IN1L_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv), +SOC_SINGLE_TLV("IN1R Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_1R, + ARIZONA_IN1R_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv), +SOC_SINGLE_TLV("IN2L Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_2L, + ARIZONA_IN2L_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv), +SOC_SINGLE_TLV("IN2R Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_2R, + ARIZONA_IN2R_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv), +SOC_SINGLE_TLV("IN3L Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_3L, + ARIZONA_IN3L_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv), +SOC_SINGLE_TLV("IN3R Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_3R, + ARIZONA_IN3R_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv), SOC_ENUM("Input Ramp Up", arizona_in_vi_ramp), SOC_ENUM("Input Ramp Down", arizona_in_vd_ramp), -- cgit v0.10.2 From 02482da46ec13856c9244812db13d4d1be61bff7 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 14 Dec 2012 15:18:30 +0900 Subject: ASoC: wm5110: Split input PGA controls Though the controls are named as stereo controls in the part the main use case for the analogue inputs to the WM5102 is mono. Reflect this in the controls exposed to userspace, providing a series of mono controls rather than stereo ones. Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm5110.c b/sound/soc/codecs/wm5110.c index ae80c8c..9cdac35 100644 --- a/sound/soc/codecs/wm5110.c +++ b/sound/soc/codecs/wm5110.c @@ -52,37 +52,52 @@ SOC_SINGLE("IN3 High Performance Switch", ARIZONA_IN3L_CONTROL, SOC_SINGLE("IN4 High Performance Switch", ARIZONA_IN4L_CONTROL, ARIZONA_IN4_OSR_SHIFT, 1, 0), -SOC_DOUBLE_R_RANGE_TLV("IN1 Volume", ARIZONA_IN1L_CONTROL, - ARIZONA_IN1R_CONTROL, - ARIZONA_IN1L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), -SOC_DOUBLE_R_RANGE_TLV("IN2 Volume", ARIZONA_IN2L_CONTROL, - ARIZONA_IN2R_CONTROL, - ARIZONA_IN2L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), -SOC_DOUBLE_R_RANGE_TLV("IN3 Volume", ARIZONA_IN3L_CONTROL, - ARIZONA_IN3R_CONTROL, - ARIZONA_IN3L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), - -SOC_DOUBLE_R("IN1 Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_1L, - ARIZONA_ADC_DIGITAL_VOLUME_1R, ARIZONA_IN1L_MUTE_SHIFT, 1, 1), -SOC_DOUBLE_R("IN2 Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_2L, - ARIZONA_ADC_DIGITAL_VOLUME_2R, ARIZONA_IN2L_MUTE_SHIFT, 1, 1), -SOC_DOUBLE_R("IN3 Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_3L, - ARIZONA_ADC_DIGITAL_VOLUME_3R, ARIZONA_IN3L_MUTE_SHIFT, 1, 1), -SOC_DOUBLE_R("IN4 Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_4L, - ARIZONA_ADC_DIGITAL_VOLUME_4R, ARIZONA_IN4L_MUTE_SHIFT, 1, 1), - -SOC_DOUBLE_R_TLV("IN1 Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_1L, - ARIZONA_ADC_DIGITAL_VOLUME_1R, ARIZONA_IN1L_DIG_VOL_SHIFT, - 0xbf, 0, digital_tlv), -SOC_DOUBLE_R_TLV("IN2 Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_2L, - ARIZONA_ADC_DIGITAL_VOLUME_2R, ARIZONA_IN2L_DIG_VOL_SHIFT, - 0xbf, 0, digital_tlv), -SOC_DOUBLE_R_TLV("IN3 Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_3L, - ARIZONA_ADC_DIGITAL_VOLUME_3R, ARIZONA_IN3L_DIG_VOL_SHIFT, - 0xbf, 0, digital_tlv), -SOC_DOUBLE_R_TLV("IN4 Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_4L, - ARIZONA_ADC_DIGITAL_VOLUME_4R, ARIZONA_IN4L_DIG_VOL_SHIFT, - 0xbf, 0, digital_tlv), +SOC_SINGLE_RANGE_TLV("IN1L Volume", ARIZONA_IN1L_CONTROL, + ARIZONA_IN1L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), +SOC_SINGLE_RANGE_TLV("IN1R Volume", ARIZONA_IN1R_CONTROL, + ARIZONA_IN1R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), +SOC_SINGLE_RANGE_TLV("IN2L Volume", ARIZONA_IN2L_CONTROL, + ARIZONA_IN2L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), +SOC_SINGLE_RANGE_TLV("IN2R Volume", ARIZONA_IN2R_CONTROL, + ARIZONA_IN2R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), +SOC_SINGLE_RANGE_TLV("IN3L Volume", ARIZONA_IN3L_CONTROL, + ARIZONA_IN3L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), +SOC_SINGLE_RANGE_TLV("IN3R Volume", ARIZONA_IN3R_CONTROL, + ARIZONA_IN3R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), + +SOC_SINGLE("IN1L Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_1L, + ARIZONA_IN1L_MUTE_SHIFT, 1, 1), +SOC_SINGLE("IN1R Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_1R, + ARIZONA_IN1R_MUTE_SHIFT, 1, 1), +SOC_SINGLE("IN2L Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_2L, + ARIZONA_IN2L_MUTE_SHIFT, 1, 1), +SOC_SINGLE("IN2R Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_2R, + ARIZONA_IN2R_MUTE_SHIFT, 1, 1), +SOC_SINGLE("IN3L Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_3L, + ARIZONA_IN3L_MUTE_SHIFT, 1, 1), +SOC_SINGLE("IN3R Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_3R, + ARIZONA_IN3R_MUTE_SHIFT, 1, 1), +SOC_SINGLE("IN4L Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_4L, + ARIZONA_IN4L_MUTE_SHIFT, 1, 1), +SOC_SINGLE("IN4R Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_4R, + ARIZONA_IN4R_MUTE_SHIFT, 1, 1), + +SOC_SINGLE_TLV("IN1L Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_1L, + ARIZONA_IN1L_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv), +SOC_SINGLE_TLV("IN1R Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_1R, + ARIZONA_IN1R_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv), +SOC_SINGLE_TLV("IN2L Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_2L, + ARIZONA_IN2L_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv), +SOC_SINGLE_TLV("IN2R Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_2R, + ARIZONA_IN2R_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv), +SOC_SINGLE_TLV("IN3L Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_3L, + ARIZONA_IN3L_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv), +SOC_SINGLE_TLV("IN3R Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_3R, + ARIZONA_IN3R_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv), +SOC_SINGLE_TLV("IN4L Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_4L, + ARIZONA_IN4L_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv), +SOC_SINGLE_TLV("IN4R Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_4R, + ARIZONA_IN4R_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv), SOC_ENUM("Input Ramp Up", arizona_in_vi_ramp), SOC_ENUM("Input Ramp Down", arizona_in_vd_ramp), -- cgit v0.10.2 From 845571cce6aebffd6de9b9024daf789f333a734d Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 18 Dec 2012 13:47:57 +0000 Subject: ASoC: arizona: Add noise gate hold time enumeration Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c index adf397b..819920d 100644 --- a/sound/soc/codecs/arizona.c +++ b/sound/soc/codecs/arizona.c @@ -275,6 +275,15 @@ const struct soc_enum arizona_lhpf4_mode = arizona_lhpf_mode_text); EXPORT_SYMBOL_GPL(arizona_lhpf4_mode); +static const char *arizona_ng_hold_text[] = { + "30ms", "120ms", "250ms", "500ms", +}; + +const struct soc_enum arizona_ng_hold = + SOC_ENUM_SINGLE(ARIZONA_NOISE_GATE_CONTROL, ARIZONA_NGATE_HOLD_SHIFT, + 4, arizona_ng_hold_text); +EXPORT_SYMBOL_GPL(arizona_ng_hold); + int arizona_in_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { diff --git a/sound/soc/codecs/arizona.h b/sound/soc/codecs/arizona.h index 41dae1e..3d083d0 100644 --- a/sound/soc/codecs/arizona.h +++ b/sound/soc/codecs/arizona.h @@ -176,6 +176,8 @@ extern const struct soc_enum arizona_lhpf2_mode; extern const struct soc_enum arizona_lhpf3_mode; extern const struct soc_enum arizona_lhpf4_mode; +extern const struct soc_enum arizona_ng_hold; + extern int arizona_in_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event); -- cgit v0.10.2 From 50571263722452a57a67edee23b892b2607dc374 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 18 Dec 2012 13:48:36 +0000 Subject: ASoC: wm5102: Add noise gate control The references used for the noise gates and parameters for their triggering are configurable, expose that to users. Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm5102.c b/sound/soc/codecs/wm5102.c index a16239f..e64b968 100644 --- a/sound/soc/codecs/wm5102.c +++ b/sound/soc/codecs/wm5102.c @@ -42,6 +42,7 @@ static DECLARE_TLV_DB_SCALE(ana_tlv, 0, 100, 0); static DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0); static DECLARE_TLV_DB_SCALE(digital_tlv, -6400, 50, 0); static DECLARE_TLV_DB_SCALE(noise_tlv, 0, 600, 0); +static DECLARE_TLV_DB_SCALE(ng_tlv, -10200, 600, 0); static const struct wm_adsp_region wm5102_dsp1_regions[] = { { .type = WMFW_ADSP2_PM, .base = 0x100000 }, @@ -600,6 +601,17 @@ static int wm5102_sysclk_ev(struct snd_soc_dapm_widget *w, return 0; } +#define WM5102_NG_SRC(name, base) \ + SOC_SINGLE(name " NG HPOUT1L Switch", base, 0, 1, 0), \ + SOC_SINGLE(name " NG HPOUT1R Switch", base, 1, 1, 0), \ + SOC_SINGLE(name " NG HPOUT2L Switch", base, 2, 1, 0), \ + SOC_SINGLE(name " NG HPOUT2R Switch", base, 3, 1, 0), \ + SOC_SINGLE(name " NG EPOUT Switch", base, 4, 1, 0), \ + SOC_SINGLE(name " NG SPKOUTL Switch", base, 6, 1, 0), \ + SOC_SINGLE(name " NG SPKOUTR Switch", base, 7, 1, 0), \ + SOC_SINGLE(name " NG SPKDAT1L Switch", base, 8, 1, 0), \ + SOC_SINGLE(name " NG SPKDAT1R Switch", base, 9, 1, 0) + static const struct snd_kcontrol_new wm5102_snd_controls[] = { SOC_SINGLE("IN1 High Performance Switch", ARIZONA_IN1L_CONTROL, ARIZONA_IN1_OSR_SHIFT, 1, 0), @@ -783,6 +795,22 @@ SOC_ENUM("Output Ramp Down", arizona_out_vd_ramp), SOC_DOUBLE("SPKDAT1 Switch", ARIZONA_PDM_SPK1_CTRL_1, ARIZONA_SPK1L_MUTE_SHIFT, ARIZONA_SPK1R_MUTE_SHIFT, 1, 1), +SOC_SINGLE("Noise Gate Switch", ARIZONA_NOISE_GATE_CONTROL, + ARIZONA_NGATE_ENA_SHIFT, 1, 0), +SOC_SINGLE_TLV("Noise Gate Threshold Volume", ARIZONA_NOISE_GATE_CONTROL, + ARIZONA_NGATE_THR_SHIFT, 7, 1, ng_tlv), +SOC_ENUM("Noise Gate Hold", arizona_ng_hold), + +WM5102_NG_SRC("HPOUT1L", ARIZONA_NOISE_GATE_SELECT_1L), +WM5102_NG_SRC("HPOUT1R", ARIZONA_NOISE_GATE_SELECT_1R), +WM5102_NG_SRC("HPOUT2L", ARIZONA_NOISE_GATE_SELECT_2L), +WM5102_NG_SRC("HPOUT2R", ARIZONA_NOISE_GATE_SELECT_2R), +WM5102_NG_SRC("EPOUT", ARIZONA_NOISE_GATE_SELECT_3L), +WM5102_NG_SRC("SPKOUTL", ARIZONA_NOISE_GATE_SELECT_4L), +WM5102_NG_SRC("SPKOUTR", ARIZONA_NOISE_GATE_SELECT_4R), +WM5102_NG_SRC("SPKDAT1L", ARIZONA_NOISE_GATE_SELECT_5L), +WM5102_NG_SRC("SPKDAT1R", ARIZONA_NOISE_GATE_SELECT_5R), + ARIZONA_MIXER_CONTROLS("AIF1TX1", ARIZONA_AIF1TX1MIX_INPUT_1_SOURCE), ARIZONA_MIXER_CONTROLS("AIF1TX2", ARIZONA_AIF1TX2MIX_INPUT_1_SOURCE), ARIZONA_MIXER_CONTROLS("AIF1TX3", ARIZONA_AIF1TX3MIX_INPUT_1_SOURCE), -- cgit v0.10.2 From bd7fe24bc43bc75255cebb7aed5ed675579531d3 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 18 Dec 2012 13:53:26 +0000 Subject: ASoC: wm5110: Add noise gate control The references used for the noise gates and parameters for their triggering are configurable, expose that to users. Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm5110.c b/sound/soc/codecs/wm5110.c index 9cdac35..8f7081e 100644 --- a/sound/soc/codecs/wm5110.c +++ b/sound/soc/codecs/wm5110.c @@ -41,6 +41,21 @@ static DECLARE_TLV_DB_SCALE(ana_tlv, 0, 100, 0); static DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0); static DECLARE_TLV_DB_SCALE(digital_tlv, -6400, 50, 0); static DECLARE_TLV_DB_SCALE(noise_tlv, 0, 600, 0); +static DECLARE_TLV_DB_SCALE(ng_tlv, -10200, 600, 0); + +#define WM5110_NG_SRC(name, base) \ + SOC_SINGLE(name " NG HPOUT1L Switch", base, 0, 1, 0), \ + SOC_SINGLE(name " NG HPOUT1R Switch", base, 1, 1, 0), \ + SOC_SINGLE(name " NG HPOUT2L Switch", base, 2, 1, 0), \ + SOC_SINGLE(name " NG HPOUT2R Switch", base, 3, 1, 0), \ + SOC_SINGLE(name " NG HPOUT3L Switch", base, 4, 1, 0), \ + SOC_SINGLE(name " NG HPOUT3R Switch", base, 5, 1, 0), \ + SOC_SINGLE(name " NG SPKOUTL Switch", base, 6, 1, 0), \ + SOC_SINGLE(name " NG SPKOUTR Switch", base, 7, 1, 0), \ + SOC_SINGLE(name " NG SPKDAT1L Switch", base, 8, 1, 0), \ + SOC_SINGLE(name " NG SPKDAT1R Switch", base, 9, 1, 0), \ + SOC_SINGLE(name " NG SPKDAT2L Switch", base, 10, 1, 0), \ + SOC_SINGLE(name " NG SPKDAT2R Switch", base, 11, 1, 0) static const struct snd_kcontrol_new wm5110_snd_controls[] = { SOC_SINGLE("IN1 High Performance Switch", ARIZONA_IN1L_CONTROL, @@ -278,6 +293,25 @@ SOC_DOUBLE("SPKDAT2 Switch", ARIZONA_PDM_SPK2_CTRL_1, ARIZONA_SPK2L_MUTE_SHIFT, SOC_ENUM("Output Ramp Up", arizona_out_vi_ramp), SOC_ENUM("Output Ramp Down", arizona_out_vd_ramp), +SOC_SINGLE("Noise Gate Switch", ARIZONA_NOISE_GATE_CONTROL, + ARIZONA_NGATE_ENA_SHIFT, 1, 0), +SOC_SINGLE_TLV("Noise Gate Threshold Volume", ARIZONA_NOISE_GATE_CONTROL, + ARIZONA_NGATE_THR_SHIFT, 7, 1, ng_tlv), +SOC_ENUM("Noise Gate Hold", arizona_ng_hold), + +WM5110_NG_SRC("HPOUT1L", ARIZONA_NOISE_GATE_SELECT_1L), +WM5110_NG_SRC("HPOUT1R", ARIZONA_NOISE_GATE_SELECT_1R), +WM5110_NG_SRC("HPOUT2L", ARIZONA_NOISE_GATE_SELECT_2L), +WM5110_NG_SRC("HPOUT2R", ARIZONA_NOISE_GATE_SELECT_2R), +WM5110_NG_SRC("HPOUT3L", ARIZONA_NOISE_GATE_SELECT_3L), +WM5110_NG_SRC("HPOUT3R", ARIZONA_NOISE_GATE_SELECT_3R), +WM5110_NG_SRC("SPKOUTL", ARIZONA_NOISE_GATE_SELECT_4L), +WM5110_NG_SRC("SPKOUTR", ARIZONA_NOISE_GATE_SELECT_4R), +WM5110_NG_SRC("SPKDAT1L", ARIZONA_NOISE_GATE_SELECT_5L), +WM5110_NG_SRC("SPKDAT1R", ARIZONA_NOISE_GATE_SELECT_5R), +WM5110_NG_SRC("SPKDAT2L", ARIZONA_NOISE_GATE_SELECT_6L), +WM5110_NG_SRC("SPKDAT2R", ARIZONA_NOISE_GATE_SELECT_6R), + ARIZONA_MIXER_CONTROLS("AIF1TX1", ARIZONA_AIF1TX1MIX_INPUT_1_SOURCE), ARIZONA_MIXER_CONTROLS("AIF1TX2", ARIZONA_AIF1TX2MIX_INPUT_1_SOURCE), ARIZONA_MIXER_CONTROLS("AIF1TX3", ARIZONA_AIF1TX3MIX_INPUT_1_SOURCE), -- cgit v0.10.2 From 01df259f592147db97293b90d03e5fd8075cbeb3 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 12 Dec 2012 16:22:08 +0900 Subject: ASoC: arizona: Implement tristate support Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c index 819920d..e55fa03 100644 --- a/sound/soc/codecs/arizona.c +++ b/sound/soc/codecs/arizona.c @@ -803,11 +803,27 @@ static int arizona_dai_set_sysclk(struct snd_soc_dai *dai, return snd_soc_dapm_sync(&codec->dapm); } +static int arizona_set_tristate(struct snd_soc_dai *dai, int tristate) +{ + struct snd_soc_codec *codec = dai->codec; + int base = dai->driver->base; + unsigned int reg; + + if (tristate) + reg = ARIZONA_AIF1_TRI; + else + reg = 0; + + return snd_soc_update_bits(codec, base + ARIZONA_AIF_RATE_CTRL, + ARIZONA_AIF1_TRI, reg); +} + const struct snd_soc_dai_ops arizona_dai_ops = { .startup = arizona_startup, .set_fmt = arizona_set_fmt, .hw_params = arizona_hw_params, .set_sysclk = arizona_dai_set_sysclk, + .set_tristate = arizona_set_tristate, }; EXPORT_SYMBOL_GPL(arizona_dai_ops); -- cgit v0.10.2 From 514cfd6dd72508b79030c8504764a73a7261b713 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 13 Dec 2012 17:29:00 +0900 Subject: ASoC: wm2000: Integrate with clock API Request MCLK as a clock and then enable it when carrying out a state transtion and while ANC is active, minimising system power consumption in idle modes. Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm2000.c b/sound/soc/codecs/wm2000.c index 1cbe88f..0aba8ce 100644 --- a/sound/soc/codecs/wm2000.c +++ b/sound/soc/codecs/wm2000.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -62,6 +63,7 @@ enum wm2000_anc_mode { struct wm2000_priv { struct i2c_client *i2c; struct regmap *regmap; + struct clk *mclk; struct regulator_bulk_data supplies[WM2000_NUM_SUPPLIES]; @@ -550,6 +552,15 @@ static int wm2000_anc_transition(struct wm2000_priv *wm2000, return -EINVAL; } + /* Maintain clock while active */ + if (anc_transitions[i].source == ANC_OFF) { + ret = clk_prepare_enable(wm2000->mclk); + if (ret != 0) { + dev_err(&i2c->dev, "Failed to enable MCLK: %d\n", ret); + return ret; + } + } + for (j = 0; j < ARRAY_SIZE(anc_transitions[j].step); j++) { if (!anc_transitions[i].step[j]) break; @@ -559,7 +570,10 @@ static int wm2000_anc_transition(struct wm2000_priv *wm2000, return ret; } - return 0; + if (anc_transitions[i].dest == ANC_OFF) + clk_disable_unprepare(wm2000->mclk); + + return ret; } static int wm2000_anc_set_mode(struct wm2000_priv *wm2000) @@ -823,6 +837,13 @@ static int wm2000_i2c_probe(struct i2c_client *i2c, reg = wm2000_read(i2c, WM2000_REG_REVISON); dev_info(&i2c->dev, "revision %c\n", reg + 'A'); + wm2000->mclk = devm_clk_get(&i2c->dev, "MCLK"); + if (IS_ERR(wm2000->mclk)) { + ret = PTR_ERR(wm2000->mclk); + dev_err(&i2c->dev, "Failed to get MCLK: %d\n", ret); + goto err_supplies; + } + filename = "wm2000_anc.bin"; pdata = dev_get_platdata(&i2c->dev); if (pdata) { -- cgit v0.10.2 From d61100bbd18e8b3fc9406be55354dabd5e7525ec Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 14 Dec 2012 15:16:58 +0900 Subject: ASoC: wm2000: Use clock API integration to configure MCLK divisor Since we are now using the clock API integration to manage MCLK we can now use clk_get_rate() to determine if we need to divide MCLK without relying on platform data. Signed-off-by: Mark Brown diff --git a/include/sound/wm2000.h b/include/sound/wm2000.h index aa388ca..4de81f4 100644 --- a/include/sound/wm2000.h +++ b/include/sound/wm2000.h @@ -15,9 +15,6 @@ struct wm2000_platform_data { /** Filename for system-specific image to download to device. */ const char *download_file; - /** Divide MCLK by 2 for system clock? */ - unsigned int mclkdiv2:1; - /** Disable speech clarity enhancement, for use when an * external algorithm is used. */ unsigned int speech_enh_disable:1; diff --git a/sound/soc/codecs/wm2000.c b/sound/soc/codecs/wm2000.c index 0aba8ce..85550dc 100644 --- a/sound/soc/codecs/wm2000.c +++ b/sound/soc/codecs/wm2000.c @@ -73,7 +73,6 @@ struct wm2000_priv { unsigned int anc_eng_ena:1; unsigned int spk_ena:1; - unsigned int mclk_div:1; unsigned int speech_clarity:1; int anc_download_size; @@ -133,6 +132,7 @@ static int wm2000_poll_bit(struct i2c_client *i2c, static int wm2000_power_up(struct i2c_client *i2c, int analogue) { struct wm2000_priv *wm2000 = dev_get_drvdata(&i2c->dev); + unsigned long rate; int ret; BUG_ON(wm2000->anc_mode != ANC_OFF); @@ -145,7 +145,8 @@ static int wm2000_power_up(struct i2c_client *i2c, int analogue) return ret; } - if (!wm2000->mclk_div) { + rate = clk_get_rate(wm2000->mclk); + if (rate <= 13500000) { dev_dbg(&i2c->dev, "Disabling MCLK divider\n"); wm2000_write(i2c, WM2000_REG_SYS_CTL2, WM2000_MCLK_DIV2_ENA_CLR); @@ -847,7 +848,6 @@ static int wm2000_i2c_probe(struct i2c_client *i2c, filename = "wm2000_anc.bin"; pdata = dev_get_platdata(&i2c->dev); if (pdata) { - wm2000->mclk_div = pdata->mclkdiv2; wm2000->speech_clarity = !pdata->speech_enh_disable; if (pdata->download_file) -- cgit v0.10.2 From f4319ff2bd23a5183f25ec0b3f5f753060ae2e4d Mon Sep 17 00:00:00 2001 From: Joachim Eastwood Date: Sat, 8 Dec 2012 13:46:41 +0100 Subject: ASoC: atmel-ssc: make it buildable on other architectures Not very useful on non AT91/AVR32 platforms but it provides more build coverage and prepares for ARM multiplatform. Also fixes a truncated warning that would come when building on a 64-bit arch. Signed-off-by: Joachim Eastwood Acked-by: Nicolas Ferre Signed-off-by: Mark Brown diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index b151b7c..8f59d88 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -192,7 +192,7 @@ config ICS932S401 config ATMEL_SSC tristate "Device driver for Atmel SSC peripheral" - depends on AVR32 || ARCH_AT91 + depends on HAS_IOMEM ---help--- This option enables device driver support for Atmel Synchronized Serial Communication peripheral (SSC). diff --git a/drivers/misc/atmel-ssc.c b/drivers/misc/atmel-ssc.c index 158da5a..0cee274 100644 --- a/drivers/misc/atmel-ssc.c +++ b/drivers/misc/atmel-ssc.c @@ -167,7 +167,7 @@ static int ssc_probe(struct platform_device *pdev) /* disable all interrupts */ clk_enable(ssc->clk); - ssc_writel(ssc->regs, IDR, ~0UL); + ssc_writel(ssc->regs, IDR, -1); ssc_readl(ssc->regs, SR); clk_disable(ssc->clk); -- cgit v0.10.2 From 153f5a18e4a88d8a3c2d1a19658a3ce34559b473 Mon Sep 17 00:00:00 2001 From: Joachim Eastwood Date: Sat, 8 Dec 2012 14:23:22 +0100 Subject: ASoC: atmel-soc: make it buildable on other architectures Not very useful on non AT91/AVR32 platforms but it provides more build coverage and prepares for ARM multiplatform. Also fixes a couple of format type warnings. Signed-off-by: Joachim Eastwood Acked-by: Nicolas Ferre Signed-off-by: Mark Brown diff --git a/sound/soc/atmel/Kconfig b/sound/soc/atmel/Kconfig index d1b691b..3fdd87f 100644 --- a/sound/soc/atmel/Kconfig +++ b/sound/soc/atmel/Kconfig @@ -1,6 +1,6 @@ config SND_ATMEL_SOC tristate "SoC Audio for the Atmel System-on-Chip" - depends on ARCH_AT91 + depends on HAS_IOMEM help Say Y or M if you want to add support for codecs attached to the ATMEL SSC interface. You will also need @@ -24,7 +24,7 @@ config SND_ATMEL_SOC_SSC config SND_AT91_SOC_SAM9G20_WM8731 tristate "SoC Audio support for WM8731-based At91sam9g20 evaluation board" - depends on ATMEL_SSC && SND_ATMEL_SOC && AT91_PROGRAMMABLE_CLOCKS + depends on ARCH_AT91 && ATMEL_SSC && SND_ATMEL_SOC && AT91_PROGRAMMABLE_CLOCKS select SND_ATMEL_SOC_PDC select SND_ATMEL_SOC_SSC select SND_SOC_WM8731 @@ -34,7 +34,7 @@ config SND_AT91_SOC_SAM9G20_WM8731 config SND_AT91_SOC_AFEB9260 tristate "SoC Audio support for AFEB9260 board" - depends on ATMEL_SSC && ARCH_AT91 && MACH_AFEB9260 && SND_ATMEL_SOC + depends on ARCH_AT91 && ATMEL_SSC && ARCH_AT91 && MACH_AFEB9260 && SND_ATMEL_SOC select SND_ATMEL_SOC_PDC select SND_ATMEL_SOC_SSC select SND_SOC_TLV320AIC23 diff --git a/sound/soc/atmel/atmel-pcm-pdc.c b/sound/soc/atmel/atmel-pcm-pdc.c index 6a293c7..054ea4d 100644 --- a/sound/soc/atmel/atmel-pcm-pdc.c +++ b/sound/soc/atmel/atmel-pcm-pdc.c @@ -159,7 +159,7 @@ static int atmel_pcm_hw_params(struct snd_pcm_substream *substream, pr_debug("atmel-pcm: " "hw_params: DMA for %s initialized " - "(dma_bytes=%u, period_size=%u)\n", + "(dma_bytes=%zu, period_size=%zu)\n", prtd->params->name, runtime->dma_bytes, prtd->period_size); @@ -201,7 +201,7 @@ static int atmel_pcm_trigger(struct snd_pcm_substream *substream, int ret = 0; pr_debug("atmel-pcm:buffer_size = %ld," - "dma_area = %p, dma_bytes = %u\n", + "dma_area = %p, dma_bytes = %zu\n", rtd->buffer_size, rtd->dma_area, rtd->dma_bytes); switch (cmd) { diff --git a/sound/soc/atmel/atmel-pcm.c b/sound/soc/atmel/atmel-pcm.c index e99f181..3109db7 100644 --- a/sound/soc/atmel/atmel-pcm.c +++ b/sound/soc/atmel/atmel-pcm.c @@ -49,7 +49,7 @@ static int atmel_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, buf->private_data = NULL; buf->area = dma_alloc_coherent(pcm->card->dev, size, &buf->addr, GFP_KERNEL); - pr_debug("atmel-pcm: alloc dma buffer: area=%p, addr=%p, size=%d\n", + pr_debug("atmel-pcm: alloc dma buffer: area=%p, addr=%p, size=%zu\n", (void *)buf->area, (void *)buf->addr, size); if (!buf->area) diff --git a/sound/soc/atmel/atmel_ssc_dai.c b/sound/soc/atmel/atmel_ssc_dai.c index 1c76634..2755750 100644 --- a/sound/soc/atmel/atmel_ssc_dai.c +++ b/sound/soc/atmel/atmel_ssc_dai.c @@ -42,8 +42,6 @@ #include #include -#include - #include "atmel-pcm.h" #include "atmel_ssc_dai.h" -- cgit v0.10.2 From fd23fb9f6bfd43a6e62b2646d18d5ca3edc3ebe3 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Mon, 10 Dec 2012 10:30:04 +0100 Subject: ALSA: ASoC: cs4271: add optional soft reset workaround The CS4271 requires its LRCLK and MCLK to be stable before its RESET line is de-asserted. That also means that clocks cannot be changed without putting the chip back into hardware reset, which also requires a complete re-initialization of all registers. One (undocumented) workaround is to assert and de-assert the PDN bit in the MODE2 register. This patch adds a new flag to both the DT bindings as well as to the platform data to enable that workaround. Signed-off-by: Daniel Mack Acked-by: Alexander Sverdlin Signed-off-by: Mark Brown diff --git a/Documentation/devicetree/bindings/sound/cs4271.txt b/Documentation/devicetree/bindings/sound/cs4271.txt index a850fb9..e2cd1d7 100644 --- a/Documentation/devicetree/bindings/sound/cs4271.txt +++ b/Documentation/devicetree/bindings/sound/cs4271.txt @@ -20,6 +20,18 @@ Optional properties: !RESET pin - cirrus,amuteb-eq-bmutec: When given, the Codec's AMUTEB=BMUTEC flag is enabled. + - cirrus,enable-soft-reset: + The CS4271 requires its LRCLK and MCLK to be stable before its RESET + line is de-asserted. That also means that clocks cannot be changed + without putting the chip back into hardware reset, which also requires + a complete re-initialization of all registers. + + One (undocumented) workaround is to assert and de-assert the PDN bit + in the MODE2 register. This workaround can be enabled with this DT + property. + + Note that this is not needed in case the clocks are stable + throughout the entire runtime of the codec. Examples: diff --git a/include/sound/cs4271.h b/include/sound/cs4271.h index dd8c48d..70f4535 100644 --- a/include/sound/cs4271.h +++ b/include/sound/cs4271.h @@ -20,6 +20,21 @@ struct cs4271_platform_data { int gpio_nreset; /* GPIO driving Reset pin, if any */ bool amutec_eq_bmutec; /* flag to enable AMUTEC=BMUTEC */ + + /* + * The CS4271 requires its LRCLK and MCLK to be stable before its RESET + * line is de-asserted. That also means that clocks cannot be changed + * without putting the chip back into hardware reset, which also requires + * a complete re-initialization of all registers. + * + * One (undocumented) workaround is to assert and de-assert the PDN bit + * in the MODE2 register. This workaround can be enabled with the + * following flag. + * + * Note that this is not needed in case the clocks are stable + * throughout the entire runtime of the codec. + */ + bool enable_soft_reset; }; #endif /* __CS4271_H */ diff --git a/sound/soc/codecs/cs4271.c b/sound/soc/codecs/cs4271.c index ac8742a..2415a41 100644 --- a/sound/soc/codecs/cs4271.c +++ b/sound/soc/codecs/cs4271.c @@ -167,6 +167,8 @@ struct cs4271_private { int gpio_nreset; /* GPIO that disable serial bus, if any */ int gpio_disable; + /* enable soft reset workaround */ + bool enable_soft_reset; }; /* @@ -325,6 +327,33 @@ static int cs4271_hw_params(struct snd_pcm_substream *substream, int i, ret; unsigned int ratio, val; + if (cs4271->enable_soft_reset) { + /* + * Put the codec in soft reset and back again in case it's not + * currently streaming data. This way of bringing the codec in + * sync to the current clocks is not explicitly documented in + * the data sheet, but it seems to work fine, and in contrast + * to a read hardware reset, we don't have to sync back all + * registers every time. + */ + + if ((substream->stream == SNDRV_PCM_STREAM_PLAYBACK && + !dai->capture_active) || + (substream->stream == SNDRV_PCM_STREAM_CAPTURE && + !dai->playback_active)) { + ret = snd_soc_update_bits(codec, CS4271_MODE2, + CS4271_MODE2_PDN, + CS4271_MODE2_PDN); + if (ret < 0) + return ret; + + ret = snd_soc_update_bits(codec, CS4271_MODE2, + CS4271_MODE2_PDN, 0); + if (ret < 0) + return ret; + } + } + cs4271->rate = params_rate(params); /* Configure DAC */ @@ -484,6 +513,10 @@ static int cs4271_probe(struct snd_soc_codec *codec) if (of_get_property(codec->dev->of_node, "cirrus,amutec-eq-bmutec", NULL)) amutec_eq_bmutec = true; + + if (of_get_property(codec->dev->of_node, + "cirrus,enable-soft-reset", NULL)) + cs4271->enable_soft_reset = true; } #endif @@ -492,6 +525,7 @@ static int cs4271_probe(struct snd_soc_codec *codec) gpio_nreset = cs4271plat->gpio_nreset; amutec_eq_bmutec = cs4271plat->amutec_eq_bmutec; + cs4271->enable_soft_reset = cs4271plat->enable_soft_reset; } if (gpio_nreset >= 0) -- cgit v0.10.2 From 1edbd35667c5a7f7cf9140b2009027ac9546e8fd Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Wed, 12 Dec 2012 00:53:13 -0200 Subject: ASoC: wm8804: Remove redundant check The condition "if (!freq_in || !freq_out)" has already been tested previously, so no need to do it again. Signed-off-by: Fabio Estevam Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm8804.c b/sound/soc/codecs/wm8804.c index d321a87..1704b1e 100644 --- a/sound/soc/codecs/wm8804.c +++ b/sound/soc/codecs/wm8804.c @@ -395,9 +395,6 @@ static int wm8804_set_pll(struct snd_soc_dai *dai, int pll_id, /* power down the PLL before reprogramming it */ snd_soc_update_bits(codec, WM8804_PWRDN, 0x1, 0x1); - if (!freq_in || !freq_out) - return 0; - /* set PLLN and PRESCALE */ snd_soc_update_bits(codec, WM8804_PLL4, 0xf | 0x10, pll_div.n | (pll_div.prescale << 4)); -- cgit v0.10.2 From 6757d8cc0c6ff2e61972d84797d358a2f69f2217 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Wed, 12 Dec 2012 01:24:40 -0200 Subject: ASoC: wm8993: Refactor set_pll code to avoid GCC warnings Refactor set_pll code to avoid the following warnings: sound/soc/codecs/wm8983.c:873:40: warning: 'pll_div.k' may be used uninitialized in this function [-Wuninitialized] sound/soc/codecs/wm8983.c:870:9: warning: 'pll_div.n' may be used uninitialized in this function [-Wuninitialized] sound/soc/codecs/wm8983.c:869:23: warning: 'pll_div.div2' may be used uninitialized in this function [-Wuninitialized] Do the same as in commit 86ce6c9a (ASoC: WM8804: Refactor set_pll code to avoid GCC warnings). Signed-off-by: Fabio Estevam Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm8983.c b/sound/soc/codecs/wm8983.c index 9fe1e04..c9c707b 100644 --- a/sound/soc/codecs/wm8983.c +++ b/sound/soc/codecs/wm8983.c @@ -851,30 +851,33 @@ static int wm8983_set_pll(struct snd_soc_dai *dai, int pll_id, struct pll_div pll_div; codec = dai->codec; - if (freq_in && freq_out) { + if (!freq_in || !freq_out) { + /* disable the PLL */ + snd_soc_update_bits(codec, WM8983_POWER_MANAGEMENT_1, + WM8983_PLLEN_MASK, 0); + return 0; + } else { ret = pll_factors(&pll_div, freq_out * 4 * 2, freq_in); if (ret) return ret; - } - - /* disable the PLL before re-programming it */ - snd_soc_update_bits(codec, WM8983_POWER_MANAGEMENT_1, - WM8983_PLLEN_MASK, 0); - if (!freq_in || !freq_out) - return 0; + /* disable the PLL before re-programming it */ + snd_soc_update_bits(codec, WM8983_POWER_MANAGEMENT_1, + WM8983_PLLEN_MASK, 0); + + /* set PLLN and PRESCALE */ + snd_soc_write(codec, WM8983_PLL_N, + (pll_div.div2 << WM8983_PLL_PRESCALE_SHIFT) + | pll_div.n); + /* set PLLK */ + snd_soc_write(codec, WM8983_PLL_K_3, pll_div.k & 0x1ff); + snd_soc_write(codec, WM8983_PLL_K_2, (pll_div.k >> 9) & 0x1ff); + snd_soc_write(codec, WM8983_PLL_K_1, (pll_div.k >> 18)); + /* enable the PLL */ + snd_soc_update_bits(codec, WM8983_POWER_MANAGEMENT_1, + WM8983_PLLEN_MASK, WM8983_PLLEN); + } - /* set PLLN and PRESCALE */ - snd_soc_write(codec, WM8983_PLL_N, - (pll_div.div2 << WM8983_PLL_PRESCALE_SHIFT) - | pll_div.n); - /* set PLLK */ - snd_soc_write(codec, WM8983_PLL_K_3, pll_div.k & 0x1ff); - snd_soc_write(codec, WM8983_PLL_K_2, (pll_div.k >> 9) & 0x1ff); - snd_soc_write(codec, WM8983_PLL_K_1, (pll_div.k >> 18)); - /* enable the PLL */ - snd_soc_update_bits(codec, WM8983_POWER_MANAGEMENT_1, - WM8983_PLLEN_MASK, WM8983_PLLEN); return 0; } -- cgit v0.10.2 From 6cbdbffba19620db77de38094f407b6f21d3f10c Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Sun, 16 Dec 2012 22:12:21 -0800 Subject: ASoC: fsi: remove platform depended .set_rate() callback support ab6f6d85210c4d0265cf48e9958c04e08595055a (ASoC: fsi: add master clock control functions) added driver level clock control functions. And now, platform depended .set_rate() is no longer needed. This patch removed unnecessary .set_rate() platform callback support. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown diff --git a/include/sound/sh_fsi.h b/include/sound/sh_fsi.h index cc1c919..66285e1 100644 --- a/include/sound/sh_fsi.h +++ b/include/sound/sh_fsi.h @@ -50,43 +50,10 @@ #define SH_FSI_CLK_EXTERNAL (0 << 12) #define SH_FSI_CLK_CPG (1 << 12) /* FSIxCK + FSI-DIV */ -/* - * set_rate return value - * - * see ACKMD/BPFMD on - * ACK_MD (FSI2) - * CKG1 (FSI) - * - * err : return value < 0 - * no change : return value == 0 - * change xMD : return value > 0 - * - * 0x-00000AB - * - * A: ACKMD value - * B: BPFMD value - */ - -#define SH_FSI_ACKMD_MASK (0xF << 0) -#define SH_FSI_ACKMD_512 (1 << 0) -#define SH_FSI_ACKMD_256 (2 << 0) -#define SH_FSI_ACKMD_128 (3 << 0) -#define SH_FSI_ACKMD_64 (4 << 0) -#define SH_FSI_ACKMD_32 (5 << 0) - -#define SH_FSI_BPFMD_MASK (0xF << 4) -#define SH_FSI_BPFMD_512 (1 << 4) -#define SH_FSI_BPFMD_256 (2 << 4) -#define SH_FSI_BPFMD_128 (3 << 4) -#define SH_FSI_BPFMD_64 (4 << 4) -#define SH_FSI_BPFMD_32 (5 << 4) -#define SH_FSI_BPFMD_16 (6 << 4) - struct sh_fsi_port_info { unsigned long flags; int tx_id; int rx_id; - int (*set_rate)(struct device *dev, int rate, int enable); }; struct sh_fsi_platform_info { diff --git a/sound/soc/sh/fsi.c b/sound/soc/sh/fsi.c index a606d0f..5cb1332 100644 --- a/sound/soc/sh/fsi.c +++ b/sound/soc/sh/fsi.c @@ -131,8 +131,6 @@ #define FSI_FMTS (SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE) -typedef int (*set_rate_func)(struct device *dev, int rate, int enable); - /* * bus options * @@ -244,8 +242,7 @@ struct fsi_clk { struct clk *ick; struct clk *div; int (*set_rate)(struct device *dev, - struct fsi_priv *fsi, - unsigned long rate); + struct fsi_priv *fsi); unsigned long rate; unsigned int count; @@ -270,8 +267,6 @@ struct fsi_priv { int enable_stream:1; int bit_clk_inv:1; int lr_clk_inv:1; - - long rate; }; struct fsi_stream_handler { @@ -431,14 +426,6 @@ static struct fsi_priv *fsi_get_priv(struct snd_pcm_substream *substream) return fsi_get_priv_frm_dai(fsi_get_dai(substream)); } -static set_rate_func fsi_get_info_set_rate(struct fsi_priv *fsi) -{ - if (!fsi->info) - return NULL; - - return fsi->info->set_rate; -} - static u32 fsi_get_info_flags(struct fsi_priv *fsi) { if (!fsi->info) @@ -757,8 +744,7 @@ static int fsi_clk_init(struct device *dev, int ick, int div, int (*set_rate)(struct device *dev, - struct fsi_priv *fsi, - unsigned long rate)) + struct fsi_priv *fsi)) { struct fsi_clk *clock = &fsi->clock; int is_porta = fsi_is_port_a(fsi); @@ -829,8 +815,7 @@ static int fsi_clk_is_valid(struct fsi_priv *fsi) } static int fsi_clk_enable(struct device *dev, - struct fsi_priv *fsi, - unsigned long rate) + struct fsi_priv *fsi) { struct fsi_clk *clock = &fsi->clock; int ret = -EINVAL; @@ -839,7 +824,7 @@ static int fsi_clk_enable(struct device *dev, return ret; if (0 == clock->count) { - ret = clock->set_rate(dev, fsi, rate); + ret = clock->set_rate(dev, fsi); if (ret < 0) { fsi_clk_invalid(fsi); return ret; @@ -946,11 +931,11 @@ static int fsi_clk_set_ackbpf(struct device *dev, } static int fsi_clk_set_rate_external(struct device *dev, - struct fsi_priv *fsi, - unsigned long rate) + struct fsi_priv *fsi) { struct clk *xck = fsi->clock.xck; struct clk *ick = fsi->clock.ick; + unsigned long rate = fsi->clock.rate; unsigned long xrate; int ackmd, bpfmd; int ret = 0; @@ -978,11 +963,11 @@ static int fsi_clk_set_rate_external(struct device *dev, } static int fsi_clk_set_rate_cpg(struct device *dev, - struct fsi_priv *fsi, - unsigned long rate) + struct fsi_priv *fsi) { struct clk *ick = fsi->clock.ick; struct clk *div = fsi->clock.div; + unsigned long rate = fsi->clock.rate; unsigned long target = 0; /* 12288000 or 11289600 */ unsigned long actual, cout; unsigned long diff, min; @@ -1063,85 +1048,6 @@ static int fsi_clk_set_rate_cpg(struct device *dev, return ret; } -static int fsi_set_master_clk(struct device *dev, struct fsi_priv *fsi, - long rate, int enable) -{ - set_rate_func set_rate = fsi_get_info_set_rate(fsi); - int ret; - - /* - * CAUTION - * - * set_rate will be deleted - */ - if (!set_rate) { - if (enable) - return fsi_clk_enable(dev, fsi, rate); - else - return fsi_clk_disable(dev, fsi); - } - - ret = set_rate(dev, rate, enable); - if (ret < 0) /* error */ - return ret; - - if (!enable) - return 0; - - if (ret > 0) { - u32 data = 0; - - switch (ret & SH_FSI_ACKMD_MASK) { - default: - /* FALL THROUGH */ - case SH_FSI_ACKMD_512: - data |= (0x0 << 12); - break; - case SH_FSI_ACKMD_256: - data |= (0x1 << 12); - break; - case SH_FSI_ACKMD_128: - data |= (0x2 << 12); - break; - case SH_FSI_ACKMD_64: - data |= (0x3 << 12); - break; - case SH_FSI_ACKMD_32: - data |= (0x4 << 12); - break; - } - - switch (ret & SH_FSI_BPFMD_MASK) { - default: - /* FALL THROUGH */ - case SH_FSI_BPFMD_32: - data |= (0x0 << 8); - break; - case SH_FSI_BPFMD_64: - data |= (0x1 << 8); - break; - case SH_FSI_BPFMD_128: - data |= (0x2 << 8); - break; - case SH_FSI_BPFMD_256: - data |= (0x3 << 8); - break; - case SH_FSI_BPFMD_512: - data |= (0x4 << 8); - break; - case SH_FSI_BPFMD_16: - data |= (0x7 << 8); - break; - } - - fsi_reg_mask_set(fsi, CKG1, (ACKMD_MASK | BPFMD_MASK) , data); - udelay(10); - ret = 0; - } - - return ret; -} - /* * pio data transfer handler */ @@ -1698,7 +1604,7 @@ static int fsi_hw_startup(struct fsi_priv *fsi, /* start master clock */ if (fsi_is_clk_master(fsi)) - return fsi_set_master_clk(dev, fsi, fsi->rate, 1); + return fsi_clk_enable(dev, fsi); return 0; } @@ -1708,7 +1614,7 @@ static int fsi_hw_shutdown(struct fsi_priv *fsi, { /* stop master clock */ if (fsi_is_clk_master(fsi)) - return fsi_set_master_clk(dev, fsi, fsi->rate, 0); + return fsi_clk_disable(dev, fsi); return 0; } @@ -1719,7 +1625,6 @@ static int fsi_dai_startup(struct snd_pcm_substream *substream, struct fsi_priv *fsi = fsi_get_priv(substream); fsi_clk_invalid(fsi); - fsi->rate = 0; return 0; } @@ -1730,7 +1635,6 @@ static void fsi_dai_shutdown(struct snd_pcm_substream *substream, struct fsi_priv *fsi = fsi_get_priv(substream); fsi_clk_invalid(fsi); - fsi->rate = 0; } static int fsi_dai_trigger(struct snd_pcm_substream *substream, int cmd, @@ -1795,7 +1699,6 @@ static int fsi_set_fmt_spdif(struct fsi_priv *fsi) static int fsi_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) { struct fsi_priv *fsi = fsi_get_priv_frm_dai(dai); - set_rate_func set_rate = fsi_get_info_set_rate(fsi); int ret; /* set master/slave audio interface */ @@ -1831,14 +1734,6 @@ static int fsi_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) } if (fsi_is_clk_master(fsi)) { - /* - * CAUTION - * - * set_rate will be deleted - */ - if (set_rate) - dev_warn(dai->dev, "set_rate will be removed soon\n"); - if (fsi->clk_cpg) fsi_clk_init(dai->dev, fsi, 0, 1, 1, fsi_clk_set_rate_cpg); @@ -1862,10 +1757,8 @@ static int fsi_dai_hw_params(struct snd_pcm_substream *substream, { struct fsi_priv *fsi = fsi_get_priv(substream); - if (fsi_is_clk_master(fsi)) { - fsi->rate = params_rate(params); - fsi_clk_valid(fsi, fsi->rate); - } + if (fsi_is_clk_master(fsi)) + fsi_clk_valid(fsi, params_rate(params)); return 0; } -- cgit v0.10.2 From abca75814a82c0c53c0a8ec7fa1300c133bc4f01 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Sun, 16 Dec 2012 22:12:42 -0800 Subject: ASoC: fsi: remove SH_FSI_xxx_INV flags 3449f5fab8c51e37a8a48bc2516588c615373191 (ASoC: fsi: add SND_SOC_DAIFMT_INV_xxx support) added clock inversion support via snd_soc_dai_set_fmt(). Thus, this patch removed SH_FSI_xxx_INV and fsi_get_info() from fsi driver, and modified platform settings to use new style. Then, it cleaned up meaningless settings from platform. Signed-off-by: Kuninori Morimoto Acked-by: Simon Horman Signed-off-by: Mark Brown diff --git a/arch/arm/mach-shmobile/board-ap4evb.c b/arch/arm/mach-shmobile/board-ap4evb.c index 99ef190..4c97903 100644 --- a/arch/arm/mach-shmobile/board-ap4evb.c +++ b/arch/arm/mach-shmobile/board-ap4evb.c @@ -657,14 +657,8 @@ static struct platform_device lcdc_device = { /* FSI */ #define IRQ_FSI evt2irq(0x1840) static struct sh_fsi_platform_info fsi_info = { - .port_a = { - .flags = SH_FSI_BRS_INV, - }, .port_b = { - .flags = SH_FSI_BRS_INV | - SH_FSI_BRM_INV | - SH_FSI_LRS_INV | - SH_FSI_CLK_CPG | + .flags = SH_FSI_CLK_CPG | SH_FSI_FMT_SPDIF, }, }; @@ -816,7 +810,8 @@ static struct platform_device lcdc1_device = { }; static struct asoc_simple_dai_init_info fsi2_hdmi_init_info = { - .cpu_daifmt = SND_SOC_DAIFMT_CBM_CFM, + .cpu_daifmt = SND_SOC_DAIFMT_CBM_CFM | + SND_SOC_DAIFMT_IB_NF, }; static struct asoc_simple_card_info fsi2_hdmi_info = { diff --git a/arch/arm/mach-shmobile/board-mackerel.c b/arch/arm/mach-shmobile/board-mackerel.c index 2fed62f..b5d210b 100644 --- a/arch/arm/mach-shmobile/board-mackerel.c +++ b/arch/arm/mach-shmobile/board-mackerel.c @@ -503,7 +503,8 @@ static struct platform_device hdmi_lcdc_device = { }; static struct asoc_simple_dai_init_info fsi2_hdmi_init_info = { - .cpu_daifmt = SND_SOC_DAIFMT_CBM_CFM, + .cpu_daifmt = SND_SOC_DAIFMT_CBM_CFM | + SND_SOC_DAIFMT_IB_NF, }; static struct asoc_simple_card_info fsi2_hdmi_info = { @@ -858,16 +859,12 @@ static struct platform_device leds_device = { #define IRQ_FSI evt2irq(0x1840) static struct sh_fsi_platform_info fsi_info = { .port_a = { - .flags = SH_FSI_BRS_INV, .tx_id = SHDMA_SLAVE_FSIA_TX, .rx_id = SHDMA_SLAVE_FSIA_RX, }, .port_b = { - .flags = SH_FSI_BRS_INV | - SH_FSI_BRM_INV | - SH_FSI_LRS_INV | - SH_FSI_CLK_CPG | - SH_FSI_FMT_SPDIF, + .flags = SH_FSI_CLK_CPG | + SH_FSI_FMT_SPDIF, } }; diff --git a/arch/sh/boards/mach-ecovec24/setup.c b/arch/sh/boards/mach-ecovec24/setup.c index 3fede45..8ebe4c7 100644 --- a/arch/sh/boards/mach-ecovec24/setup.c +++ b/arch/sh/boards/mach-ecovec24/setup.c @@ -877,12 +877,6 @@ static struct platform_device camera_devices[] = { }; /* FSI */ -static struct sh_fsi_platform_info fsi_info = { - .port_b = { - .flags = SH_FSI_BRS_INV, - }, -}; - static struct resource fsi_resources[] = { [0] = { .name = "FSI", @@ -901,15 +895,13 @@ static struct platform_device fsi_device = { .id = 0, .num_resources = ARRAY_SIZE(fsi_resources), .resource = fsi_resources, - .dev = { - .platform_data = &fsi_info, - }, }; static struct asoc_simple_dai_init_info fsi_da7210_init_info = { .fmt = SND_SOC_DAIFMT_I2S, .codec_daifmt = SND_SOC_DAIFMT_CBM_CFM, - .cpu_daifmt = SND_SOC_DAIFMT_CBS_CFS, + .cpu_daifmt = SND_SOC_DAIFMT_CBS_CFS | + SND_SOC_DAIFMT_IB_NF, }; static struct asoc_simple_card_info fsi_da7210_info = { diff --git a/arch/sh/boards/mach-se/7724/setup.c b/arch/sh/boards/mach-se/7724/setup.c index 35f6efa..975608f 100644 --- a/arch/sh/boards/mach-se/7724/setup.c +++ b/arch/sh/boards/mach-se/7724/setup.c @@ -279,12 +279,6 @@ static struct platform_device ceu1_device = { /* FSI */ /* change J20, J21, J22 pin to 1-2 connection to use slave mode */ -static struct sh_fsi_platform_info fsi_info = { - .port_a = { - .flags = SH_FSI_BRS_INV, - }, -}; - static struct resource fsi_resources[] = { [0] = { .name = "FSI", @@ -303,15 +297,13 @@ static struct platform_device fsi_device = { .id = 0, .num_resources = ARRAY_SIZE(fsi_resources), .resource = fsi_resources, - .dev = { - .platform_data = &fsi_info, - }, }; static struct asoc_simple_dai_init_info fsi2_ak4642_init_info = { .fmt = SND_SOC_DAIFMT_LEFT_J, .codec_daifmt = SND_SOC_DAIFMT_CBM_CFM, - .cpu_daifmt = SND_SOC_DAIFMT_CBS_CFS, + .cpu_daifmt = SND_SOC_DAIFMT_CBS_CFS | + SND_SOC_DAIFMT_IB_NF, .sysclk = 11289600, }; diff --git a/include/sound/sh_fsi.h b/include/sound/sh_fsi.h index 66285e1..43ac285 100644 --- a/include/sound/sh_fsi.h +++ b/include/sound/sh_fsi.h @@ -29,13 +29,6 @@ * D: clock selecter if master mode */ -/* A: clock inversion */ -#define SH_FSI_INVERSION_MASK 0x0000000F -#define SH_FSI_LRM_INV (1 << 0) -#define SH_FSI_BRM_INV (1 << 1) -#define SH_FSI_LRS_INV (1 << 2) -#define SH_FSI_BRS_INV (1 << 3) - /* B: format mode */ #define SH_FSI_FMT_MASK 0x000000F0 #define SH_FSI_FMT_DAI (0 << 4) diff --git a/sound/soc/sh/fsi.c b/sound/soc/sh/fsi.c index 5cb1332..f14c611 100644 --- a/sound/soc/sh/fsi.c +++ b/sound/soc/sh/fsi.c @@ -251,7 +251,6 @@ struct fsi_clk { struct fsi_priv { void __iomem *base; struct fsi_master *master; - struct sh_fsi_port_info *info; struct fsi_stream playback; struct fsi_stream capture; @@ -426,14 +425,6 @@ static struct fsi_priv *fsi_get_priv(struct snd_pcm_substream *substream) return fsi_get_priv_frm_dai(fsi_get_dai(substream)); } -static u32 fsi_get_info_flags(struct fsi_priv *fsi) -{ - if (!fsi->info) - return 0; - - return fsi->info->flags; -} - static u32 fsi_get_port_shift(struct fsi_priv *fsi, struct fsi_stream *io) { int is_play = fsi_stream_is_play(fsi, io); @@ -1543,7 +1534,6 @@ static int fsi_hw_startup(struct fsi_priv *fsi, struct fsi_stream *io, struct device *dev) { - u32 flags = fsi_get_info_flags(fsi); u32 data = 0; /* clock setting */ @@ -1560,19 +1550,6 @@ static int fsi_hw_startup(struct fsi_priv *fsi, data |= (1 << 4); if (fsi_is_clk_master(fsi)) data <<= 8; - /* FIXME - * - * SH_FSI_xxx_INV style will be removed - */ - if (SH_FSI_LRM_INV & flags) - data |= 1 << 12; - if (SH_FSI_BRM_INV & flags) - data |= 1 << 8; - if (SH_FSI_LRS_INV & flags) - data |= 1 << 4; - if (SH_FSI_BRS_INV & flags) - data |= 1 << 0; - fsi_reg_write(fsi, CKG2, data); /* spdif ? */ @@ -1988,7 +1965,6 @@ static int fsi_probe(struct platform_device *pdev) fsi = &master->fsia; fsi->base = master->base; fsi->master = master; - fsi->info = pinfo; fsi_port_info_init(fsi, pinfo); fsi_handler_init(fsi, pinfo); ret = fsi_stream_probe(fsi, &pdev->dev); @@ -2002,7 +1978,6 @@ static int fsi_probe(struct platform_device *pdev) fsi = &master->fsib; fsi->base = master->base + 0x40; fsi->master = master; - fsi->info = pinfo; fsi_port_info_init(fsi, pinfo); fsi_handler_init(fsi, pinfo); ret = fsi_stream_probe(fsi, &pdev->dev); -- cgit v0.10.2 From 5d0bfc5eb9f57b319d7cd6a1d5543c8287c77812 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Sun, 16 Dec 2012 22:12:55 -0800 Subject: ASoC: fsi: cleanup sh_fsi.h FSI driver's flag usage was changed/removed by 3449f5fab8c51e37a8a48bc2516588c615373191 (ASoC: fsi: add SND_SOC_DAIFMT_INV_xxx support) ab6f6d85210c4d0265cf48e9958c04e08595055a (ASoC: fsi: add master clock control functions) And unused flags had been removed on FSI driver, but the definition had been kept to avoid compile error. It is possible to cleanup sh_fsi.h now. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown diff --git a/include/sound/sh_fsi.h b/include/sound/sh_fsi.h index 43ac285..7a9710b 100644 --- a/include/sound/sh_fsi.h +++ b/include/sound/sh_fsi.h @@ -11,37 +11,15 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ - -#define FSI_PORT_A 0 -#define FSI_PORT_B 1 - #include #include /* - * flags format - * - * 0x00000CBA - * - * A: inversion - * B: format mode - * C: chip specific - * D: clock selecter if master mode + * flags */ - -/* B: format mode */ -#define SH_FSI_FMT_MASK 0x000000F0 -#define SH_FSI_FMT_DAI (0 << 4) -#define SH_FSI_FMT_SPDIF (1 << 4) - -/* C: chip specific */ -#define SH_FSI_OPTION_MASK 0x00000F00 -#define SH_FSI_ENABLE_STREAM_MODE (1 << 8) /* for 16bit data */ - -/* D: clock selecter if master mode */ -#define SH_FSI_CLK_MASK 0x0000F000 -#define SH_FSI_CLK_EXTERNAL (0 << 12) -#define SH_FSI_CLK_CPG (1 << 12) /* FSIxCK + FSI-DIV */ +#define SH_FSI_FMT_SPDIF (1 << 0) /* spdif for HDMI */ +#define SH_FSI_ENABLE_STREAM_MODE (1 << 1) /* for 16bit data */ +#define SH_FSI_CLK_CPG (1 << 2) /* FSIxCK + FSI-DIV */ struct sh_fsi_port_info { unsigned long flags; -- cgit v0.10.2 From 919ad49c214adcc80578c7a02efd3fe8460e0797 Mon Sep 17 00:00:00 2001 From: Lucas Stach Date: Thu, 20 Dec 2012 00:17:33 +0100 Subject: ASoC: tegra: add function to set ac97 rate AC97 uses a fixed rate, unrelated to the sample rate. Add a function to make the setup more trivial. Signed-off-by: Lucas Stach Acked-by: Stephen Warren Signed-off-by: Mark Brown diff --git a/sound/soc/tegra/tegra_asoc_utils.c b/sound/soc/tegra/tegra_asoc_utils.c index 6872c77..ba419f8 100644 --- a/sound/soc/tegra/tegra_asoc_utils.c +++ b/sound/soc/tegra/tegra_asoc_utils.c @@ -112,6 +112,59 @@ int tegra_asoc_utils_set_rate(struct tegra_asoc_utils_data *data, int srate, } EXPORT_SYMBOL_GPL(tegra_asoc_utils_set_rate); +int tegra_asoc_utils_set_ac97_rate(struct tegra_asoc_utils_data *data) +{ + const int pll_rate = 73728000; + const int ac97_rate = 24576000; + int err; + + clk_disable_unprepare(data->clk_cdev1); + clk_disable_unprepare(data->clk_pll_a_out0); + clk_disable_unprepare(data->clk_pll_a); + + /* + * AC97 rate is fixed at 24.576MHz and is used for both the host + * controller and the external codec + */ + err = clk_set_rate(data->clk_pll_a, pll_rate); + if (err) { + dev_err(data->dev, "Can't set pll_a rate: %d\n", err); + return err; + } + + err = clk_set_rate(data->clk_pll_a_out0, ac97_rate); + if (err) { + dev_err(data->dev, "Can't set pll_a_out0 rate: %d\n", err); + return err; + } + + /* Don't set cdev1/extern1 rate; it's locked to pll_a_out0 */ + + err = clk_prepare_enable(data->clk_pll_a); + if (err) { + dev_err(data->dev, "Can't enable pll_a: %d\n", err); + return err; + } + + err = clk_prepare_enable(data->clk_pll_a_out0); + if (err) { + dev_err(data->dev, "Can't enable pll_a_out0: %d\n", err); + return err; + } + + err = clk_prepare_enable(data->clk_cdev1); + if (err) { + dev_err(data->dev, "Can't enable cdev1: %d\n", err); + return err; + } + + data->set_baseclock = pll_rate; + data->set_mclk = ac97_rate; + + return 0; +} +EXPORT_SYMBOL_GPL(tegra_asoc_utils_set_ac97_rate); + int tegra_asoc_utils_init(struct tegra_asoc_utils_data *data, struct device *dev) { diff --git a/sound/soc/tegra/tegra_asoc_utils.h b/sound/soc/tegra/tegra_asoc_utils.h index 44db1db..974c9f8 100644 --- a/sound/soc/tegra/tegra_asoc_utils.h +++ b/sound/soc/tegra/tegra_asoc_utils.h @@ -43,6 +43,7 @@ struct tegra_asoc_utils_data { int tegra_asoc_utils_set_rate(struct tegra_asoc_utils_data *data, int srate, int mclk); +int tegra_asoc_utils_set_ac97_rate(struct tegra_asoc_utils_data *data); int tegra_asoc_utils_init(struct tegra_asoc_utils_data *data, struct device *dev); void tegra_asoc_utils_fini(struct tegra_asoc_utils_data *data); -- cgit v0.10.2 From 15fab585070ebdd6b31880b3a9a848389d302dd2 Mon Sep 17 00:00:00 2001 From: Lucas Stach Date: Thu, 20 Dec 2012 00:17:32 +0100 Subject: ASoC: tegra: setup DAP3<->DAC3 connection by default This connection is used by the AC97 controller. Signed-off-by: Lucas Stach Acked-by: Stephen Warren Signed-off-by: Mark Brown diff --git a/sound/soc/tegra/tegra20_das.c b/sound/soc/tegra/tegra20_das.c index 6543184..e723929 100644 --- a/sound/soc/tegra/tegra20_das.c +++ b/sound/soc/tegra/tegra20_das.c @@ -191,6 +191,19 @@ static int tegra20_das_probe(struct platform_device *pdev) goto err; } + ret = tegra20_das_connect_dap_to_dac(TEGRA20_DAS_DAP_ID_3, + TEGRA20_DAS_DAP_SEL_DAC3); + if (ret) { + dev_err(&pdev->dev, "Can't set up DAS DAP connection\n"); + goto err; + } + ret = tegra20_das_connect_dac_to_dap(TEGRA20_DAS_DAC_ID_3, + TEGRA20_DAS_DAC_SEL_DAP3); + if (ret) { + dev_err(&pdev->dev, "Can't set up DAS DAC connection\n"); + goto err; + } + platform_set_drvdata(pdev, das); return 0; -- cgit v0.10.2 From 8a47ca957a6bf86f81893edd9401b91c00be4848 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Fri, 21 Dec 2012 17:57:03 -0800 Subject: ASoC: wm8350: don't use [delayed_]work_pending() There's no need to test whether a (delayed) work item in pending before queueing, flushing or cancelling it. Most uses are unnecessary and quite a few of them are buggy. Remove unnecessary pending tests from wm8350. Only compile tested. Signed-off-by: Tejun Heo Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm8350.c b/sound/soc/codecs/wm8350.c index fb92fb4..ec0efc1 100644 --- a/sound/soc/codecs/wm8350.c +++ b/sound/soc/codecs/wm8350.c @@ -283,18 +283,16 @@ static int pga_event(struct snd_soc_dapm_widget *w, out->ramp = WM8350_RAMP_UP; out->active = 1; - if (!delayed_work_pending(&codec->dapm.delayed_work)) - schedule_delayed_work(&codec->dapm.delayed_work, - msecs_to_jiffies(1)); + schedule_delayed_work(&codec->dapm.delayed_work, + msecs_to_jiffies(1)); break; case SND_SOC_DAPM_PRE_PMD: out->ramp = WM8350_RAMP_DOWN; out->active = 0; - if (!delayed_work_pending(&codec->dapm.delayed_work)) - schedule_delayed_work(&codec->dapm.delayed_work, - msecs_to_jiffies(1)); + schedule_delayed_work(&codec->dapm.delayed_work, + msecs_to_jiffies(1)); break; } -- cgit v0.10.2 From e958f8b806c3953fcadce3929400638ae0dc796f Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Tue, 25 Dec 2012 13:28:26 +0800 Subject: ASoC: cs42l52: Convert to devm_input_allocate_device() Signed-off-by: Axel Lin Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/cs42l52.c b/sound/soc/codecs/cs42l52.c index 99bb1c6..73a8049 100644 --- a/sound/soc/codecs/cs42l52.c +++ b/sound/soc/codecs/cs42l52.c @@ -1040,7 +1040,7 @@ static void cs42l52_init_beep(struct snd_soc_codec *codec) struct cs42l52_private *cs42l52 = snd_soc_codec_get_drvdata(codec); int ret; - cs42l52->beep = input_allocate_device(); + cs42l52->beep = devm_input_allocate_device(codec->dev); if (!cs42l52->beep) { dev_err(codec->dev, "Failed to allocate beep device\n"); return; @@ -1061,7 +1061,6 @@ static void cs42l52_init_beep(struct snd_soc_codec *codec) ret = input_register_device(cs42l52->beep); if (ret != 0) { - input_free_device(cs42l52->beep); cs42l52->beep = NULL; dev_err(codec->dev, "Failed to register beep device\n"); } @@ -1078,7 +1077,6 @@ static void cs42l52_free_beep(struct snd_soc_codec *codec) struct cs42l52_private *cs42l52 = snd_soc_codec_get_drvdata(codec); device_remove_file(codec->dev, &dev_attr_beep); - input_unregister_device(cs42l52->beep); cancel_work_sync(&cs42l52->beep_work); cs42l52->beep = NULL; -- cgit v0.10.2 From 5f3d25c08dee44a40229f1f9e8934f3217478a67 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Mon, 24 Dec 2012 15:55:37 -0200 Subject: ASoC: wm8985: Refactor set_pll code to avoid gcc warnings Refactor set_pll code to avoid the following warnings: sound/soc/codecs/wm8985.c:852:50: warning: 'pll_div.k' may be used uninitialized in this function sound/soc/codecs/wm8985.c:849:9: warning: 'pll_div.n' may be used uninitialized in this function sound/soc/codecs/wm8985.c:848:23: warning: 'pll_div.div2' may be used uninitialized in this function Do the same as in commit 86ce6c9a (ASoC: WM8804: Refactor set_pll code to avoid GCC warnings). Signed-off-by: Fabio Estevam Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm8985.c b/sound/soc/codecs/wm8985.c index ab37826..dd6ce3b 100644 --- a/sound/soc/codecs/wm8985.c +++ b/sound/soc/codecs/wm8985.c @@ -830,33 +830,30 @@ static int wm8985_set_pll(struct snd_soc_dai *dai, int pll_id, struct pll_div pll_div; codec = dai->codec; - if (freq_in && freq_out) { + if (!freq_in || !freq_out) { + /* disable the PLL */ + snd_soc_update_bits(codec, WM8985_POWER_MANAGEMENT_1, + WM8985_PLLEN_MASK, 0); + } else { ret = pll_factors(&pll_div, freq_out * 4 * 2, freq_in); if (ret) return ret; - } - /* disable the PLL before reprogramming it */ - snd_soc_update_bits(codec, WM8985_POWER_MANAGEMENT_1, - WM8985_PLLEN_MASK, 0); - - if (!freq_in || !freq_out) - return 0; - - /* set PLLN and PRESCALE */ - snd_soc_write(codec, WM8985_PLL_N, - (pll_div.div2 << WM8985_PLL_PRESCALE_SHIFT) - | pll_div.n); - /* set PLLK */ - snd_soc_write(codec, WM8985_PLL_K_3, pll_div.k & 0x1ff); - snd_soc_write(codec, WM8985_PLL_K_2, (pll_div.k >> 9) & 0x1ff); - snd_soc_write(codec, WM8985_PLL_K_1, (pll_div.k >> 18)); - /* set the source of the clock to be the PLL */ - snd_soc_update_bits(codec, WM8985_CLOCK_GEN_CONTROL, - WM8985_CLKSEL_MASK, WM8985_CLKSEL); - /* enable the PLL */ - snd_soc_update_bits(codec, WM8985_POWER_MANAGEMENT_1, - WM8985_PLLEN_MASK, WM8985_PLLEN); + /* set PLLN and PRESCALE */ + snd_soc_write(codec, WM8985_PLL_N, + (pll_div.div2 << WM8985_PLL_PRESCALE_SHIFT) + | pll_div.n); + /* set PLLK */ + snd_soc_write(codec, WM8985_PLL_K_3, pll_div.k & 0x1ff); + snd_soc_write(codec, WM8985_PLL_K_2, (pll_div.k >> 9) & 0x1ff); + snd_soc_write(codec, WM8985_PLL_K_1, (pll_div.k >> 18)); + /* set the source of the clock to be the PLL */ + snd_soc_update_bits(codec, WM8985_CLOCK_GEN_CONTROL, + WM8985_CLKSEL_MASK, WM8985_CLKSEL); + /* enable the PLL */ + snd_soc_update_bits(codec, WM8985_POWER_MANAGEMENT_1, + WM8985_PLLEN_MASK, WM8985_PLLEN); + } return 0; } -- cgit v0.10.2 From f89983ef61677afc828ed32b3c42ef5a71eb0686 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 25 Dec 2012 22:52:33 -0800 Subject: ASoC: simple-card: use struct device pointer for dev_xxx() Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c index b4b4cab..bc050ec 100644 --- a/sound/soc/generic/simple-card.c +++ b/sound/soc/generic/simple-card.c @@ -50,9 +50,10 @@ static int asoc_simple_card_dai_init(struct snd_soc_pcm_runtime *rtd) static int asoc_simple_card_probe(struct platform_device *pdev) { struct asoc_simple_card_info *cinfo = pdev->dev.platform_data; + struct device *dev = &pdev->dev; if (!cinfo) { - dev_err(&pdev->dev, "no info for asoc-simple-card\n"); + dev_err(dev, "no info for asoc-simple-card\n"); return -EINVAL; } @@ -62,7 +63,7 @@ static int asoc_simple_card_probe(struct platform_device *pdev) !cinfo->codec || !cinfo->platform || !cinfo->codec_dai) { - dev_err(&pdev->dev, "insufficient asoc_simple_card_info settings\n"); + dev_err(dev, "insufficient asoc_simple_card_info settings\n"); return -EINVAL; } -- cgit v0.10.2 From 927a77476ed37080793f0e9d3211359f2d480d4d Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 31 Dec 2012 11:51:42 +0100 Subject: ASoC: twl4030: Correct the support for Voice port In order to be able to use the Voice port of twl4030 three bits need to be handled in VOICE_IF register: VIF_EN: to enable the voice port (needed for both playback and capture) VIF_DIN_EN: Need to be enabled for playback only (input to the codec) VIF_DOUT_EN: Need to be enabled for capture only (output from codec) Use DAPM_SUPPLY for the VIF_EN bit and add DAPM_AIF_IO/OUT widget to handle the playback/capture bit. Signed-off-by: Peter Ujfalusi Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c index 63b280b..79b2f86 100644 --- a/sound/soc/codecs/twl4030.c +++ b/sound/soc/codecs/twl4030.c @@ -1306,6 +1306,9 @@ static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = { SND_SOC_DAPM_DAC("DAC Left2", NULL, SND_SOC_NOPM, 0, 0), SND_SOC_DAPM_DAC("DAC Voice", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("VAIFIN", "Voice Playback", 0, + TWL4030_REG_VOICE_IF, 6, 0), + /* Analog bypasses */ SND_SOC_DAPM_SWITCH("Right1 Analog Loopback", SND_SOC_NOPM, 0, 0, &twl4030_dapm_abypassr1_control), @@ -1438,6 +1441,9 @@ static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = { SND_SOC_DAPM_ADC("ADC Virtual Left2", NULL, SND_SOC_NOPM, 0, 0), SND_SOC_DAPM_ADC("ADC Virtual Right2", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("VAIFOUT", "Voice Capture", 0, + TWL4030_REG_VOICE_IF, 5, 0), + /* Analog/Digital mic path selection. TX1 Left/Right: either analog Left/Right or Digimic0 TX2 Left/Right: either analog Left/Right or Digimic1 */ @@ -1477,6 +1483,7 @@ static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = { SND_SOC_DAPM_MICBIAS("Mic Bias 2", TWL4030_REG_MICBIAS_CTL, 1, 0), SND_SOC_DAPM_MICBIAS("Headset Mic Bias", TWL4030_REG_MICBIAS_CTL, 2, 0), + SND_SOC_DAPM_SUPPLY("VIF Enable", TWL4030_REG_VOICE_IF, 0, 0, NULL, 0), }; static const struct snd_soc_dapm_route intercon[] = { @@ -1485,17 +1492,16 @@ static const struct snd_soc_dapm_route intercon[] = { {"DAC Left1", NULL, "HiFi Playback"}, {"DAC Right2", NULL, "HiFi Playback"}, {"DAC Left2", NULL, "HiFi Playback"}, - {"DAC Voice", NULL, "Voice Playback"}, + {"DAC Voice", NULL, "VAIFIN"}, /* ADC -> Stream mapping */ {"HiFi Capture", NULL, "ADC Virtual Left1"}, {"HiFi Capture", NULL, "ADC Virtual Right1"}, {"HiFi Capture", NULL, "ADC Virtual Left2"}, {"HiFi Capture", NULL, "ADC Virtual Right2"}, - {"Voice Capture", NULL, "ADC Virtual Left1"}, - {"Voice Capture", NULL, "ADC Virtual Right1"}, - {"Voice Capture", NULL, "ADC Virtual Left2"}, - {"Voice Capture", NULL, "ADC Virtual Right2"}, + {"VAIFOUT", NULL, "ADC Virtual Left2"}, + {"VAIFOUT", NULL, "ADC Virtual Right2"}, + {"VAIFOUT", NULL, "VIF Enable"}, {"Digital L1 Playback Mixer", NULL, "DAC Left1"}, {"Digital R1 Playback Mixer", NULL, "DAC Right1"}, @@ -1510,6 +1516,7 @@ static const struct snd_soc_dapm_route intercon[] = { {"DAC Right1", NULL, "AIF Enable"}, {"DAC Left2", NULL, "AIF Enable"}, {"DAC Right1", NULL, "AIF Enable"}, + {"DAC Voice", NULL, "VIF Enable"}, {"Digital R2 Playback Mixer", NULL, "AIF Enable"}, {"Digital L2 Playback Mixer", NULL, "AIF Enable"}, -- cgit v0.10.2 From 01df26edaf4d15e1af3300f8a52b11d3f06c5096 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 31 Dec 2012 11:51:43 +0100 Subject: ASoC: zoom2: No need to configure the Voice port anymore The codec driver takes care of these bits. Signed-off-by: Peter Ujfalusi Signed-off-by: Mark Brown diff --git a/sound/soc/omap/zoom2.c b/sound/soc/omap/zoom2.c index 771bff2..5845d48 100644 --- a/sound/soc/omap/zoom2.c +++ b/sound/soc/omap/zoom2.c @@ -110,19 +110,6 @@ static int zoom2_twl4030_init(struct snd_soc_pcm_runtime *rtd) return 0; } -static int zoom2_twl4030_voice_init(struct snd_soc_pcm_runtime *rtd) -{ - struct snd_soc_codec *codec = rtd->codec; - unsigned short reg; - - /* Enable voice interface */ - reg = codec->driver->read(codec, TWL4030_REG_VOICE_IF); - reg |= TWL4030_VIF_DIN_EN | TWL4030_VIF_DOUT_EN | TWL4030_VIF_EN; - codec->driver->write(codec, TWL4030_REG_VOICE_IF, reg); - - return 0; -} - /* Digital audio interface glue - connects codec <--> CPU */ static struct snd_soc_dai_link zoom2_dai[] = { { @@ -146,7 +133,6 @@ static struct snd_soc_dai_link zoom2_dai[] = { .codec_name = "twl4030-codec", .dai_fmt = SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_IB_NF | SND_SOC_DAIFMT_CBM_CFM, - .init = zoom2_twl4030_voice_init, .ops = &zoom2_ops, }, }; -- cgit v0.10.2 From 57296cc28cf8aab2e5e63d8f9964718ae29cc229 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 31 Dec 2012 11:51:44 +0100 Subject: ASoC: sdp3430: No need to configure the Voice port anymore The codec driver takes care of these bits. Signed-off-by: Peter Ujfalusi Signed-off-by: Mark Brown diff --git a/sound/soc/omap/sdp3430.c b/sound/soc/omap/sdp3430.c index b462a2c..86e77e9 100644 --- a/sound/soc/omap/sdp3430.c +++ b/sound/soc/omap/sdp3430.c @@ -167,20 +167,6 @@ static int sdp3430_twl4030_init(struct snd_soc_pcm_runtime *rtd) return ret; } -static int sdp3430_twl4030_voice_init(struct snd_soc_pcm_runtime *rtd) -{ - struct snd_soc_codec *codec = rtd->codec; - unsigned short reg; - - /* Enable voice interface */ - reg = codec->driver->read(codec, TWL4030_REG_VOICE_IF); - reg |= TWL4030_VIF_DIN_EN | TWL4030_VIF_DOUT_EN | TWL4030_VIF_EN; - codec->driver->write(codec, TWL4030_REG_VOICE_IF, reg); - - return 0; -} - - /* Digital audio interface glue - connects codec <--> CPU */ static struct snd_soc_dai_link sdp3430_dai[] = { { @@ -204,7 +190,6 @@ static struct snd_soc_dai_link sdp3430_dai[] = { .codec_name = "twl4030-codec", .dai_fmt = SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_IB_NF | SND_SOC_DAIFMT_CBM_CFM, - .init = sdp3430_twl4030_voice_init, .ops = &sdp3430_ops, }, }; -- cgit v0.10.2 From e04d6e55fe02351b1ac338d9e8082fcc434610ce Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 31 Dec 2012 11:51:45 +0100 Subject: ASoC: twl4030: Convert MICBIAS to SUPPLY widget In order to avoid breakage update the machine drivers at the same time using twl4030: omap3pandora, sdp3430 and zoom2 Signed-off-by: Peter Ujfalusi Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c index 79b2f86..5dd7c81 100644 --- a/sound/soc/codecs/twl4030.c +++ b/sound/soc/codecs/twl4030.c @@ -1479,9 +1479,13 @@ static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = { SND_SOC_DAPM_SUPPLY("micbias2 select", TWL4030_REG_MICBIAS_CTL, 6, 0, NULL, 0), - SND_SOC_DAPM_MICBIAS("Mic Bias 1", TWL4030_REG_MICBIAS_CTL, 0, 0), - SND_SOC_DAPM_MICBIAS("Mic Bias 2", TWL4030_REG_MICBIAS_CTL, 1, 0), - SND_SOC_DAPM_MICBIAS("Headset Mic Bias", TWL4030_REG_MICBIAS_CTL, 2, 0), + /* Microphone bias */ + SND_SOC_DAPM_SUPPLY("Mic Bias 1", + TWL4030_REG_MICBIAS_CTL, 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("Mic Bias 2", + TWL4030_REG_MICBIAS_CTL, 1, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("Headset Mic Bias", + TWL4030_REG_MICBIAS_CTL, 2, 0, NULL, 0), SND_SOC_DAPM_SUPPLY("VIF Enable", TWL4030_REG_VOICE_IF, 0, 0, NULL, 0), }; diff --git a/sound/soc/omap/omap3pandora.c b/sound/soc/omap/omap3pandora.c index 43d950a..805512f 100644 --- a/sound/soc/omap/omap3pandora.c +++ b/sound/soc/omap/omap3pandora.c @@ -144,11 +144,11 @@ static const struct snd_soc_dapm_route omap3pandora_in_map[] = { {"AUXL", NULL, "Line In"}, {"AUXR", NULL, "Line In"}, - {"MAINMIC", NULL, "Mic Bias 1"}, - {"Mic Bias 1", NULL, "Mic (internal)"}, + {"MAINMIC", NULL, "Mic (internal)"}, + {"Mic (internal)", NULL, "Mic Bias 1"}, - {"SUBMIC", NULL, "Mic Bias 2"}, - {"Mic Bias 2", NULL, "Mic (external)"}, + {"SUBMIC", NULL, "Mic (external)"}, + {"Mic (external)", NULL, "Mic Bias 2"}, }; static int omap3pandora_out_init(struct snd_soc_pcm_runtime *rtd) diff --git a/sound/soc/omap/sdp3430.c b/sound/soc/omap/sdp3430.c index 86e77e9..f2e2651 100644 --- a/sound/soc/omap/sdp3430.c +++ b/sound/soc/omap/sdp3430.c @@ -104,19 +104,19 @@ static const struct snd_soc_dapm_widget sdp3430_twl4030_dapm_widgets[] = { }; static const struct snd_soc_dapm_route audio_map[] = { - /* External Mics: MAINMIC, SUBMIC with bias*/ - {"MAINMIC", NULL, "Mic Bias 1"}, - {"SUBMIC", NULL, "Mic Bias 2"}, - {"Mic Bias 1", NULL, "Ext Mic"}, - {"Mic Bias 2", NULL, "Ext Mic"}, + /* External Mics: MAINMIC, SUBMIC with bias */ + {"MAINMIC", NULL, "Ext Mic"}, + {"SUBMIC", NULL, "Ext Mic"}, + {"Ext Mic", NULL, "Mic Bias 1"}, + {"Ext Mic", NULL, "Mic Bias 2"}, /* External Speakers: HFL, HFR */ {"Ext Spk", NULL, "HFL"}, {"Ext Spk", NULL, "HFR"}, /* Headset Mic: HSMIC with bias */ - {"HSMIC", NULL, "Headset Mic Bias"}, - {"Headset Mic Bias", NULL, "Headset Mic"}, + {"HSMIC", NULL, "Headset Mic"}, + {"Headset Mic", NULL, "Headset Mic Bias"}, /* Headset Stereophone (Headphone): HSOL, HSOR */ {"Headset Stereophone", NULL, "HSOL"}, diff --git a/sound/soc/omap/zoom2.c b/sound/soc/omap/zoom2.c index 5845d48..62a6b02 100644 --- a/sound/soc/omap/zoom2.c +++ b/sound/soc/omap/zoom2.c @@ -69,11 +69,11 @@ static const struct snd_soc_dapm_widget zoom2_twl4030_dapm_widgets[] = { }; static const struct snd_soc_dapm_route audio_map[] = { - /* External Mics: MAINMIC, SUBMIC with bias*/ - {"MAINMIC", NULL, "Mic Bias 1"}, - {"SUBMIC", NULL, "Mic Bias 2"}, - {"Mic Bias 1", NULL, "Ext Mic"}, - {"Mic Bias 2", NULL, "Ext Mic"}, + /* External Mics: MAINMIC, SUBMIC with bias */ + {"MAINMIC", NULL, "Ext Mic"}, + {"SUBMIC", NULL, "Ext Mic"}, + {"Ext Mic", NULL, "Mic Bias 1"}, + {"Ext Mic", NULL, "Mic Bias 2"}, /* External Speakers: HFL, HFR */ {"Ext Spk", NULL, "HFL"}, @@ -84,8 +84,8 @@ static const struct snd_soc_dapm_route audio_map[] = { {"Headset Stereophone", NULL, "HSOR"}, /* Headset Mic: HSMIC with bias */ - {"HSMIC", NULL, "Headset Mic Bias"}, - {"Headset Mic Bias", NULL, "Headset Mic"}, + {"HSMIC", NULL, "Headset Mic"}, + {"Headset Mic", NULL, "Headset Mic Bias"}, /* Aux In: AUXL, AUXR */ {"Aux In", NULL, "AUXL"}, -- cgit v0.10.2 From 5712ded9cf0b6981825af3c86e0059be93e5e17c Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 31 Dec 2012 11:51:46 +0100 Subject: ASoC: twl4030: Configure extmute pinmux when the dedicated pin is in use When HS extmute is enabled without custom GPIO we should configure the mux to allow the pin to be used as extmute signal. Signed-off-by: Peter Ujfalusi Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c index 5dd7c81..7bfabe5 100644 --- a/sound/soc/codecs/twl4030.c +++ b/sound/soc/codecs/twl4030.c @@ -41,6 +41,11 @@ /* Register descriptions are here */ #include +/* TWL4030 PMBR1 Register */ +#define TWL4030_PMBR1_REG 0x0D +/* TWL4030 PMBR1 Register GPIO6 mux bits */ +#define TWL4030_GPIO6_PWM0_MUTE(value) ((value & 0x03) << 2) + /* Shadow register used by the audio driver */ #define TWL4030_REG_SW_SHADOW 0x4A #define TWL4030_CACHEREGNUM (TWL4030_REG_SW_SHADOW + 1) @@ -348,19 +353,32 @@ static void twl4030_init_chip(struct snd_soc_codec *codec) pdata = twl4030_get_pdata(codec); - if (pdata && pdata->hs_extmute && - gpio_is_valid(pdata->hs_extmute_gpio)) { - int ret; - - if (!pdata->hs_extmute_gpio) - dev_warn(codec->dev, - "Extmute GPIO is 0 is this correct?\n"); - - ret = gpio_request_one(pdata->hs_extmute_gpio, - GPIOF_OUT_INIT_LOW, "hs_extmute"); - if (ret) { - dev_err(codec->dev, "Failed to get hs_extmute GPIO\n"); - pdata->hs_extmute_gpio = -1; + if (pdata && pdata->hs_extmute) { + if (gpio_is_valid(pdata->hs_extmute_gpio)) { + int ret; + + if (!pdata->hs_extmute_gpio) + dev_warn(codec->dev, + "Extmute GPIO is 0 is this correct?\n"); + + ret = gpio_request_one(pdata->hs_extmute_gpio, + GPIOF_OUT_INIT_LOW, + "hs_extmute"); + if (ret) { + dev_err(codec->dev, + "Failed to get hs_extmute GPIO\n"); + pdata->hs_extmute_gpio = -1; + } + } else { + u8 pin_mux; + + /* Set TWL4030 GPIO6 as EXTMUTE signal */ + twl_i2c_read_u8(TWL4030_MODULE_INTBR, &pin_mux, + TWL4030_PMBR1_REG); + pin_mux &= ~TWL4030_GPIO6_PWM0_MUTE(0x03); + pin_mux |= TWL4030_GPIO6_PWM0_MUTE(0x02); + twl_i2c_write_u8(TWL4030_MODULE_INTBR, pin_mux, + TWL4030_PMBR1_REG); } } -- cgit v0.10.2 From fff3dd40132d6106d4d2a61e70e782f82394fd17 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 31 Dec 2012 11:51:47 +0100 Subject: ASoC: sdp3430: No need to configure pin mux for extmute The codec driver takes care of this. Signed-off-by: Peter Ujfalusi Signed-off-by: Mark Brown diff --git a/sound/soc/omap/sdp3430.c b/sound/soc/omap/sdp3430.c index f2e2651..216cbdd 100644 --- a/sound/soc/omap/sdp3430.c +++ b/sound/soc/omap/sdp3430.c @@ -24,7 +24,6 @@ #include #include -#include #include #include #include @@ -32,20 +31,12 @@ #include #include -#include -/* Register descriptions for twl4030 codec part */ -#include #include #include "omap-mcbsp.h" #include "omap-pcm.h" -/* TWL4030 PMBR1 Register */ -#define TWL4030_INTBR_PMBR1 0x0D -/* TWL4030 PMBR1 Register GPIO6 mux bit */ -#define TWL4030_GPIO6_PWM0_MUTE(value) (value << 2) - static struct snd_soc_card snd_soc_sdp3430; static int sdp3430_hw_params(struct snd_pcm_substream *substream, @@ -212,7 +203,6 @@ static struct platform_device *sdp3430_snd_device; static int __init sdp3430_soc_init(void) { int ret; - u8 pin_mux; if (!machine_is_omap_3430sdp()) return -ENODEV; @@ -226,14 +216,6 @@ static int __init sdp3430_soc_init(void) platform_set_drvdata(sdp3430_snd_device, &snd_soc_sdp3430); - /* Set TWL4030 GPIO6 as EXTMUTE signal */ - twl_i2c_read_u8(TWL4030_MODULE_INTBR, &pin_mux, - TWL4030_INTBR_PMBR1); - pin_mux &= ~TWL4030_GPIO6_PWM0_MUTE(0x03); - pin_mux |= TWL4030_GPIO6_PWM0_MUTE(0x02); - twl_i2c_write_u8(TWL4030_MODULE_INTBR, pin_mux, - TWL4030_INTBR_PMBR1); - ret = platform_device_add(sdp3430_snd_device); if (ret) goto err1; -- cgit v0.10.2 From bd0b286e838ef1ca19bbe1cb55f0ec7e0135de1f Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 31 Dec 2012 11:51:48 +0100 Subject: ASoC: omap-twl4030: Add support for routing, voice port and jack detect Update the common machine driver to support more boards including Zoom2 and SDP3430. - Support for voice port of twl4030 - HS jack plug detection support - The audio routing can be fine tuned via pdata or via provided routing table from DT. Signed-off-by: Peter Ujfalusi Signed-off-by: Mark Brown diff --git a/Documentation/devicetree/bindings/sound/omap-twl4030.txt b/Documentation/devicetree/bindings/sound/omap-twl4030.txt index 6fae51c..1ab6bc8 100644 --- a/Documentation/devicetree/bindings/sound/omap-twl4030.txt +++ b/Documentation/devicetree/bindings/sound/omap-twl4030.txt @@ -6,6 +6,52 @@ Required properties: - ti,mcbsp: phandle for the McBSP node - ti,codec: phandle for the twl4030 audio node +Optional properties: +- ti,mcbsp-voice: phandle for the McBSP node connected to the voice port of twl +- ti, jack-det-gpio: Jack detect GPIO +- ti,audio-routing: List of connections between audio components. + Each entry is a pair of strings, the first being the connection's sink, + the second being the connection's source. + If the routing is not provided all possible connection will be available + +Available audio endpoints for the audio-routing table: + +Board connectors: + * Headset Stereophone + * Earpiece Spk + * Handsfree Spk + * Ext Spk + * Main Mic + * Sub Mic + * Headset Mic + * Carkit Mic + * Digital0 Mic + * Digital1 Mic + * Line In + +twl4030 pins: + * HSOL + * HSOR + * EARPIECE + * HFL + * HFR + * PREDRIVEL + * PREDRIVER + * CARKITL + * CARKITR + * MAINMIC + * SUBMIC + * HSMIC + * DIGIMIC0 + * DIGIMIC1 + * CARKITMIC + * AUXL + * AUXR + + * Headset Mic Bias + * Mic Bias 1 /* Used for Main Mic or Digimic0 */ + * Mic Bias 2 /* Used for Sub Mic or Digimic1 */ + Example: sound { diff --git a/sound/soc/omap/Kconfig b/sound/soc/omap/Kconfig index 7048137..e8d2a2f 100644 --- a/sound/soc/omap/Kconfig +++ b/sound/soc/omap/Kconfig @@ -91,6 +91,8 @@ config SND_OMAP_SOC_OMAP_TWL4030 - Gumstix Overo or CompuLab CM-T35/CM-T3730 - IGEP v2 - OMAP3EVM + - SDP3430 + - Zoom2 config SND_OMAP_SOC_OMAP_ABE_TWL6040 tristate "SoC Audio support for OMAP boards using ABE and twl6040 codec" diff --git a/sound/soc/omap/omap-twl4030.c b/sound/soc/omap/omap-twl4030.c index 4541d28..fd98509 100644 --- a/sound/soc/omap/omap-twl4030.c +++ b/sound/soc/omap/omap-twl4030.c @@ -11,6 +11,8 @@ * omap3evm (Author: Anuj Aggarwal ) * overo (Author: Steve Sakoman ) * igep0020 (Author: Enric Balletbo i Serra ) + * zoom2 (Author: Misael Lopez Cruz ) + * sdp3430 (Author: Misael Lopez Cruz ) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -32,14 +34,22 @@ #include #include #include +#include +#include #include #include #include +#include #include "omap-mcbsp.h" #include "omap-pcm.h" +struct omap_twl4030 { + int jack_detect; /* board can detect jack events */ + struct snd_soc_jack hs_jack; +}; + static int omap_twl4030_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { @@ -87,17 +97,164 @@ static struct snd_soc_ops omap_twl4030_ops = { .hw_params = omap_twl4030_hw_params, }; +static const struct snd_soc_dapm_widget dapm_widgets[] = { + SND_SOC_DAPM_SPK("Earpiece Spk", NULL), + SND_SOC_DAPM_SPK("Handsfree Spk", NULL), + SND_SOC_DAPM_HP("Headset Stereophone", NULL), + SND_SOC_DAPM_SPK("Ext Spk", NULL), + SND_SOC_DAPM_SPK("Carkit Spk", NULL), + + SND_SOC_DAPM_MIC("Main Mic", NULL), + SND_SOC_DAPM_MIC("Sub Mic", NULL), + SND_SOC_DAPM_MIC("Headset Mic", NULL), + SND_SOC_DAPM_MIC("Carkit Mic", NULL), + SND_SOC_DAPM_MIC("Digital0 Mic", NULL), + SND_SOC_DAPM_MIC("Digital1 Mic", NULL), + SND_SOC_DAPM_LINE("Line In", NULL), +}; + +static const struct snd_soc_dapm_route audio_map[] = { + /* Headset Stereophone: HSOL, HSOR */ + {"Headset Stereophone", NULL, "HSOL"}, + {"Headset Stereophone", NULL, "HSOR"}, + /* External Speakers: HFL, HFR */ + {"Handsfree Spk", NULL, "HFL"}, + {"Handsfree Spk", NULL, "HFR"}, + /* External Speakers: PredrivL, PredrivR */ + {"Ext Spk", NULL, "PREDRIVEL"}, + {"Ext Spk", NULL, "PREDRIVER"}, + /* Carkit speakers: CARKITL, CARKITR */ + {"Carkit Spk", NULL, "CARKITL"}, + {"Carkit Spk", NULL, "CARKITR"}, + /* Earpiece */ + {"Earpiece Spk", NULL, "EARPIECE"}, + + /* External Mics: MAINMIC, SUBMIC with bias */ + {"MAINMIC", NULL, "Main Mic"}, + {"Main Mic", NULL, "Mic Bias 1"}, + {"SUBMIC", NULL, "Sub Mic"}, + {"Sub Mic", NULL, "Mic Bias 2"}, + /* Headset Mic: HSMIC with bias */ + {"HSMIC", NULL, "Headset Mic"}, + {"Headset Mic", NULL, "Headset Mic Bias"}, + /* Digital Mics: DIGIMIC0, DIGIMIC1 with bias */ + {"DIGIMIC0", NULL, "Digital0 Mic"}, + {"Digital0 Mic", NULL, "Mic Bias 1"}, + {"DIGIMIC1", NULL, "Digital1 Mic"}, + {"Digital1 Mic", NULL, "Mic Bias 2"}, + /* Carkit In: CARKITMIC */ + {"CARKITMIC", NULL, "Carkit Mic"}, + /* Aux In: AUXL, AUXR */ + {"AUXL", NULL, "Line In"}, + {"AUXR", NULL, "Line In"}, +}; + +/* Headset jack detection DAPM pins */ +static struct snd_soc_jack_pin hs_jack_pins[] = { + { + .pin = "Headset Mic", + .mask = SND_JACK_MICROPHONE, + }, + { + .pin = "Headset Stereophone", + .mask = SND_JACK_HEADPHONE, + }, +}; + +/* Headset jack detection gpios */ +static struct snd_soc_jack_gpio hs_jack_gpios[] = { + { + .name = "hsdet-gpio", + .report = SND_JACK_HEADSET, + .debounce_time = 200, + }, +}; + +static inline void twl4030_disconnect_pin(struct snd_soc_dapm_context *dapm, + int connected, char *pin) +{ + if (!connected) + snd_soc_dapm_disable_pin(dapm, pin); +} + +static int omap_twl4030_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_card *card = codec->card; + struct snd_soc_dapm_context *dapm = &codec->dapm; + struct omap_tw4030_pdata *pdata = dev_get_platdata(card->dev); + struct omap_twl4030 *priv = snd_soc_card_get_drvdata(card); + int ret = 0; + + /* Headset jack detection only if it is supported */ + if (priv->jack_detect > 0) { + hs_jack_gpios[0].gpio = priv->jack_detect; + + ret = snd_soc_jack_new(codec, "Headset Jack", SND_JACK_HEADSET, + &priv->hs_jack); + if (ret) + return ret; + + ret = snd_soc_jack_add_pins(&priv->hs_jack, + ARRAY_SIZE(hs_jack_pins), + hs_jack_pins); + if (ret) + return ret; + + ret = snd_soc_jack_add_gpios(&priv->hs_jack, + ARRAY_SIZE(hs_jack_gpios), + hs_jack_gpios); + if (ret) + return ret; + } + + /* + * NULL pdata means we booted with DT. In this case the routing is + * provided and the card is fully routed, no need to mark pins. + */ + if (!pdata || !pdata->custom_routing) + return ret; + + /* Disable not connected paths if not used */ + twl4030_disconnect_pin(dapm, pdata->has_ear, "Earpiece Spk"); + twl4030_disconnect_pin(dapm, pdata->has_hf, "Handsfree Spk"); + twl4030_disconnect_pin(dapm, pdata->has_hs, "Headset Stereophone"); + twl4030_disconnect_pin(dapm, pdata->has_predriv, "Ext Spk"); + twl4030_disconnect_pin(dapm, pdata->has_carkit, "Carkit Spk"); + + twl4030_disconnect_pin(dapm, pdata->has_mainmic, "Main Mic"); + twl4030_disconnect_pin(dapm, pdata->has_submic, "Sub Mic"); + twl4030_disconnect_pin(dapm, pdata->has_hsmic, "Headset Mic"); + twl4030_disconnect_pin(dapm, pdata->has_carkitmic, "Carkit Mic"); + twl4030_disconnect_pin(dapm, pdata->has_digimic0, "Digital0 Mic"); + twl4030_disconnect_pin(dapm, pdata->has_digimic1, "Digital1 Mic"); + twl4030_disconnect_pin(dapm, pdata->has_linein, "Line In"); + + return ret; +} + /* Digital audio interface glue - connects codec <--> CPU */ static struct snd_soc_dai_link omap_twl4030_dai_links[] = { { - .name = "TWL4030", - .stream_name = "TWL4030", + .name = "TWL4030 HiFi", + .stream_name = "TWL4030 HiFi", .cpu_dai_name = "omap-mcbsp.2", .codec_dai_name = "twl4030-hifi", .platform_name = "omap-pcm-audio", .codec_name = "twl4030-codec", + .init = omap_twl4030_init, .ops = &omap_twl4030_ops, }, + { + .name = "TWL4030 Voice", + .stream_name = "TWL4030 Voice", + .cpu_dai_name = "omap-mcbsp.3", + .codec_dai_name = "twl4030-voice", + .platform_name = "omap-pcm-audio", + .codec_name = "twl4030-codec", + .dai_fmt = SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_IB_NF | + SND_SOC_DAIFMT_CBM_CFM, + }, }; /* Audio machine driver */ @@ -105,6 +262,11 @@ static struct snd_soc_card omap_twl4030_card = { .owner = THIS_MODULE, .dai_link = omap_twl4030_dai_links, .num_links = ARRAY_SIZE(omap_twl4030_dai_links), + + .dapm_widgets = dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(dapm_widgets), + .dapm_routes = audio_map, + .num_dapm_routes = ARRAY_SIZE(audio_map), }; static int omap_twl4030_probe(struct platform_device *pdev) @@ -112,12 +274,18 @@ static int omap_twl4030_probe(struct platform_device *pdev) struct omap_tw4030_pdata *pdata = dev_get_platdata(&pdev->dev); struct device_node *node = pdev->dev.of_node; struct snd_soc_card *card = &omap_twl4030_card; + struct omap_twl4030 *priv; int ret = 0; card->dev = &pdev->dev; + priv = devm_kzalloc(&pdev->dev, sizeof(struct omap_twl4030), GFP_KERNEL); + if (priv == NULL) + return -ENOMEM; + if (node) { struct device_node *dai_node; + struct property *prop; if (snd_soc_of_parse_card_name(card, "ti,model")) { dev_err(&pdev->dev, "Card name is not provided\n"); @@ -132,6 +300,27 @@ static int omap_twl4030_probe(struct platform_device *pdev) omap_twl4030_dai_links[0].cpu_dai_name = NULL; omap_twl4030_dai_links[0].cpu_of_node = dai_node; + dai_node = of_parse_phandle(node, "ti,mcbsp-voice", 0); + if (!dai_node) { + card->num_links = 1; + } else { + omap_twl4030_dai_links[1].cpu_dai_name = NULL; + omap_twl4030_dai_links[1].cpu_of_node = dai_node; + } + + priv->jack_detect = of_get_named_gpio(node, + "ti,jack-det-gpio", 0); + + /* Optional: audio routing can be provided */ + prop = of_find_property(node, "ti,audio-routing", NULL); + if (prop) { + ret = snd_soc_of_parse_audio_routing(card, + "ti,audio-routing"); + if (ret) + return ret; + + card->fully_routed = 1; + } } else if (pdata) { if (pdata->card_name) { card->name = pdata->card_name; @@ -139,11 +328,17 @@ static int omap_twl4030_probe(struct platform_device *pdev) dev_err(&pdev->dev, "Card name is not provided\n"); return -ENODEV; } + + if (!pdata->voice_connected) + card->num_links = 1; + + priv->jack_detect = pdata->jack_detect; } else { dev_err(&pdev->dev, "Missing pdata\n"); return -ENODEV; } + snd_soc_card_set_drvdata(card, priv); ret = snd_soc_register_card(card); if (ret) { dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n", @@ -157,7 +352,12 @@ static int omap_twl4030_probe(struct platform_device *pdev) static int omap_twl4030_remove(struct platform_device *pdev) { struct snd_soc_card *card = platform_get_drvdata(pdev); + struct omap_twl4030 *priv = snd_soc_card_get_drvdata(card); + if (priv->jack_detect > 0) + snd_soc_jack_free_gpios(&priv->hs_jack, + ARRAY_SIZE(hs_jack_gpios), + hs_jack_gpios); snd_soc_unregister_card(card); return 0; -- cgit v0.10.2 From 57d61b9d2d7bc618b7b28a46310d9c2f086f8f51 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 31 Dec 2012 11:51:49 +0100 Subject: ASoC: OMAP: Remove obsolete machine drivers for Zoom2 and SDP3430 These boards are using the common omap-twl4030 machine driver, no need for separate machine drivers anymore. Signed-off-by: Peter Ujfalusi Signed-off-by: Mark Brown diff --git a/sound/soc/omap/Kconfig b/sound/soc/omap/Kconfig index e8d2a2f..60259f2 100644 --- a/sound/soc/omap/Kconfig +++ b/sound/soc/omap/Kconfig @@ -70,15 +70,6 @@ config SND_OMAP_SOC_AM3517EVM Say Y if you want to add support for SoC audio on the OMAP3517 / AM3517 EVM. -config SND_OMAP_SOC_SDP3430 - tristate "SoC Audio support for Texas Instruments SDP3430" - depends on TWL4030_CORE && SND_OMAP_SOC && MACH_OMAP_3430SDP - select SND_OMAP_SOC_MCBSP - select SND_SOC_TWL4030 - help - Say Y if you want to add support for SoC audio on Texas Instruments - SDP3430. - config SND_OMAP_SOC_OMAP_TWL4030 tristate "SoC Audio support for TI SoC based boards with twl4030 codec" depends on TWL4030_CORE && SND_OMAP_SOC @@ -125,11 +116,3 @@ config SND_OMAP_SOC_OMAP3_PANDORA select SND_SOC_TWL4030 help Say Y if you want to add support for SoC audio on the OMAP3 Pandora. - -config SND_OMAP_SOC_ZOOM2 - tristate "SoC Audio support for Zoom2" - depends on TWL4030_CORE && SND_OMAP_SOC && MACH_OMAP_ZOOM2 - select SND_OMAP_SOC_MCBSP - select SND_SOC_TWL4030 - help - Say Y if you want to add support for Soc audio on Zoom2 board. diff --git a/sound/soc/omap/Makefile b/sound/soc/omap/Makefile index 19637e5..2b22594 100644 --- a/sound/soc/omap/Makefile +++ b/sound/soc/omap/Makefile @@ -17,11 +17,9 @@ snd-soc-rx51-objs := rx51.o snd-soc-ams-delta-objs := ams-delta.o snd-soc-osk5912-objs := osk5912.o snd-soc-am3517evm-objs := am3517evm.o -snd-soc-sdp3430-objs := sdp3430.o snd-soc-omap-abe-twl6040-objs := omap-abe-twl6040.o snd-soc-omap-twl4030-objs := omap-twl4030.o snd-soc-omap3pandora-objs := omap3pandora.o -snd-soc-zoom2-objs := zoom2.o snd-soc-omap-hdmi-card-objs := omap-hdmi-card.o obj-$(CONFIG_SND_OMAP_SOC_N810) += snd-soc-n810.o @@ -30,9 +28,7 @@ obj-$(CONFIG_SND_OMAP_SOC_AMS_DELTA) += snd-soc-ams-delta.o obj-$(CONFIG_SND_OMAP_SOC_OSK5912) += snd-soc-osk5912.o obj-$(CONFIG_SND_OMAP_SOC_OMAP2EVM) += snd-soc-omap2evm.o obj-$(CONFIG_SND_OMAP_SOC_AM3517EVM) += snd-soc-am3517evm.o -obj-$(CONFIG_SND_OMAP_SOC_SDP3430) += snd-soc-sdp3430.o obj-$(CONFIG_SND_OMAP_SOC_OMAP_ABE_TWL6040) += snd-soc-omap-abe-twl6040.o obj-$(CONFIG_SND_OMAP_SOC_OMAP_TWL4030) += snd-soc-omap-twl4030.o obj-$(CONFIG_SND_OMAP_SOC_OMAP3_PANDORA) += snd-soc-omap3pandora.o -obj-$(CONFIG_SND_OMAP_SOC_ZOOM2) += snd-soc-zoom2.o obj-$(CONFIG_SND_OMAP_SOC_OMAP_HDMI) += snd-soc-omap-hdmi-card.o diff --git a/sound/soc/omap/sdp3430.c b/sound/soc/omap/sdp3430.c deleted file mode 100644 index 216cbdd..0000000 --- a/sound/soc/omap/sdp3430.c +++ /dev/null @@ -1,245 +0,0 @@ -/* - * sdp3430.c -- SoC audio for TI OMAP3430 SDP - * - * Author: Misael Lopez Cruz - * - * Based on: - * Author: Steve Sakoman - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - * - */ - -#include -#include -#include -#include -#include -#include - -#include -#include - -#include - -#include "omap-mcbsp.h" -#include "omap-pcm.h" - -static struct snd_soc_card snd_soc_sdp3430; - -static int sdp3430_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; - int ret; - - /* Set the codec system clock for DAC and ADC */ - ret = snd_soc_dai_set_sysclk(codec_dai, 0, 26000000, - SND_SOC_CLOCK_IN); - if (ret < 0) { - printk(KERN_ERR "can't set codec system clock\n"); - return ret; - } - - return 0; -} - -static struct snd_soc_ops sdp3430_ops = { - .hw_params = sdp3430_hw_params, -}; - -/* Headset jack */ -static struct snd_soc_jack hs_jack; - -/* Headset jack detection DAPM pins */ -static struct snd_soc_jack_pin hs_jack_pins[] = { - { - .pin = "Headset Mic", - .mask = SND_JACK_MICROPHONE, - }, - { - .pin = "Headset Stereophone", - .mask = SND_JACK_HEADPHONE, - }, -}; - -/* Headset jack detection gpios */ -static struct snd_soc_jack_gpio hs_jack_gpios[] = { - { - .gpio = (OMAP_MAX_GPIO_LINES + 2), - .name = "hsdet-gpio", - .report = SND_JACK_HEADSET, - .debounce_time = 200, - }, -}; - -/* SDP3430 machine DAPM */ -static const struct snd_soc_dapm_widget sdp3430_twl4030_dapm_widgets[] = { - SND_SOC_DAPM_MIC("Ext Mic", NULL), - SND_SOC_DAPM_SPK("Ext Spk", NULL), - SND_SOC_DAPM_MIC("Headset Mic", NULL), - SND_SOC_DAPM_HP("Headset Stereophone", NULL), -}; - -static const struct snd_soc_dapm_route audio_map[] = { - /* External Mics: MAINMIC, SUBMIC with bias */ - {"MAINMIC", NULL, "Ext Mic"}, - {"SUBMIC", NULL, "Ext Mic"}, - {"Ext Mic", NULL, "Mic Bias 1"}, - {"Ext Mic", NULL, "Mic Bias 2"}, - - /* External Speakers: HFL, HFR */ - {"Ext Spk", NULL, "HFL"}, - {"Ext Spk", NULL, "HFR"}, - - /* Headset Mic: HSMIC with bias */ - {"HSMIC", NULL, "Headset Mic"}, - {"Headset Mic", NULL, "Headset Mic Bias"}, - - /* Headset Stereophone (Headphone): HSOL, HSOR */ - {"Headset Stereophone", NULL, "HSOL"}, - {"Headset Stereophone", NULL, "HSOR"}, -}; - -static int sdp3430_twl4030_init(struct snd_soc_pcm_runtime *rtd) -{ - struct snd_soc_codec *codec = rtd->codec; - struct snd_soc_dapm_context *dapm = &codec->dapm; - int ret; - - /* SDP3430 connected pins */ - snd_soc_dapm_enable_pin(dapm, "Ext Mic"); - snd_soc_dapm_enable_pin(dapm, "Ext Spk"); - snd_soc_dapm_disable_pin(dapm, "Headset Mic"); - snd_soc_dapm_disable_pin(dapm, "Headset Stereophone"); - - /* TWL4030 not connected pins */ - snd_soc_dapm_nc_pin(dapm, "AUXL"); - snd_soc_dapm_nc_pin(dapm, "AUXR"); - snd_soc_dapm_nc_pin(dapm, "CARKITMIC"); - snd_soc_dapm_nc_pin(dapm, "DIGIMIC0"); - snd_soc_dapm_nc_pin(dapm, "DIGIMIC1"); - - snd_soc_dapm_nc_pin(dapm, "OUTL"); - snd_soc_dapm_nc_pin(dapm, "OUTR"); - snd_soc_dapm_nc_pin(dapm, "EARPIECE"); - snd_soc_dapm_nc_pin(dapm, "PREDRIVEL"); - snd_soc_dapm_nc_pin(dapm, "PREDRIVER"); - snd_soc_dapm_nc_pin(dapm, "CARKITL"); - snd_soc_dapm_nc_pin(dapm, "CARKITR"); - - /* Headset jack detection */ - ret = snd_soc_jack_new(codec, "Headset Jack", - SND_JACK_HEADSET, &hs_jack); - if (ret) - return ret; - - ret = snd_soc_jack_add_pins(&hs_jack, ARRAY_SIZE(hs_jack_pins), - hs_jack_pins); - if (ret) - return ret; - - ret = snd_soc_jack_add_gpios(&hs_jack, ARRAY_SIZE(hs_jack_gpios), - hs_jack_gpios); - - return ret; -} - -/* Digital audio interface glue - connects codec <--> CPU */ -static struct snd_soc_dai_link sdp3430_dai[] = { - { - .name = "TWL4030 I2S", - .stream_name = "TWL4030 Audio", - .cpu_dai_name = "omap-mcbsp.2", - .codec_dai_name = "twl4030-hifi", - .platform_name = "omap-pcm-audio", - .codec_name = "twl4030-codec", - .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBM_CFM, - .init = sdp3430_twl4030_init, - .ops = &sdp3430_ops, - }, - { - .name = "TWL4030 PCM", - .stream_name = "TWL4030 Voice", - .cpu_dai_name = "omap-mcbsp.3", - .codec_dai_name = "twl4030-voice", - .platform_name = "omap-pcm-audio", - .codec_name = "twl4030-codec", - .dai_fmt = SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_IB_NF | - SND_SOC_DAIFMT_CBM_CFM, - .ops = &sdp3430_ops, - }, -}; - -/* Audio machine driver */ -static struct snd_soc_card snd_soc_sdp3430 = { - .name = "SDP3430", - .owner = THIS_MODULE, - .dai_link = sdp3430_dai, - .num_links = ARRAY_SIZE(sdp3430_dai), - - .dapm_widgets = sdp3430_twl4030_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(sdp3430_twl4030_dapm_widgets), - .dapm_routes = audio_map, - .num_dapm_routes = ARRAY_SIZE(audio_map), -}; - -static struct platform_device *sdp3430_snd_device; - -static int __init sdp3430_soc_init(void) -{ - int ret; - - if (!machine_is_omap_3430sdp()) - return -ENODEV; - printk(KERN_INFO "SDP3430 SoC init\n"); - - sdp3430_snd_device = platform_device_alloc("soc-audio", -1); - if (!sdp3430_snd_device) { - printk(KERN_ERR "Platform device allocation failed\n"); - return -ENOMEM; - } - - platform_set_drvdata(sdp3430_snd_device, &snd_soc_sdp3430); - - ret = platform_device_add(sdp3430_snd_device); - if (ret) - goto err1; - - return 0; - -err1: - printk(KERN_ERR "Unable to add platform device\n"); - platform_device_put(sdp3430_snd_device); - - return ret; -} -module_init(sdp3430_soc_init); - -static void __exit sdp3430_soc_exit(void) -{ - snd_soc_jack_free_gpios(&hs_jack, ARRAY_SIZE(hs_jack_gpios), - hs_jack_gpios); - - platform_device_unregister(sdp3430_snd_device); -} -module_exit(sdp3430_soc_exit); - -MODULE_AUTHOR("Misael Lopez Cruz "); -MODULE_DESCRIPTION("ALSA SoC SDP3430"); -MODULE_LICENSE("GPL"); - diff --git a/sound/soc/omap/zoom2.c b/sound/soc/omap/zoom2.c deleted file mode 100644 index 62a6b02..0000000 --- a/sound/soc/omap/zoom2.c +++ /dev/null @@ -1,193 +0,0 @@ -/* - * zoom2.c -- SoC audio for Zoom2 - * - * Author: Misael Lopez Cruz - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - * - */ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -/* Register descriptions for twl4030 codec part */ -#include -#include - -#include "omap-mcbsp.h" -#include "omap-pcm.h" - -static int zoom2_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; - int ret; - - /* Set the codec system clock for DAC and ADC */ - ret = snd_soc_dai_set_sysclk(codec_dai, 0, 26000000, - SND_SOC_CLOCK_IN); - if (ret < 0) { - printk(KERN_ERR "can't set codec system clock\n"); - return ret; - } - - return 0; -} - -static struct snd_soc_ops zoom2_ops = { - .hw_params = zoom2_hw_params, -}; - -/* Zoom2 machine DAPM */ -static const struct snd_soc_dapm_widget zoom2_twl4030_dapm_widgets[] = { - SND_SOC_DAPM_MIC("Ext Mic", NULL), - SND_SOC_DAPM_SPK("Ext Spk", NULL), - SND_SOC_DAPM_MIC("Headset Mic", NULL), - SND_SOC_DAPM_HP("Headset Stereophone", NULL), - SND_SOC_DAPM_LINE("Aux In", NULL), -}; - -static const struct snd_soc_dapm_route audio_map[] = { - /* External Mics: MAINMIC, SUBMIC with bias */ - {"MAINMIC", NULL, "Ext Mic"}, - {"SUBMIC", NULL, "Ext Mic"}, - {"Ext Mic", NULL, "Mic Bias 1"}, - {"Ext Mic", NULL, "Mic Bias 2"}, - - /* External Speakers: HFL, HFR */ - {"Ext Spk", NULL, "HFL"}, - {"Ext Spk", NULL, "HFR"}, - - /* Headset Stereophone: HSOL, HSOR */ - {"Headset Stereophone", NULL, "HSOL"}, - {"Headset Stereophone", NULL, "HSOR"}, - - /* Headset Mic: HSMIC with bias */ - {"HSMIC", NULL, "Headset Mic"}, - {"Headset Mic", NULL, "Headset Mic Bias"}, - - /* Aux In: AUXL, AUXR */ - {"Aux In", NULL, "AUXL"}, - {"Aux In", NULL, "AUXR"}, -}; - -static int zoom2_twl4030_init(struct snd_soc_pcm_runtime *rtd) -{ - struct snd_soc_codec *codec = rtd->codec; - struct snd_soc_dapm_context *dapm = &codec->dapm; - - /* TWL4030 not connected pins */ - snd_soc_dapm_nc_pin(dapm, "CARKITMIC"); - snd_soc_dapm_nc_pin(dapm, "DIGIMIC0"); - snd_soc_dapm_nc_pin(dapm, "DIGIMIC1"); - snd_soc_dapm_nc_pin(dapm, "EARPIECE"); - snd_soc_dapm_nc_pin(dapm, "PREDRIVEL"); - snd_soc_dapm_nc_pin(dapm, "PREDRIVER"); - snd_soc_dapm_nc_pin(dapm, "CARKITL"); - snd_soc_dapm_nc_pin(dapm, "CARKITR"); - - return 0; -} - -/* Digital audio interface glue - connects codec <--> CPU */ -static struct snd_soc_dai_link zoom2_dai[] = { - { - .name = "TWL4030 I2S", - .stream_name = "TWL4030 Audio", - .cpu_dai_name = "omap-mcbsp.2", - .codec_dai_name = "twl4030-hifi", - .platform_name = "omap-pcm-audio", - .codec_name = "twl4030-codec", - .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBM_CFM, - .init = zoom2_twl4030_init, - .ops = &zoom2_ops, - }, - { - .name = "TWL4030 PCM", - .stream_name = "TWL4030 Voice", - .cpu_dai_name = "omap-mcbsp.3", - .codec_dai_name = "twl4030-voice", - .platform_name = "omap-pcm-audio", - .codec_name = "twl4030-codec", - .dai_fmt = SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_IB_NF | - SND_SOC_DAIFMT_CBM_CFM, - .ops = &zoom2_ops, - }, -}; - -/* Audio machine driver */ -static struct snd_soc_card snd_soc_zoom2 = { - .name = "Zoom2", - .owner = THIS_MODULE, - .dai_link = zoom2_dai, - .num_links = ARRAY_SIZE(zoom2_dai), - - .dapm_widgets = zoom2_twl4030_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(zoom2_twl4030_dapm_widgets), - .dapm_routes = audio_map, - .num_dapm_routes = ARRAY_SIZE(audio_map), -}; - -static struct platform_device *zoom2_snd_device; - -static int __init zoom2_soc_init(void) -{ - int ret; - - if (!machine_is_omap_zoom2()) - return -ENODEV; - printk(KERN_INFO "Zoom2 SoC init\n"); - - zoom2_snd_device = platform_device_alloc("soc-audio", -1); - if (!zoom2_snd_device) { - printk(KERN_ERR "Platform device allocation failed\n"); - return -ENOMEM; - } - - platform_set_drvdata(zoom2_snd_device, &snd_soc_zoom2); - ret = platform_device_add(zoom2_snd_device); - if (ret) - goto err1; - - return 0; - -err1: - printk(KERN_ERR "Unable to add platform device\n"); - platform_device_put(zoom2_snd_device); - - return ret; -} -module_init(zoom2_soc_init); - -static void __exit zoom2_soc_exit(void) -{ - platform_device_unregister(zoom2_snd_device); -} -module_exit(zoom2_soc_exit); - -MODULE_AUTHOR("Misael Lopez Cruz "); -MODULE_DESCRIPTION("ALSA SoC Zoom2"); -MODULE_LICENSE("GPL"); - -- cgit v0.10.2 From 016fb39c98a71539d9abd5ee66cc1103a32fbb34 Mon Sep 17 00:00:00 2001 From: Mike Dunn Date: Sat, 29 Dec 2012 10:53:17 -0800 Subject: ASoC: palm27x: fix widgets and routes in dai_link init ASoC core code now handles creation of controls and routing based on contents of struct snd_soc_card, so remove calls to snd_soc_dapm_new_controls() and snd_soc_dapm_add_routes() from the snd_soc_dai_link init function, and add widget and route definitions to struct snd_soc_card. Signed-off-by: Mike Dunn Signed-off-by: Mark Brown diff --git a/sound/soc/pxa/palm27x.c b/sound/soc/pxa/palm27x.c index 2074e2d..bb062e6 100644 --- a/sound/soc/pxa/palm27x.c +++ b/sound/soc/pxa/palm27x.c @@ -79,17 +79,6 @@ static int palm27x_ac97_init(struct snd_soc_pcm_runtime *rtd) struct snd_soc_dapm_context *dapm = &codec->dapm; int err; - /* add palm27x specific widgets */ - err = snd_soc_dapm_new_controls(dapm, palm27x_dapm_widgets, - ARRAY_SIZE(palm27x_dapm_widgets)); - if (err) - return err; - - /* set up palm27x specific audio path audio_map */ - err = snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map)); - if (err) - return err; - /* connected pins */ if (machine_is_palmld()) snd_soc_dapm_enable_pin(dapm, "MIC1"); @@ -149,6 +138,10 @@ static struct snd_soc_card palm27x_asoc = { .owner = THIS_MODULE, .dai_link = palm27x_dai, .num_links = ARRAY_SIZE(palm27x_dai), + .dapm_widgets = palm27x_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(palm27x_dapm_widgets), + .dapm_routes = audio_map, + .num_dapm_routes = ARRAY_SIZE(audio_map) }; static struct platform_device *palm27x_snd_device; -- cgit v0.10.2 From 01a61f490cab732542753db69e17e5db657d185a Mon Sep 17 00:00:00 2001 From: Mike Dunn Date: Sat, 29 Dec 2012 10:53:18 -0800 Subject: ASoC: palm27x: register card in platform_driver probe Remove creation of an soc-audio device from the machine platform_driver probe function, and add a call to snd_soc_register_card() instead. The current code still works, but this mechanism has been deprecated, if I'm not mistaken. The ASoC core code produces the warning "ASoC: machine Palm/PXA27x should use snd_soc_register_card()" Signed-off-by: Mike Dunn Signed-off-by: Mark Brown diff --git a/sound/soc/pxa/palm27x.c b/sound/soc/pxa/palm27x.c index bb062e6..e1ffcdd 100644 --- a/sound/soc/pxa/palm27x.c +++ b/sound/soc/pxa/palm27x.c @@ -144,8 +144,6 @@ static struct snd_soc_card palm27x_asoc = { .num_dapm_routes = ARRAY_SIZE(audio_map) }; -static struct platform_device *palm27x_snd_device; - static int palm27x_asoc_probe(struct platform_device *pdev) { int ret; @@ -162,27 +160,18 @@ static int palm27x_asoc_probe(struct platform_device *pdev) hs_jack_gpios[0].gpio = ((struct palm27x_asoc_info *) (pdev->dev.platform_data))->jack_gpio; - palm27x_snd_device = platform_device_alloc("soc-audio", -1); - if (!palm27x_snd_device) - return -ENOMEM; - - platform_set_drvdata(palm27x_snd_device, &palm27x_asoc); - ret = platform_device_add(palm27x_snd_device); - - if (ret != 0) - goto put_device; - - return 0; - -put_device: - platform_device_put(palm27x_snd_device); + palm27x_asoc.dev = &pdev->dev; + ret = snd_soc_register_card(&palm27x_asoc); + if (ret) + dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n", + ret); return ret; } static int palm27x_asoc_remove(struct platform_device *pdev) { - platform_device_unregister(palm27x_snd_device); + snd_soc_unregister_card(&palm27x_asoc); return 0; } -- cgit v0.10.2 From fd974e52dbbdb27910d40d8feea7000f26545683 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 27 Dec 2012 19:15:08 -0800 Subject: ASoC: fsi: don't use platform info pointer on probe() Current FSI driver is using platform info pointer, but it is not good design for DT support. This patch made it not to use platform info pointer. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown diff --git a/sound/soc/sh/fsi.c b/sound/soc/sh/fsi.c index f14c611..ef34ef8 100644 --- a/sound/soc/sh/fsi.c +++ b/sound/soc/sh/fsi.c @@ -1918,16 +1918,15 @@ static int fsi_probe(struct platform_device *pdev) { struct fsi_master *master; const struct platform_device_id *id_entry; - struct sh_fsi_platform_info *info = pdev->dev.platform_data; - struct sh_fsi_port_info nul_info, *pinfo; + struct sh_fsi_platform_info info; struct fsi_priv *fsi; struct resource *res; unsigned int irq; int ret; - nul_info.flags = 0; - nul_info.tx_id = 0; - nul_info.rx_id = 0; + memset(&info, 0, sizeof(info)); + if (pdev->dev.platform_data) + memcpy(&info, pdev->dev.platform_data, sizeof(info)); id_entry = pdev->id_entry; if (!id_entry) { @@ -1961,12 +1960,11 @@ static int fsi_probe(struct platform_device *pdev) spin_lock_init(&master->lock); /* FSI A setting */ - pinfo = (info) ? &info->port_a : &nul_info; fsi = &master->fsia; fsi->base = master->base; fsi->master = master; - fsi_port_info_init(fsi, pinfo); - fsi_handler_init(fsi, pinfo); + fsi_port_info_init(fsi, &info.port_a); + fsi_handler_init(fsi, &info.port_a); ret = fsi_stream_probe(fsi, &pdev->dev); if (ret < 0) { dev_err(&pdev->dev, "FSIA stream probe failed\n"); @@ -1974,12 +1972,11 @@ static int fsi_probe(struct platform_device *pdev) } /* FSI B setting */ - pinfo = (info) ? &info->port_b : &nul_info; fsi = &master->fsib; fsi->base = master->base + 0x40; fsi->master = master; - fsi_port_info_init(fsi, pinfo); - fsi_handler_init(fsi, pinfo); + fsi_port_info_init(fsi, &info.port_b); + fsi_handler_init(fsi, &info.port_b); ret = fsi_stream_probe(fsi, &pdev->dev); if (ret < 0) { dev_err(&pdev->dev, "FSIB stream probe failed\n"); -- cgit v0.10.2 From 91660bd65c26b71c35772004c686ed437a1e2cf1 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 5 Dec 2012 20:35:24 +0900 Subject: ASoC: wm5102: Implement routing and power management for ISRCs Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c index e55fa03..27c6a52 100644 --- a/sound/soc/codecs/arizona.c +++ b/sound/soc/codecs/arizona.c @@ -141,6 +141,30 @@ const char *arizona_mixer_texts[ARIZONA_NUM_MIXER_INPUTS] = { "ASRC1R", "ASRC2L", "ASRC2R", + "ISRC1INT1", + "ISRC1INT2", + "ISRC1INT3", + "ISRC1INT4", + "ISRC1DEC1", + "ISRC1DEC2", + "ISRC1DEC3", + "ISRC1DEC4", + "ISRC2INT1", + "ISRC2INT2", + "ISRC2INT3", + "ISRC2INT4", + "ISRC2DEC1", + "ISRC2DEC2", + "ISRC2DEC3", + "ISRC2DEC4", + "ISRC3INT1", + "ISRC3INT2", + "ISRC3INT3", + "ISRC3INT4", + "ISRC3DEC1", + "ISRC3DEC2", + "ISRC3DEC3", + "ISRC3DEC4", }; EXPORT_SYMBOL_GPL(arizona_mixer_texts); @@ -220,6 +244,30 @@ int arizona_mixer_values[ARIZONA_NUM_MIXER_INPUTS] = { 0x91, 0x92, 0x93, + 0xa0, /* ISRC1INT1 */ + 0xa1, + 0xa2, + 0xa3, + 0xa4, /* ISRC1DEC1 */ + 0xa5, + 0xa6, + 0xa7, + 0xa8, /* ISRC2DEC1 */ + 0xa9, + 0xaa, + 0xab, + 0xac, /* ISRC2INT1 */ + 0xad, + 0xae, + 0xaf, + 0xb0, /* ISRC3DEC1 */ + 0xb1, + 0xb2, + 0xb3, + 0xb4, /* ISRC3INT1 */ + 0xb5, + 0xb6, + 0xb7, }; EXPORT_SYMBOL_GPL(arizona_mixer_values); diff --git a/sound/soc/codecs/arizona.h b/sound/soc/codecs/arizona.h index 3d083d0..7f22b4f 100644 --- a/sound/soc/codecs/arizona.h +++ b/sound/soc/codecs/arizona.h @@ -66,7 +66,7 @@ struct arizona_priv { struct arizona_dai_priv dai[ARIZONA_MAX_DAI]; }; -#define ARIZONA_NUM_MIXER_INPUTS 75 +#define ARIZONA_NUM_MIXER_INPUTS 99 extern const unsigned int arizona_mixer_tlv[]; extern const char *arizona_mixer_texts[ARIZONA_NUM_MIXER_INPUTS]; diff --git a/sound/soc/codecs/wm5102.c b/sound/soc/codecs/wm5102.c index e64b968..04ceb6d 100644 --- a/sound/soc/codecs/wm5102.c +++ b/sound/soc/codecs/wm5102.c @@ -876,6 +876,18 @@ ARIZONA_MUX_ENUMS(ASRC1R, ARIZONA_ASRC1RMIX_INPUT_1_SOURCE); ARIZONA_MUX_ENUMS(ASRC2L, ARIZONA_ASRC2LMIX_INPUT_1_SOURCE); ARIZONA_MUX_ENUMS(ASRC2R, ARIZONA_ASRC2RMIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC1INT1, ARIZONA_ISRC1INT1MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC1INT2, ARIZONA_ISRC1INT2MIX_INPUT_1_SOURCE); + +ARIZONA_MUX_ENUMS(ISRC1DEC1, ARIZONA_ISRC1DEC1MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC1DEC2, ARIZONA_ISRC1DEC2MIX_INPUT_1_SOURCE); + +ARIZONA_MUX_ENUMS(ISRC2INT1, ARIZONA_ISRC2INT1MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC2INT2, ARIZONA_ISRC2INT2MIX_INPUT_1_SOURCE); + +ARIZONA_MUX_ENUMS(ISRC2DEC1, ARIZONA_ISRC2DEC1MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC2DEC2, ARIZONA_ISRC2DEC2MIX_INPUT_1_SOURCE); + ARIZONA_MIXER_ENUMS(DSP1L, ARIZONA_DSP1LMIX_INPUT_1_SOURCE); ARIZONA_MIXER_ENUMS(DSP1R, ARIZONA_DSP1RMIX_INPUT_1_SOURCE); @@ -999,6 +1011,26 @@ SND_SOC_DAPM_PGA("ASRC2L", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC2L_ENA_SHIFT, 0, SND_SOC_DAPM_PGA("ASRC2R", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC2R_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC1INT1", ARIZONA_ISRC_1_CTRL_3, + ARIZONA_ISRC1_INT0_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC1INT2", ARIZONA_ISRC_1_CTRL_3, + ARIZONA_ISRC1_INT1_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("ISRC1DEC1", ARIZONA_ISRC_1_CTRL_3, + ARIZONA_ISRC1_DEC0_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC1DEC2", ARIZONA_ISRC_1_CTRL_3, + ARIZONA_ISRC1_DEC1_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("ISRC2INT1", ARIZONA_ISRC_2_CTRL_3, + ARIZONA_ISRC2_INT0_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC2INT2", ARIZONA_ISRC_2_CTRL_3, + ARIZONA_ISRC2_INT1_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("ISRC2DEC1", ARIZONA_ISRC_2_CTRL_3, + ARIZONA_ISRC2_DEC0_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC2DEC2", ARIZONA_ISRC_2_CTRL_3, + ARIZONA_ISRC2_DEC1_ENA_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_AIF_OUT("AIF1TX1", NULL, 0, ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX1_ENA_SHIFT, 0), SND_SOC_DAPM_AIF_OUT("AIF1TX2", NULL, 0, @@ -1135,6 +1167,18 @@ ARIZONA_MUX_WIDGETS(ASRC1R, "ASRC1R"), ARIZONA_MUX_WIDGETS(ASRC2L, "ASRC2L"), ARIZONA_MUX_WIDGETS(ASRC2R, "ASRC2R"), +ARIZONA_MUX_WIDGETS(ISRC1DEC1, "ISRC1DEC1"), +ARIZONA_MUX_WIDGETS(ISRC1DEC2, "ISRC1DEC2"), + +ARIZONA_MUX_WIDGETS(ISRC1INT1, "ISRC1INT1"), +ARIZONA_MUX_WIDGETS(ISRC1INT2, "ISRC1INT2"), + +ARIZONA_MUX_WIDGETS(ISRC2DEC1, "ISRC2DEC1"), +ARIZONA_MUX_WIDGETS(ISRC2DEC2, "ISRC2DEC2"), + +ARIZONA_MUX_WIDGETS(ISRC2INT1, "ISRC2INT1"), +ARIZONA_MUX_WIDGETS(ISRC2INT2, "ISRC2INT2"), + WM_ADSP2("DSP1", 0), SND_SOC_DAPM_OUTPUT("HPOUT1L"), @@ -1190,6 +1234,14 @@ SND_SOC_DAPM_OUTPUT("SPKDAT1R"), { name, "ASRC1R", "ASRC1R" }, \ { name, "ASRC2L", "ASRC2L" }, \ { name, "ASRC2R", "ASRC2R" }, \ + { name, "ISRC1DEC1", "ISRC1DEC1" }, \ + { name, "ISRC1DEC2", "ISRC1DEC2" }, \ + { name, "ISRC1INT1", "ISRC1INT1" }, \ + { name, "ISRC1INT2", "ISRC1INT2" }, \ + { name, "ISRC2DEC1", "ISRC2DEC1" }, \ + { name, "ISRC2DEC2", "ISRC2DEC2" }, \ + { name, "ISRC2INT1", "ISRC2INT1" }, \ + { name, "ISRC2INT2", "ISRC2INT2" }, \ { name, "DSP1.1", "DSP1" }, \ { name, "DSP1.2", "DSP1" }, \ { name, "DSP1.3", "DSP1" }, \ @@ -1286,6 +1338,18 @@ static const struct snd_soc_dapm_route wm5102_dapm_routes[] = { { "ASRC2L", NULL, "ASRC2L Input" }, { "ASRC2R", NULL, "ASRC2R Input" }, + { "ISRC1DEC1", NULL, "ISRC1DEC1 Input" }, + { "ISRC1DEC2", NULL, "ISRC1DEC2 Input" }, + + { "ISRC1INT1", NULL, "ISRC1INT1 Input" }, + { "ISRC1INT2", NULL, "ISRC1INT2 Input" }, + + { "ISRC2DEC1", NULL, "ISRC2DEC1 Input" }, + { "ISRC2DEC2", NULL, "ISRC2DEC2 Input" }, + + { "ISRC2INT1", NULL, "ISRC2INT1 Input" }, + { "ISRC2INT2", NULL, "ISRC2INT2 Input" }, + ARIZONA_MIXER_ROUTES("OUT1L", "HPOUT1L"), ARIZONA_MIXER_ROUTES("OUT1R", "HPOUT1R"), ARIZONA_MIXER_ROUTES("OUT2L", "HPOUT2L"), @@ -1333,6 +1397,18 @@ static const struct snd_soc_dapm_route wm5102_dapm_routes[] = { ARIZONA_MUX_ROUTES("ASRC2L"), ARIZONA_MUX_ROUTES("ASRC2R"), + ARIZONA_MUX_ROUTES("ISRC1INT1"), + ARIZONA_MUX_ROUTES("ISRC1INT2"), + + ARIZONA_MUX_ROUTES("ISRC1DEC1"), + ARIZONA_MUX_ROUTES("ISRC1DEC2"), + + ARIZONA_MUX_ROUTES("ISRC2INT1"), + ARIZONA_MUX_ROUTES("ISRC2INT2"), + + ARIZONA_MUX_ROUTES("ISRC2DEC1"), + ARIZONA_MUX_ROUTES("ISRC2DEC2"), + ARIZONA_DSP_ROUTES("DSP1"), { "AEC Loopback", "HPOUT1L", "OUT1L" }, -- cgit v0.10.2 From bc9ab6d31c4fde1016ecc6606913ed0d52b3ed76 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 4 Jan 2013 19:31:00 +0000 Subject: ASoC: arizona: Allow runtime reconfiguration of the output mode Some systems use external analogue switches to connect more analogue devices to the CODEC than are supported by the device. In some systems this requires changing the switched output from single ended to differential mode dynamically at runtime. Add a new function arizona_set_output_mode() to support this. Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c index 27c6a52..bcd2258 100644 --- a/sound/soc/codecs/arizona.c +++ b/sound/soc/codecs/arizona.c @@ -1164,6 +1164,40 @@ int arizona_init_fll(struct arizona *arizona, int id, int base, int lock_irq, } EXPORT_SYMBOL_GPL(arizona_init_fll); +/** + * arizona_set_output_mode - Set the mode of the specified output + * + * @codec: Device to configure + * @output: Output number + * @diff: True to set the output to differential mode + * + * Some systems use external analogue switches to connect more + * analogue devices to the CODEC than are supported by the device. In + * some systems this requires changing the switched output from single + * ended to differential mode dynamically at runtime, an operation + * supported using this function. + * + * Most systems have a single static configuration and should use + * platform data instead. + */ +int arizona_set_output_mode(struct snd_soc_codec *codec, int output, bool diff) +{ + unsigned int reg, val; + + if (output < 1 || output > 6) + return -EINVAL; + + reg = ARIZONA_OUTPUT_PATH_CONFIG_1L + (output - 1) * 8; + + if (diff) + val = ARIZONA_OUT1_MONO; + else + val = 0; + + return snd_soc_update_bits(codec, reg, ARIZONA_OUT1_MONO, val); +} +EXPORT_SYMBOL_GPL(arizona_set_output_mode); + MODULE_DESCRIPTION("ASoC Wolfson Arizona class device support"); MODULE_AUTHOR("Mark Brown "); MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/arizona.h b/sound/soc/codecs/arizona.h index 7f22b4f..0973fd9 100644 --- a/sound/soc/codecs/arizona.h +++ b/sound/soc/codecs/arizona.h @@ -213,4 +213,7 @@ extern int arizona_set_fll(struct arizona_fll *fll, int source, extern int arizona_init_dai(struct arizona_priv *priv, int dai); +int arizona_set_output_mode(struct snd_soc_codec *codec, int output, + bool diff); + #endif -- cgit v0.10.2 From b272efc8600a7bbf2dd91d0eba8a3b8949e84497 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 10 Oct 2012 15:10:08 +0900 Subject: ASoC: arizona: Factor out rate selection code In preparation for more advanced sample rate managment move the existing code out of the main hw_params() function. Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c index 5afc811..6d98cf4 100644 --- a/sound/soc/codecs/arizona.c +++ b/sound/soc/codecs/arizona.c @@ -702,17 +702,63 @@ static int arizona_startup(struct snd_pcm_substream *substream, constraint); } +static int arizona_hw_params_rate(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec); + struct arizona_dai_priv *dai_priv = &priv->dai[dai->id - 1]; + int base = dai->driver->base; + int i, sr_val; + + /* + * We will need to be more flexible than this in future, + * currently we use a single sample rate for SYSCLK. + */ + for (i = 0; i < ARRAY_SIZE(arizona_sr_vals); i++) + if (arizona_sr_vals[i] == params_rate(params)) + break; + if (i == ARRAY_SIZE(arizona_sr_vals)) { + arizona_aif_err(dai, "Unsupported sample rate %dHz\n", + params_rate(params)); + return -EINVAL; + } + sr_val = i; + + switch (dai_priv->clk) { + case ARIZONA_CLK_SYSCLK: + snd_soc_update_bits(codec, ARIZONA_SAMPLE_RATE_1, + ARIZONA_SAMPLE_RATE_1_MASK, sr_val); + if (base) + snd_soc_update_bits(codec, base + ARIZONA_AIF_RATE_CTRL, + ARIZONA_AIF1_RATE_MASK, 0); + break; + case ARIZONA_CLK_ASYNCCLK: + snd_soc_update_bits(codec, ARIZONA_ASYNC_SAMPLE_RATE_1, + ARIZONA_ASYNC_SAMPLE_RATE_MASK, sr_val); + if (base) + snd_soc_update_bits(codec, base + ARIZONA_AIF_RATE_CTRL, + ARIZONA_AIF1_RATE_MASK, + 8 << ARIZONA_AIF1_RATE_SHIFT); + break; + default: + arizona_aif_err(dai, "Invalid clock %d\n", dai_priv->clk); + return -EINVAL; + } + + return 0; +} + static int arizona_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { struct snd_soc_codec *codec = dai->codec; - struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec); - struct arizona_dai_priv *dai_priv = &priv->dai[dai->id - 1]; int base = dai->driver->base; const int *rates; - int i; - int bclk, lrclk, wl, frame, sr_val; + int i, ret; + int bclk, lrclk, wl, frame; if (params_rate(params) % 8000) rates = &arizona_44k1_bclk_rates[0]; @@ -732,16 +778,6 @@ static int arizona_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } - for (i = 0; i < ARRAY_SIZE(arizona_sr_vals); i++) - if (arizona_sr_vals[i] == params_rate(params)) - break; - if (i == ARRAY_SIZE(arizona_sr_vals)) { - arizona_aif_err(dai, "Unsupported sample rate %dHz\n", - params_rate(params)); - return -EINVAL; - } - sr_val = i; - lrclk = snd_soc_params_to_bclk(params) / params_rate(params); arizona_aif_dbg(dai, "BCLK %dHz LRCLK %dHz\n", @@ -750,28 +786,9 @@ static int arizona_hw_params(struct snd_pcm_substream *substream, wl = snd_pcm_format_width(params_format(params)); frame = wl << ARIZONA_AIF1TX_WL_SHIFT | wl; - /* - * We will need to be more flexible than this in future, - * currently we use a single sample rate for SYSCLK. - */ - switch (dai_priv->clk) { - case ARIZONA_CLK_SYSCLK: - snd_soc_update_bits(codec, ARIZONA_SAMPLE_RATE_1, - ARIZONA_SAMPLE_RATE_1_MASK, sr_val); - snd_soc_update_bits(codec, base + ARIZONA_AIF_RATE_CTRL, - ARIZONA_AIF1_RATE_MASK, 0); - break; - case ARIZONA_CLK_ASYNCCLK: - snd_soc_update_bits(codec, ARIZONA_ASYNC_SAMPLE_RATE_1, - ARIZONA_ASYNC_SAMPLE_RATE_MASK, sr_val); - snd_soc_update_bits(codec, base + ARIZONA_AIF_RATE_CTRL, - ARIZONA_AIF1_RATE_MASK, - 8 << ARIZONA_AIF1_RATE_SHIFT); - break; - default: - arizona_aif_err(dai, "Invalid clock %d\n", dai_priv->clk); - return -EINVAL; - } + ret = arizona_hw_params_rate(substream, params, dai); + if (ret != 0) + return ret; snd_soc_update_bits(codec, base + ARIZONA_AIF_BCLK_CTRL, ARIZONA_AIF1_BCLK_FREQ_MASK, bclk); -- cgit v0.10.2 From 4498a3cae5012979bbf3be2064c5ca00fe29109b Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Wed, 14 Nov 2012 18:28:58 -0200 Subject: ASoC: mxs-saif: Remove platform data All MXS users have been converted to device tree and the board files have been removed. No need to keep platform data in the driver. Signed-off-by: Fabio Estevam Acked-by: Dong Aisheng Acked-by: Shawn Guo Signed-off-by: Mark Brown diff --git a/include/sound/saif.h b/include/sound/saif.h deleted file mode 100644 index f22f3e1..0000000 --- a/include/sound/saif.h +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright 2011 Freescale Semiconductor, Inc. All Rights Reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#ifndef __SOUND_SAIF_H__ -#define __SOUND_SAIF_H__ - -struct mxs_saif_platform_data { - bool master_mode; /* if true use master mode */ - int master_id; /* id of the master if in slave mode */ -}; -#endif diff --git a/sound/soc/mxs/mxs-saif.c b/sound/soc/mxs/mxs-saif.c index 365d9d2..752675d 100644 --- a/sound/soc/mxs/mxs-saif.c +++ b/sound/soc/mxs/mxs-saif.c @@ -32,7 +32,6 @@ #include #include #include -#include #include #include #include @@ -662,43 +661,32 @@ static int mxs_saif_probe(struct platform_device *pdev) struct device_node *np = pdev->dev.of_node; struct resource *iores, *dmares; struct mxs_saif *saif; - struct mxs_saif_platform_data *pdata; struct pinctrl *pinctrl; int ret = 0; + struct device_node *master; - - if (!np && pdev->id >= ARRAY_SIZE(mxs_saif)) + if (!np) return -EINVAL; saif = devm_kzalloc(&pdev->dev, sizeof(*saif), GFP_KERNEL); if (!saif) return -ENOMEM; - if (np) { - struct device_node *master; - saif->id = of_alias_get_id(np, "saif"); - if (saif->id < 0) - return saif->id; - /* - * If there is no "fsl,saif-master" phandle, it's a saif - * master. Otherwise, it's a slave and its phandle points - * to the master. - */ - master = of_parse_phandle(np, "fsl,saif-master", 0); - if (!master) { - saif->master_id = saif->id; - } else { - saif->master_id = of_alias_get_id(master, "saif"); - if (saif->master_id < 0) - return saif->master_id; - } + saif->id = of_alias_get_id(np, "saif"); + if (saif->id < 0) + return saif->id; + /* + * If there is no "fsl,saif-master" phandle, it's a saif + * master. Otherwise, it's a slave and its phandle points + * to the master. + */ + master = of_parse_phandle(np, "fsl,saif-master", 0); + if (!master) { + saif->master_id = saif->id; } else { - saif->id = pdev->id; - pdata = pdev->dev.platform_data; - if (pdata && !pdata->master_mode) - saif->master_id = pdata->master_id; - else - saif->master_id = saif->id; + saif->master_id = of_alias_get_id(master, "saif"); + if (saif->master_id < 0) + return saif->master_id; } if (saif->master_id < 0 || saif->master_id >= ARRAY_SIZE(mxs_saif)) { -- cgit v0.10.2 From 053fe0f166e540a9766548572eccfc18c21ff353 Mon Sep 17 00:00:00 2001 From: Mike Dunn Date: Mon, 7 Jan 2013 13:55:14 -0800 Subject: ALSA: pxa27x: rename pxa27x_assert_ac97reset() This patch does nothing functionally, it just gives the function a new name and modifies the prototype slightly in order to clarify what the function is doing (which is not necessarily asserting the reset). Some commentary also added. Tested on a palm treo 680 machine. Signed-off-by: Mike Dunn Acked-by: Igor Grinberg Signed-off-by: Mark Brown diff --git a/arch/arm/mach-pxa/pxa27x.c b/arch/arm/mach-pxa/pxa27x.c index 616cb87..69985b06 100644 --- a/arch/arm/mach-pxa/pxa27x.c +++ b/arch/arm/mach-pxa/pxa27x.c @@ -53,17 +53,25 @@ static unsigned long ac97_reset_config[] = { GPIO95_AC97_nRESET, }; -void pxa27x_assert_ac97reset(int reset_gpio, int on) +void pxa27x_configure_ac97reset(int reset_gpio, bool to_gpio) { + /* + * This helper function is used to work around a bug in the pxa27x's + * ac97 controller during a warm reset. The configuration of the + * reset_gpio is changed as follows: + * to_gpio == true: configured to generic output gpio and driven high + * to_gpio == false: configured to ac97 controller alt fn AC97_nRESET + */ + if (reset_gpio == 113) - pxa2xx_mfp_config(on ? &ac97_reset_config[0] : - &ac97_reset_config[1], 1); + pxa2xx_mfp_config(to_gpio ? &ac97_reset_config[0] : + &ac97_reset_config[1], 1); if (reset_gpio == 95) - pxa2xx_mfp_config(on ? &ac97_reset_config[2] : - &ac97_reset_config[3], 1); + pxa2xx_mfp_config(to_gpio ? &ac97_reset_config[2] : + &ac97_reset_config[3], 1); } -EXPORT_SYMBOL_GPL(pxa27x_assert_ac97reset); +EXPORT_SYMBOL_GPL(pxa27x_configure_ac97reset); /* Crystal clock: 13MHz */ #define BASE_CLK 13000000 diff --git a/sound/arm/pxa2xx-ac97-lib.c b/sound/arm/pxa2xx-ac97-lib.c index fff7753..e6f4633 100644 --- a/sound/arm/pxa2xx-ac97-lib.c +++ b/sound/arm/pxa2xx-ac97-lib.c @@ -34,7 +34,7 @@ static struct clk *ac97_clk; static struct clk *ac97conf_clk; static int reset_gpio; -extern void pxa27x_assert_ac97reset(int reset_gpio, int on); +extern void pxa27x_configure_ac97reset(int reset_gpio, bool to_gpio); /* * Beware PXA27x bugs: @@ -140,10 +140,10 @@ static inline void pxa_ac97_warm_pxa27x(void) gsr_bits = 0; /* warm reset broken on Bulverde, so manually keep AC97 reset high */ - pxa27x_assert_ac97reset(reset_gpio, 1); + pxa27x_configure_ac97reset(reset_gpio, true); udelay(10); GCR |= GCR_WARM_RST; - pxa27x_assert_ac97reset(reset_gpio, 0); + pxa27x_configure_ac97reset(reset_gpio, false); udelay(500); } @@ -358,7 +358,7 @@ int pxa2xx_ac97_hw_probe(struct platform_device *dev) __func__, ret); goto err_conf; } - pxa27x_assert_ac97reset(reset_gpio, 0); + pxa27x_configure_ac97reset(reset_gpio, false); ac97conf_clk = clk_get(&dev->dev, "AC97CONFCLK"); if (IS_ERR(ac97conf_clk)) { -- cgit v0.10.2 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 diff --git a/sound/pci/hda/hda_proc.c b/sound/pci/hda/hda_proc.c index 045e5d3..d6c8416 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 v0.10.2 From 324a7fb02b9e7a226916e3593f756cd85312bae8 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Tue, 8 Jan 2013 10:45:04 -0200 Subject: ASoC: mxs-saif: Use a signed integer for error value saif->id and saif->master_id are unsigned, so they can not be negative. Fix the following warning when building with W=1 option: sound/soc/mxs/mxs-saif.c: In function 'mxs_saif_probe': sound/soc/mxs/mxs-saif.c:676:2: warning: comparison of unsigned expression < 0 is always false [-Wtype-limits] sound/soc/mxs/mxs-saif.c:688:3: warning: comparison of unsigned expression < 0 is always false [-Wtype-limits] sound/soc/mxs/mxs-saif.c:692:2: warning: comparison of unsigned expression < 0 is always false [-Wtype-limits] Use a signed variable 'ret' to handle the error values. Signed-off-by: Fabio Estevam Signed-off-by: Mark Brown diff --git a/sound/soc/mxs/mxs-saif.c b/sound/soc/mxs/mxs-saif.c index 752675d..e70e6c8 100644 --- a/sound/soc/mxs/mxs-saif.c +++ b/sound/soc/mxs/mxs-saif.c @@ -672,9 +672,12 @@ static int mxs_saif_probe(struct platform_device *pdev) if (!saif) return -ENOMEM; - saif->id = of_alias_get_id(np, "saif"); - if (saif->id < 0) - return saif->id; + ret = of_alias_get_id(np, "saif"); + if (ret < 0) + return ret; + else + saif->id = ret; + /* * If there is no "fsl,saif-master" phandle, it's a saif * master. Otherwise, it's a slave and its phandle points @@ -684,12 +687,14 @@ static int mxs_saif_probe(struct platform_device *pdev) if (!master) { saif->master_id = saif->id; } else { - saif->master_id = of_alias_get_id(master, "saif"); - if (saif->master_id < 0) - return saif->master_id; + ret = of_alias_get_id(master, "saif"); + if (ret < 0) + return ret; + else + saif->master_id = ret; } - if (saif->master_id < 0 || saif->master_id >= ARRAY_SIZE(mxs_saif)) { + if (saif->master_id >= ARRAY_SIZE(mxs_saif)) { dev_err(&pdev->dev, "get wrong master id\n"); return -EINVAL; } -- cgit v0.10.2 From db40517c75e1a33a886c8cadfa2d95f0fe5f5f4c Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 26 Oct 2012 19:30:40 +0100 Subject: ASoC: wm_adsp: Add support for parsing algorithms ADSP devices report information on the algorithms loaded on them. Parse this data and use it to allow coefficients to be configured for specific algorithms. Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index ffc89fa..990403b 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -350,6 +350,141 @@ out: return ret; } +static int wm_adsp_setup_algs(struct wm_adsp *dsp) +{ + struct regmap *regmap = dsp->regmap; + struct wmfw_adsp1_id_hdr adsp1_id; + struct wmfw_adsp2_id_hdr adsp2_id; + struct wmfw_adsp1_alg_hdr *adsp1_alg; + struct wmfw_adsp2_alg_hdr *adsp2_alg; + void *alg; + const struct wm_adsp_region *mem; + unsigned int pos, term; + size_t algs; + __be32 val; + int i, ret; + + switch (dsp->type) { + case WMFW_ADSP1: + mem = wm_adsp_find_region(dsp, WMFW_ADSP1_DM); + break; + case WMFW_ADSP2: + mem = wm_adsp_find_region(dsp, WMFW_ADSP2_XM); + break; + default: + mem = NULL; + break; + } + + if (mem == NULL) { + BUG_ON(mem != NULL); + return -EINVAL; + } + + switch (dsp->type) { + case WMFW_ADSP1: + ret = regmap_raw_read(regmap, mem->base, &adsp1_id, + sizeof(adsp1_id)); + if (ret != 0) { + adsp_err(dsp, "Failed to read algorithm info: %d\n", + ret); + return ret; + } + + algs = be32_to_cpu(adsp1_id.algs); + adsp_info(dsp, "Firmware: %x v%d.%d.%d, %zu algorithms\n", + be32_to_cpu(adsp1_id.fw.id), + (be32_to_cpu(adsp1_id.fw.ver) & 0xff0000) >> 16, + (be32_to_cpu(adsp1_id.fw.ver) & 0xff00) >> 8, + be32_to_cpu(adsp1_id.fw.ver) & 0xff, + algs); + + pos = sizeof(adsp1_id) / 2; + term = pos + ((sizeof(*adsp1_alg) * algs) / 2); + break; + + case WMFW_ADSP2: + ret = regmap_raw_read(regmap, mem->base, &adsp2_id, + sizeof(adsp2_id)); + if (ret != 0) { + adsp_err(dsp, "Failed to read algorithm info: %d\n", + ret); + return ret; + } + + algs = be32_to_cpu(adsp2_id.algs); + adsp_info(dsp, "Firmware: %x v%d.%d.%d, %zu algorithms\n", + be32_to_cpu(adsp2_id.fw.id), + (be32_to_cpu(adsp2_id.fw.ver) & 0xff0000) >> 16, + (be32_to_cpu(adsp2_id.fw.ver) & 0xff00) >> 8, + be32_to_cpu(adsp2_id.fw.ver) & 0xff, + algs); + + pos = sizeof(adsp2_id) / 2; + term = pos + ((sizeof(*adsp2_alg) * algs) / 2); + break; + + default: + BUG_ON(NULL == "Unknown DSP type"); + return -EINVAL; + } + + if (algs == 0) { + adsp_err(dsp, "No algorithms\n"); + return -EINVAL; + } + + /* Read the terminator first to validate the length */ + ret = regmap_raw_read(regmap, mem->base + term, &val, sizeof(val)); + if (ret != 0) { + adsp_err(dsp, "Failed to read algorithm list end: %d\n", + ret); + return ret; + } + + if (be32_to_cpu(val) != 0xbedead) + adsp_warn(dsp, "Algorithm list end %x 0x%x != 0xbeadead\n", + term, be32_to_cpu(val)); + + alg = kzalloc((term - pos) * 2, GFP_KERNEL); + if (!alg) + return -ENOMEM; + + ret = regmap_raw_read(regmap, mem->base + pos, alg, (term - pos) * 2); + if (ret != 0) { + adsp_err(dsp, "Failed to read algorithm list: %d\n", + ret); + goto out; + } + + adsp1_alg = alg; + adsp2_alg = alg; + + for (i = 0; i < algs; i++) { + switch (dsp->type) { + case WMFW_ADSP1: + adsp_info(dsp, "%d: ID %x v%d.%d.%d\n", + i, be32_to_cpu(adsp1_alg[i].alg.id), + (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff0000) >> 16, + (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff00) >> 8, + be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff); + break; + + case WMFW_ADSP2: + adsp_info(dsp, "%d: ID %x v%d.%d.%d\n", + i, be32_to_cpu(adsp2_alg[i].alg.id), + (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff0000) >> 16, + (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff00) >> 8, + be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff); + break; + } + } + +out: + kfree(alg); + return ret; +} + static int wm_adsp_load_coeff(struct wm_adsp *dsp) { struct regmap *regmap = dsp->regmap; @@ -468,6 +603,10 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w, if (ret != 0) goto err; + ret = wm_adsp_setup_algs(dsp); + if (ret != 0) + goto err; + ret = wm_adsp_load_coeff(dsp); if (ret != 0) goto err; @@ -604,6 +743,10 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w, if (ret != 0) goto err; + ret = wm_adsp_setup_algs(dsp); + if (ret != 0) + goto err; + ret = wm_adsp_load_coeff(dsp); if (ret != 0) goto err; -- cgit v0.10.2 From 45b9ee72d0e70c11a01152ef4bec92718b55906b Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 8 Jan 2013 16:02:06 +0000 Subject: ASoC: wm_adsp: Factor out calculation of memory base addresses Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index 990403b..bcc0d0f 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -156,6 +156,26 @@ static struct wm_adsp_region const *wm_adsp_find_region(struct wm_adsp *dsp, return NULL; } +static unsigned int wm_adsp_region_to_reg(struct wm_adsp_region const *region, + unsigned int offset) +{ + switch (region->type) { + case WMFW_ADSP1_PM: + return region->base + (offset * 3); + case WMFW_ADSP1_DM: + return region->base + (offset * 2); + case WMFW_ADSP2_XM: + return region->base + (offset * 2); + case WMFW_ADSP2_YM: + return region->base + (offset * 2); + case WMFW_ADSP1_ZM: + return region->base + (offset * 2); + default: + WARN_ON(NULL != "Unknown memory region type"); + return offset; + } +} + static int wm_adsp_load(struct wm_adsp *dsp) { const struct firmware *firmware; @@ -282,27 +302,27 @@ static int wm_adsp_load(struct wm_adsp *dsp) case WMFW_ADSP1_PM: BUG_ON(!mem); region_name = "PM"; - reg = mem->base + (offset * 3); + reg = wm_adsp_region_to_reg(mem, offset); break; case WMFW_ADSP1_DM: BUG_ON(!mem); region_name = "DM"; - reg = mem->base + (offset * 2); + reg = wm_adsp_region_to_reg(mem, offset); break; case WMFW_ADSP2_XM: BUG_ON(!mem); region_name = "XM"; - reg = mem->base + (offset * 2); + reg = wm_adsp_region_to_reg(mem, offset); break; case WMFW_ADSP2_YM: BUG_ON(!mem); region_name = "YM"; - reg = mem->base + (offset * 2); + reg = wm_adsp_region_to_reg(mem, offset); break; case WMFW_ADSP1_ZM: BUG_ON(!mem); region_name = "ZM"; - reg = mem->base + (offset * 2); + reg = wm_adsp_region_to_reg(mem, offset); break; default: adsp_warn(dsp, -- cgit v0.10.2 From d62f4bc665f3efd2a3d20134f874608b8afce7a1 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 19 Dec 2012 14:00:30 +0000 Subject: ASoC: wm_asdp: Validate sanity of algorithm count If we run into I/O problems the algorithm count may be crazy, validate it before we proceed and dump the read data for diagnostic purposes. Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index bcc0d0f..017d510 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -377,10 +377,10 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp) struct wmfw_adsp2_id_hdr adsp2_id; struct wmfw_adsp1_alg_hdr *adsp1_alg; struct wmfw_adsp2_alg_hdr *adsp2_alg; - void *alg; + void *alg, *buf; const struct wm_adsp_region *mem; unsigned int pos, term; - size_t algs; + size_t algs, buf_size; __be32 val; int i, ret; @@ -411,6 +411,9 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp) return ret; } + buf = &adsp1_id; + buf_size = sizeof(adsp1_id); + algs = be32_to_cpu(adsp1_id.algs); adsp_info(dsp, "Firmware: %x v%d.%d.%d, %zu algorithms\n", be32_to_cpu(adsp1_id.fw.id), @@ -432,6 +435,9 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp) return ret; } + buf = &adsp2_id; + buf_size = sizeof(adsp2_id); + algs = be32_to_cpu(adsp2_id.algs); adsp_info(dsp, "Firmware: %x v%d.%d.%d, %zu algorithms\n", be32_to_cpu(adsp2_id.fw.id), @@ -454,6 +460,13 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp) return -EINVAL; } + if (algs > 1024) { + adsp_err(dsp, "Algorithm count %zx excessive\n", algs); + print_hex_dump_bytes(dev_name(dsp->dev), DUMP_PREFIX_OFFSET, + buf, buf_size); + return -EINVAL; + } + /* Read the terminator first to validate the length */ ret = regmap_raw_read(regmap, mem->base + term, &val, sizeof(val)); if (ret != 0) { -- cgit v0.10.2 From 471f488583c62e4daca7d24fc7c937a39de7d95f Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 8 Jan 2013 16:09:31 +0000 Subject: ASoC: wm_adsp: Implement support for algorithm-specific coefficient blocks WMDR coefficient files can specify coefficients in terms of algorithm specific data regions. Record the start addresses of these regions while parsing the algorithms and then use them to handle coefficients with these formats. Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index 017d510..5015ff2 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -378,6 +378,7 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp) struct wmfw_adsp1_alg_hdr *adsp1_alg; struct wmfw_adsp2_alg_hdr *adsp2_alg; void *alg, *buf; + struct wm_adsp_alg_region *region; const struct wm_adsp_region *mem; unsigned int pos, term; size_t algs, buf_size; @@ -496,19 +497,80 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp) for (i = 0; i < algs; i++) { switch (dsp->type) { case WMFW_ADSP1: - adsp_info(dsp, "%d: ID %x v%d.%d.%d\n", + adsp_info(dsp, "%d: ID %x v%d.%d.%d DM@%x ZM@%x\n", i, be32_to_cpu(adsp1_alg[i].alg.id), (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff0000) >> 16, (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff00) >> 8, - be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff); + be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff, + be32_to_cpu(adsp1_alg[i].dm), + be32_to_cpu(adsp1_alg[i].zm)); + + if (adsp1_alg[i].dm) { + region = kzalloc(sizeof(*region), GFP_KERNEL); + if (!region) + return -ENOMEM; + region->type = WMFW_ADSP1_DM; + region->alg = be32_to_cpu(adsp1_alg[i].alg.id); + region->base = be32_to_cpu(adsp1_alg[i].dm); + list_add_tail(®ion->list, + &dsp->alg_regions); + } + + if (adsp1_alg[i].zm) { + region = kzalloc(sizeof(*region), GFP_KERNEL); + if (!region) + return -ENOMEM; + region->type = WMFW_ADSP1_ZM; + region->alg = be32_to_cpu(adsp1_alg[i].alg.id); + region->base = be32_to_cpu(adsp1_alg[i].zm); + list_add_tail(®ion->list, + &dsp->alg_regions); + } break; case WMFW_ADSP2: - adsp_info(dsp, "%d: ID %x v%d.%d.%d\n", + adsp_info(dsp, + "%d: ID %x v%d.%d.%d XM@%x YM@%x ZM@%x\n", i, be32_to_cpu(adsp2_alg[i].alg.id), (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff0000) >> 16, (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff00) >> 8, - be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff); + be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff, + be32_to_cpu(adsp2_alg[i].xm), + be32_to_cpu(adsp2_alg[i].ym), + be32_to_cpu(adsp2_alg[i].zm)); + + if (adsp2_alg[i].xm) { + region = kzalloc(sizeof(*region), GFP_KERNEL); + if (!region) + return -ENOMEM; + region->type = WMFW_ADSP2_XM; + region->alg = be32_to_cpu(adsp2_alg[i].alg.id); + region->base = be32_to_cpu(adsp2_alg[i].xm); + list_add_tail(®ion->list, + &dsp->alg_regions); + } + + if (adsp2_alg[i].ym) { + region = kzalloc(sizeof(*region), GFP_KERNEL); + if (!region) + return -ENOMEM; + region->type = WMFW_ADSP2_YM; + region->alg = be32_to_cpu(adsp2_alg[i].alg.id); + region->base = be32_to_cpu(adsp2_alg[i].ym); + list_add_tail(®ion->list, + &dsp->alg_regions); + } + + if (adsp2_alg[i].zm) { + region = kzalloc(sizeof(*region), GFP_KERNEL); + if (!region) + return -ENOMEM; + region->type = WMFW_ADSP2_ZM; + region->alg = be32_to_cpu(adsp2_alg[i].alg.id); + region->base = be32_to_cpu(adsp2_alg[i].zm); + list_add_tail(®ion->list, + &dsp->alg_regions); + } break; } } @@ -524,6 +586,8 @@ static int wm_adsp_load_coeff(struct wm_adsp *dsp) struct wmfw_coeff_hdr *hdr; struct wmfw_coeff_item *blk; const struct firmware *firmware; + const struct wm_adsp_region *mem; + struct wm_adsp_alg_region *alg_region; const char *region_name; int ret, pos, blocks, type, offset, reg; char *file; @@ -588,6 +652,37 @@ static int wm_adsp_load_coeff(struct wm_adsp *dsp) region_name = "register"; reg = offset; break; + + case WMFW_ADSP1_DM: + case WMFW_ADSP1_ZM: + case WMFW_ADSP2_XM: + case WMFW_ADSP2_YM: + adsp_dbg(dsp, "%s.%d: %d bytes in %x for %x\n", + file, blocks, le32_to_cpu(blk->len), + type, le32_to_cpu(blk->id)); + + mem = wm_adsp_find_region(dsp, type); + if (!mem) { + adsp_err(dsp, "No base for region %x\n", type); + break; + } + + reg = 0; + list_for_each_entry(alg_region, + &dsp->alg_regions, list) { + if (le32_to_cpu(blk->id) == alg_region->alg && + type == alg_region->type) { + reg = alg_region->base + offset; + reg = wm_adsp_region_to_reg(mem, + reg); + } + } + + if (reg == 0) + adsp_err(dsp, "No %x for algorithm %x\n", + type, le32_to_cpu(blk->id)); + break; + default: adsp_err(dsp, "Unknown region type %x\n", type); break; @@ -711,6 +806,7 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w, struct snd_soc_codec *codec = w->codec; struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec); struct wm_adsp *dsp = &dsps[w->shift]; + struct wm_adsp_alg_region *alg_region; unsigned int val; int ret; @@ -811,6 +907,14 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w, "Failed to enable supply: %d\n", ret); } + + while (!list_empty(&dsp->alg_regions)) { + alg_region = list_first_entry(&dsp->alg_regions, + struct wm_adsp_alg_region, + list); + list_del(&alg_region->list); + kfree(alg_region); + } break; default: @@ -840,6 +944,8 @@ int wm_adsp2_init(struct wm_adsp *adsp, bool dvfs) return ret; } + INIT_LIST_HEAD(&adsp->alg_regions); + if (dvfs) { adsp->dvfs = devm_regulator_get(adsp->dev, "DCVDD"); if (IS_ERR(adsp->dvfs)) { diff --git a/sound/soc/codecs/wm_adsp.h b/sound/soc/codecs/wm_adsp.h index ffd29a4..4881419 100644 --- a/sound/soc/codecs/wm_adsp.h +++ b/sound/soc/codecs/wm_adsp.h @@ -25,6 +25,13 @@ struct wm_adsp_region { unsigned int base; }; +struct wm_adsp_alg_region { + struct list_head list; + unsigned int alg; + int type; + unsigned int base; +}; + struct wm_adsp { const char *part; int num; @@ -34,6 +41,8 @@ struct wm_adsp { int base; + struct list_head alg_regions; + const struct wm_adsp_region *mem; int num_mems; -- cgit v0.10.2 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 diff --git a/Documentation/sound/alsa/ALSA-Configuration.txt b/Documentation/sound/alsa/ALSA-Configuration.txt index b9cfd33..ce6581c 100644 --- a/Documentation/sound/alsa/ALSA-Configuration.txt +++ b/Documentation/sound/alsa/ALSA-Configuration.txt @@ -890,8 +890,9 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed. enable_msi - Enable Message Signaled Interrupt (MSI) (default = off) power_save - Automatic power-saving timeout (in second, 0 = disable) - power_save_controller - Reset HD-audio controller in power-saving mode - (default = on) + power_save_controller - Support runtime D3 of HD-audio controller + (-1 = on for supported chip (default), false = off, + true = force to on even for unsupported hardware) align_buffer_size - Force rounding of buffer/period sizes to multiples of 128 bytes. This is more efficient in terms of memory access but isn't required by the HDA spec and prevents diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index cca8727..9883235 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 v0.10.2 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 diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index f519627..ca07740 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 v0.10.2 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 diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index ca07740..8b768a5 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 v0.10.2 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 diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 8b768a5..1f178d6 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 v0.10.2 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 diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 1f178d6..cacfbc0 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_proc.c b/sound/pci/hda/hda_proc.c index 045e5d3..740f46a 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 v0.10.2 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 diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index cacfbc0..870ac58 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 v0.10.2 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 diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 870ac58..f893fb1 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 v0.10.2 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 diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index f893fb1..4348563 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 v0.10.2 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 diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 4348563..fbc4a97 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 v0.10.2 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 diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index fbc4a97..8ce0a0d 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 v0.10.2 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 diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 8ce0a0d..7a22ab0 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 v0.10.2 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 diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 7a22ab0..18c4a78 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 v0.10.2 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 diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index 89fc503..308a5b9 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 v0.10.2 From bbf1453e28e4e3ee2cf5a0c34a20469b4d465f0f Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 10 Jan 2013 00:29:11 -0800 Subject: ASoC: ak4642: add Device Tree support Support for loading the ak4642 codec module via devicetree. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown diff --git a/Documentation/devicetree/bindings/sound/ak4642.txt b/Documentation/devicetree/bindings/sound/ak4642.txt new file mode 100644 index 0000000..623d4e7 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/ak4642.txt @@ -0,0 +1,17 @@ +AK4642 I2C transmitter + +This device supports I2C mode only. + +Required properties: + + - compatible : "asahi-kasei,ak4642" or "asahi-kasei,ak4643" or "asahi-kasei,ak4648" + - reg : The chip select number on the I2C bus + +Example: + +&i2c { + ak4648: ak4648@0x12 { + compatible = "asahi-kasei,ak4642"; + reg = <0x12>; + }; +}; diff --git a/sound/soc/codecs/ak4642.c b/sound/soc/codecs/ak4642.c index 1f0cdab..c78794d 100644 --- a/sound/soc/codecs/ak4642.c +++ b/sound/soc/codecs/ak4642.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -513,12 +514,31 @@ static struct snd_soc_codec_driver soc_codec_dev_ak4648 = { }; #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +static struct of_device_id ak4642_of_match[]; static int ak4642_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { + struct device_node *np = i2c->dev.of_node; + const struct snd_soc_codec_driver *driver; + + driver = NULL; + if (np) { + const struct of_device_id *of_id; + + of_id = of_match_device(ak4642_of_match, &i2c->dev); + if (of_id) + driver = of_id->data; + } else { + driver = (struct snd_soc_codec_driver *)id->driver_data; + } + + if (!driver) { + dev_err(&i2c->dev, "no driver\n"); + return -EINVAL; + } + return snd_soc_register_codec(&i2c->dev, - (struct snd_soc_codec_driver *)id->driver_data, - &ak4642_dai, 1); + driver, &ak4642_dai, 1); } static int ak4642_i2c_remove(struct i2c_client *client) @@ -527,6 +547,14 @@ static int ak4642_i2c_remove(struct i2c_client *client) return 0; } +static struct of_device_id ak4642_of_match[] __devinitconst = { + { .compatible = "asahi-kasei,ak4642", .data = &soc_codec_dev_ak4642}, + { .compatible = "asahi-kasei,ak4643", .data = &soc_codec_dev_ak4642}, + { .compatible = "asahi-kasei,ak4648", .data = &soc_codec_dev_ak4648}, + {}, +}; +MODULE_DEVICE_TABLE(of, ak4642_of_match); + static const struct i2c_device_id ak4642_i2c_id[] = { { "ak4642", (kernel_ulong_t)&soc_codec_dev_ak4642 }, { "ak4643", (kernel_ulong_t)&soc_codec_dev_ak4642 }, @@ -539,6 +567,7 @@ static struct i2c_driver ak4642_i2c_driver = { .driver = { .name = "ak4642-codec", .owner = THIS_MODULE, + .of_match_table = ak4642_of_match, }, .probe = ak4642_i2c_probe, .remove = ak4642_i2c_remove, -- cgit v0.10.2 From 8d61f4901f83461e1f04df7743777e9db5f541aa Mon Sep 17 00:00:00 2001 From: Misael Lopez Cruz Date: Fri, 11 Jan 2013 17:00:59 +0100 Subject: ASoC: twl6040: Convert PLUGINT to no-suspend irq Convert headset PLUGINT interrupt to NO_SUSPEND type in order to allow handling of insertion/removal events while device is suspended. Signed-off-by: Misael Lopez Cruz Signed-off-by: Peter Ujfalusi Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/twl6040.c b/sound/soc/codecs/twl6040.c index 3fc3fc6..ef31ace 100644 --- a/sound/soc/codecs/twl6040.c +++ b/sound/soc/codecs/twl6040.c @@ -1174,7 +1174,7 @@ static int twl6040_probe(struct snd_soc_codec *codec) mutex_init(&priv->mutex); ret = request_threaded_irq(priv->plug_irq, NULL, twl6040_audio_handler, - 0, "twl6040_irq_plug", codec); + IRQF_NO_SUSPEND, "twl6040_irq_plug", codec); if (ret) { dev_err(codec->dev, "PLUG IRQ request failed: %d\n", ret); goto plugirq_err; -- cgit v0.10.2 From 09a8b6719caecff6c08d505c1f7126b7be68663a Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 11 Jan 2013 17:01:41 +0100 Subject: ASoC: twl4030: Remove suspend/resume soc driver operations With idle_bias_off these are no longer needed. Signed-off-by: Peter Ujfalusi Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c index 7bfabe5..8e6e5b0 100644 --- a/sound/soc/codecs/twl4030.c +++ b/sound/soc/codecs/twl4030.c @@ -2296,18 +2296,6 @@ static struct snd_soc_dai_driver twl4030_dai[] = { }, }; -static int twl4030_soc_suspend(struct snd_soc_codec *codec) -{ - twl4030_set_bias_level(codec, SND_SOC_BIAS_OFF); - return 0; -} - -static int twl4030_soc_resume(struct snd_soc_codec *codec) -{ - twl4030_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - return 0; -} - static int twl4030_soc_probe(struct snd_soc_codec *codec) { struct twl4030_priv *twl4030; @@ -2345,8 +2333,6 @@ static int twl4030_soc_remove(struct snd_soc_codec *codec) static struct snd_soc_codec_driver soc_codec_dev_twl4030 = { .probe = twl4030_soc_probe, .remove = twl4030_soc_remove, - .suspend = twl4030_soc_suspend, - .resume = twl4030_soc_resume, .read = twl4030_read_reg_cache, .write = twl4030_write, .set_bias_level = twl4030_set_bias_level, -- cgit v0.10.2 From 156db9f3bb3c210cdf905172f6063c90c4a62c3c Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 11 Jan 2013 17:01:00 +0100 Subject: ASoC: twl6040: Only set the bias_level once in twl6040_resume() No need to set the bias_level twice to _STANDBY - since this is the only state the device could be at suspend time. The driver do not support idle_bias_off yet. Signed-off-by: Peter Ujfalusi Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/twl6040.c b/sound/soc/codecs/twl6040.c index ef31ace..86f12a4 100644 --- a/sound/soc/codecs/twl6040.c +++ b/sound/soc/codecs/twl6040.c @@ -1115,7 +1115,6 @@ static int twl6040_suspend(struct snd_soc_codec *codec) static int twl6040_resume(struct snd_soc_codec *codec) { twl6040_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - twl6040_set_bias_level(codec, codec->dapm.suspend_bias_level); return 0; } -- cgit v0.10.2 From 9523fcdcc02e812f3a0f4849b3af1b295ad50470 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 11 Jan 2013 17:01:01 +0100 Subject: ASoC: twl6040: Convert to use devm_* when possible In this way we can clean up the probe and remove paths Signed-off-by: Peter Ujfalusi Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/twl6040.c b/sound/soc/codecs/twl6040.c index 86f12a4..90b721e 100644 --- a/sound/soc/codecs/twl6040.c +++ b/sound/soc/codecs/twl6040.c @@ -1131,9 +1131,10 @@ static int twl6040_probe(struct snd_soc_codec *codec) struct platform_device, dev); int ret = 0; - priv = kzalloc(sizeof(struct twl6040_data), GFP_KERNEL); + priv = devm_kzalloc(codec->dev, sizeof(*priv), GFP_KERNEL); if (priv == NULL) return -ENOMEM; + snd_soc_codec_set_drvdata(codec, priv); priv->codec = codec; @@ -1158,25 +1159,23 @@ static int twl6040_probe(struct snd_soc_codec *codec) priv->plug_irq = platform_get_irq(pdev, 0); if (priv->plug_irq < 0) { dev_err(codec->dev, "invalid irq\n"); - ret = -EINVAL; - goto work_err; + return -EINVAL; } priv->workqueue = alloc_workqueue("twl6040-codec", 0, 0); - if (!priv->workqueue) { - ret = -ENOMEM; - goto work_err; - } + if (!priv->workqueue) + return -ENOMEM; INIT_DELAYED_WORK(&priv->hs_jack.work, twl6040_accessory_work); mutex_init(&priv->mutex); - ret = request_threaded_irq(priv->plug_irq, NULL, twl6040_audio_handler, - IRQF_NO_SUSPEND, "twl6040_irq_plug", codec); + ret = devm_request_threaded_irq(codec->dev, priv->plug_irq, NULL, + twl6040_audio_handler, IRQF_NO_SUSPEND, + "twl6040_irq_plug", codec); if (ret) { dev_err(codec->dev, "PLUG IRQ request failed: %d\n", ret); - goto plugirq_err; + goto err; } twl6040_init_chip(codec); @@ -1186,12 +1185,8 @@ static int twl6040_probe(struct snd_soc_codec *codec) if (!ret) return 0; - /* Error path */ - free_irq(priv->plug_irq, codec); -plugirq_err: +err: destroy_workqueue(priv->workqueue); -work_err: - kfree(priv); return ret; } @@ -1200,9 +1195,7 @@ static int twl6040_remove(struct snd_soc_codec *codec) struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); twl6040_set_bias_level(codec, SND_SOC_BIAS_OFF); - free_irq(priv->plug_irq, codec); destroy_workqueue(priv->workqueue); - kfree(priv); return 0; } -- cgit v0.10.2 From da2107d1e464811407675880e9ab78f057b7c9d6 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 11 Jan 2013 17:01:02 +0100 Subject: ASoC: twl6040: Switch to use system workqueue for jack reporting There's no need to create a queue for this anymore Signed-off-by: Peter Ujfalusi Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/twl6040.c b/sound/soc/codecs/twl6040.c index 90b721e..984911b 100644 --- a/sound/soc/codecs/twl6040.c +++ b/sound/soc/codecs/twl6040.c @@ -75,7 +75,6 @@ struct twl6040_data { u16 hf_right_step; struct twl6040_jack_data hs_jack; struct snd_soc_codec *codec; - struct workqueue_struct *workqueue; struct mutex mutex; }; @@ -404,8 +403,7 @@ static irqreturn_t twl6040_audio_handler(int irq, void *data) struct snd_soc_codec *codec = data; struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); - queue_delayed_work(priv->workqueue, &priv->hs_jack.work, - msecs_to_jiffies(200)); + schedule_delayed_work(&priv->hs_jack.work, msecs_to_jiffies(200)); return IRQ_HANDLED; } @@ -1162,10 +1160,6 @@ static int twl6040_probe(struct snd_soc_codec *codec) return -EINVAL; } - priv->workqueue = alloc_workqueue("twl6040-codec", 0, 0); - if (!priv->workqueue) - return -ENOMEM; - INIT_DELAYED_WORK(&priv->hs_jack.work, twl6040_accessory_work); mutex_init(&priv->mutex); @@ -1175,27 +1169,18 @@ static int twl6040_probe(struct snd_soc_codec *codec) "twl6040_irq_plug", codec); if (ret) { dev_err(codec->dev, "PLUG IRQ request failed: %d\n", ret); - goto err; + return ret; } twl6040_init_chip(codec); /* power on device */ - ret = twl6040_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - if (!ret) - return 0; - -err: - destroy_workqueue(priv->workqueue); - return ret; + return twl6040_set_bias_level(codec, SND_SOC_BIAS_STANDBY); } static int twl6040_remove(struct snd_soc_codec *codec) { - struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); - twl6040_set_bias_level(codec, SND_SOC_BIAS_OFF); - destroy_workqueue(priv->workqueue); return 0; } -- cgit v0.10.2 From 85becda62c77d0951a1079bb45d0b5cbe6012252 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 11 Jan 2013 17:01:03 +0100 Subject: ASoC: twl6040: Remove leftover code from hs/hf ramp implementation The code to do the ramp has been removed a long time ago. Remove the remaining code as well since this is not needed. Signed-off-by: Peter Ujfalusi Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/twl6040.c b/sound/soc/codecs/twl6040.c index 984911b..9b9a6e5 100644 --- a/sound/soc/codecs/twl6040.c +++ b/sound/soc/codecs/twl6040.c @@ -69,10 +69,6 @@ struct twl6040_data { int hs_power_mode_locked; unsigned int clk_in; unsigned int sysclk; - u16 hs_left_step; - u16 hs_right_step; - u16 hf_left_step; - u16 hf_right_step; struct twl6040_jack_data hs_jack; struct snd_soc_codec *codec; struct mutex mutex; @@ -1124,7 +1120,6 @@ static int twl6040_resume(struct snd_soc_codec *codec) static int twl6040_probe(struct snd_soc_codec *codec) { struct twl6040_data *priv; - struct twl6040_codec_data *pdata = dev_get_platdata(codec->dev); struct platform_device *pdev = container_of(codec->dev, struct platform_device, dev); int ret = 0; @@ -1138,22 +1133,6 @@ static int twl6040_probe(struct snd_soc_codec *codec) priv->codec = codec; codec->control_data = dev_get_drvdata(codec->dev->parent); - if (pdata && pdata->hs_left_step && pdata->hs_right_step) { - priv->hs_left_step = pdata->hs_left_step; - priv->hs_right_step = pdata->hs_right_step; - } else { - priv->hs_left_step = 1; - priv->hs_right_step = 1; - } - - if (pdata && pdata->hf_left_step && pdata->hf_right_step) { - priv->hf_left_step = pdata->hf_left_step; - priv->hf_right_step = pdata->hf_right_step; - } else { - priv->hf_left_step = 1; - priv->hf_right_step = 1; - } - priv->plug_irq = platform_get_irq(pdev, 0); if (priv->plug_irq < 0) { dev_err(codec->dev, "invalid irq\n"); -- cgit v0.10.2 From a88fedfd342badff3653edcc6b6423e892d1773f Mon Sep 17 00:00:00 2001 From: Ricardo Neri Date: Thu, 10 Jan 2013 18:32:13 -0600 Subject: ASoC: OMAP: HDMI: Initialize IEC-60958 channel status word As the IEC-60958 channel status word is set by ANDing and ORing with the appropriate definitions, the word bytes need to be initialized to zero to avoid misconfiguration due to previous hw_params calls. Signed-off-by: Ricardo Neri Signed-off-by: Mark Brown diff --git a/sound/soc/omap/omap-hdmi.c b/sound/soc/omap/omap-hdmi.c index 7ea2481..32fa840 100644 --- a/sound/soc/omap/omap-hdmi.c +++ b/sound/soc/omap/omap-hdmi.c @@ -110,6 +110,8 @@ static int omap_hdmi_dai_hw_params(struct snd_pcm_substream *substream, /* * fill the IEC-60958 channel status word */ + /* initialize the word bytes */ + memset(iec->status, 0, sizeof(iec->status)); /* specify IEC-60958-3 (commercial use) */ iec->status[0] &= ~IEC958_AES0_PROFESSIONAL; -- cgit v0.10.2 From fffc0ca29fdf3a786e74082c698c701d6ebdf720 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 10 Jan 2013 11:59:57 +0300 Subject: ASoC: pcm: delete some dead code I've removed several unreachable returns. Signed-off-by: Dan Carpenter Signed-off-by: Mark Brown diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index d7711fc..6006b37 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -1728,20 +1728,16 @@ static int dpcm_run_update_startup(struct snd_soc_pcm_runtime *fe, int stream) /* startup must always be called for new BEs */ ret = dpcm_be_dai_startup(fe, stream); - if (ret < 0) { + if (ret < 0) goto disconnect; - return ret; - } /* keep going if FE state is > open */ if (fe->dpcm[stream].state == SND_SOC_DPCM_STATE_OPEN) return 0; ret = dpcm_be_dai_hw_params(fe, stream); - if (ret < 0) { + if (ret < 0) goto close; - return ret; - } /* keep going if FE state is > hw_params */ if (fe->dpcm[stream].state == SND_SOC_DPCM_STATE_HW_PARAMS) @@ -1749,10 +1745,8 @@ static int dpcm_run_update_startup(struct snd_soc_pcm_runtime *fe, int stream) ret = dpcm_be_dai_prepare(fe, stream); - if (ret < 0) { + if (ret < 0) goto hw_free; - return ret; - } /* run the stream event for each BE */ dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_NOP); -- cgit v0.10.2 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 diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index b8fb0a5..2f890af 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 8665540..cab39b2 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 4b40a5e..f765296 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 2f890af..0037147 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 f765296..e38519b 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 v0.10.2 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 diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 18c4a78..9046f1c 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index e38519b..ff56da8 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 9046f1c..269d41a 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 v0.10.2 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 diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 269d41a..04713a8 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 v0.10.2 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 diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 04713a8..9a38107 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 v0.10.2 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 diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 9a38107..e25b13a 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 v0.10.2 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 diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index e25b13a..76c6740 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 v0.10.2 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 diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 76c6740..b6b929e 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 v0.10.2 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 diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index b6b929e..74251b9 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 v0.10.2 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 diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 74251b9..9026b60 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 v0.10.2 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 diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 9026b60..d5181b0 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 v0.10.2 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 diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index d5181b0..1ec14ac 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 v0.10.2 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 diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 1ec14ac..3ee2be5 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 v0.10.2 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 diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 3ee2be5..4fb815c 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 v0.10.2 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 diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 4fb815c..954f4a8 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 v0.10.2 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 diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 954f4a8..d9c3b4a 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 v0.10.2 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 diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index d9c3b4a..0d6e9d8 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 v0.10.2 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 diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 0d6e9d8..c6cc833 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 v0.10.2 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 diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index c6cc833..e3fe735 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 v0.10.2 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 diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index e3fe735..f10018b 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 v0.10.2 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 diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index f10018b..a304614 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 v0.10.2 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 diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index a304614..f21c53d 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 v0.10.2 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 diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index f21c53d..25c0fc9 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 v0.10.2 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 diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 25c0fc9..13d4548 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 v0.10.2 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 diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 13d4548..6fb3922 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 v0.10.2 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 diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 6fb3922..fbdcbde 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_auto_parser.c b/sound/pci/hda/hda_auto_parser.c index 7da883a..d460688 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 632ad0a..ff11074 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 0037147..e7749de 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 cab39b2..a1cb28f 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 ff56da8..de12dcc 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -386,6 +386,59 @@ 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 a2537b2..7b0b8c3 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 dd798c3..a52f566 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 fbdcbde..567d93f 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 09bb649..b224b3d 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 v0.10.2 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 diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 567d93f..9218d30 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_auto_parser.c b/sound/pci/hda/hda_auto_parser.c index d460688..44c81d3 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 ff11074..f748071 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 b224b3d..d3c852a 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 v0.10.2 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 diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 9218d30..f210ca7 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 v0.10.2 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 diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index f210ca7..c85899d 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index b81d3d0..2d19b91 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 0000000..a9f4f63 --- /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 v0.10.2 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 diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 2d19b91..31c5677 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 a9f4f63..9c00bd5 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 31c5677..49e968c 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 9c00bd5..d71e86d 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 49e968c..e512cab 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 d71e86d..1a3b404 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index e512cab..364ec06 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 364ec06..6914d70d 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 1a3b404..417ab65 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 v0.10.2 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 diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig index 6eeb889..ebec1b7 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 c85899d..896bc2c 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 6914d70d..f8d1d03 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index e7749de..ef0a0ee 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index ef0a0ee..23c8214 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 23c8214..3207e5c 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 3207e5c..afc3ccd 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 0b6aeba..0430436 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 v0.10.2 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 diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 896bc2c..caf6fa5 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index a1cb28f..2d9a51c 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 de12dcc..fec0e2d 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 caf6fa5..a654757 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index f8d1d03..3e818b6 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 3e818b6..f5eb57c 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index f5eb57c..6fb454e 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index afc3ccd..febadc9 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index febadc9..5689393 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 5689393..f4a0f9d 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 417ab65..89ad877 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h index 89ad877..b598899 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 6fb454e..a5c4bc0 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 b598899..a406cd4 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 a654757..fee2162 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index a5c4bc0..b7b8d7e 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index b7b8d7e..96c779b 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 96c779b..a133fcf 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index a133fcf..46b00e0 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 46b00e0..f4b5043 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index f4b5043..d4cb9df6 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index d4cb9df6..43acf3d 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 a406cd4..6365140 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 43acf3d..8af5324 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 8af5324..b341450 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index b341450..18b5fae 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 6365140..85d138f 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 18b5fae..f3c6ace 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index f3c6ace..ff15aea 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); @@ -1485,6 +1486,79 @@ static int create_speaker_out_ctls(struct hda_codec *codec) } /* + * 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 */ @@ -2905,6 +2979,9 @@ int snd_hda_gen_parse_auto_config(struct hda_codec *codec, 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); if (err < 0) return err; @@ -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 85d138f..5c1569c 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index 2d9a51c..369ffaf 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 ff15aea..f5bc185 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index 369ffaf..9f241d1 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 6e9f57b..6479b65 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index f4a0f9d..7eab3ae 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) { @@ -361,6 +392,49 @@ static int read_and_add_raw_conns(struct hda_codec *codec, hda_nid_t nid) } /** + * 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 * @nid: NID to parse @@ -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 9f241d1..93ec747 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 f5bc185..88f166e 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 88f166e..6ff4a0d 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 5c1569c..1090a52 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 6ff4a0d..9228175 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 9228175..3507448 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 3507448..ee2c973 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index ee2c973..f9ecbe0 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 1090a52..f1cae2e 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index f9ecbe0..f9d3ea3 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index f9d3ea3..93db021 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 93db021..c8bf812 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 f1cae2e..71d409f 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index c8bf812..4e49c5e 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 71d409f..ba8de12 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 4e49c5e..e7574a8 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index e7574a8..a34c581 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 */ @@ -1730,6 +1759,80 @@ static int create_multi_channel_mode(struct hda_codec *codec) } /* + * 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 */ @@ -3067,6 +3170,9 @@ int snd_hda_gen_parse_auto_config(struct hda_codec *codec, 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); if (err < 0) return err; diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h index ba8de12..d4a8f6c 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index a34c581..0588dd6 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 0588dd6..f4fa604 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index f4fa604..1a1e8e2 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 1a1e8e2..bebc3f4 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index bebc3f4..296628b 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 d4a8f6c..4c0d9ad 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 296628b..28ad5e9 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 4c0d9ad..7e84c22 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_jack.c b/sound/pci/hda/hda_jack.c index 6479b65..1d035ef 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 28ad5e9..1fb3197 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 7e84c22..00a1eab 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 fee2162..bcb258b 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 1fb3197..c20df57 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index c20df57..11436c1 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 11436c1..1b8fd4d 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 1b8fd4d..3f9439c 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 3f9439c..1fbc1b3 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 7eab3ae..733bce6 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 1fbc1b3..afa54f8 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 00a1eab..b65769c 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index afa54f8..7861c3a 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 b65769c..1ad9127 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 7861c3a..8e7ce7d 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 1ad9127..343195c 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_auto_parser.c b/sound/pci/hda/hda_auto_parser.c index 44c81d3..6a01c01 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 8e7ce7d..b488c62 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 343195c..1763e33 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index b488c62..f07b326 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_auto_parser.c b/sound/pci/hda/hda_auto_parser.c index 6a01c01..e5b20219 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 f07b326..aa4e639 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index aa4e639..d16ef1d 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index d16ef1d..26e8d83 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 1763e33..89683c7 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 26e8d83..a9bf188 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 733bce6..505cb72 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 93ec747..4c4f166 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 fec0e2d..655a40f 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 505cb72..0a531f2 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 655a40f..aa721aa 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index a9bf188..e786f10 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -84,6 +84,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 v0.10.2 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 diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index e786f10..2020faf 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 v0.10.2 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 diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index bcb258b..183b951 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 v0.10.2 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 diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 183b951..7a4b783 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_auto_parser.c b/sound/pci/hda/hda_auto_parser.c index e5b20219..55ed857 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 aa721aa..c09440d 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 7a4b783..c8fcfa8 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 2020faf..fb4d843 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index fb4d843..55b7897 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 89683c7..bfa2d97 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 v0.10.2 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 diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index c8fcfa8..42fc05c 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 v0.10.2 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 diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 42fc05c..eb889d2 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 v0.10.2 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 diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index eb889d2..fab31d2 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 0a531f2..b28e403 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 4c4f166..61085b3 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 a5c9411..2dddf7f 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 a86547c..d3a81f1 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_hwdep.c b/sound/pci/hda/hda_hwdep.c index 2dddf7f..ce67608 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 c09440d..9e6353a 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 d3a81f1..9cc4cb9 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_auto_parser.c b/sound/pci/hda/hda_auto_parser.c index 55ed857..33b3ece 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 55b7897..4bc4cd9 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -84,6 +84,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 v0.10.2 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 diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig index ebec1b7..30eb4c3 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 v0.10.2 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 diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig index 30eb4c3..8136c4c 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 c8fdaae..04dd3b6 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 v0.10.2 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 diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig index 8136c4c..9aff5cf 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 19ae14f..8d09325 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 v0.10.2 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 diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig index 9aff5cf..07025a9 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 7b0b8c3..b9dfbd8 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 v0.10.2 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 diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig index 07025a9..206f678 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 a52f566..1ff5f3c 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 v0.10.2 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 diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index 1ff5f3c..2f94acb 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 v0.10.2 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 diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig index 206f678..4962441 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 308a5b9..02fe0d1 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; @@ -708,6 +574,66 @@ static int ad198x_ch_mode_put(struct snd_kcontrol *kcontrol, /* + * 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 v0.10.2 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 diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig index 4962441..4004d40 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 d3c852a..eade21c 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 v0.10.2 From 1023dbd90c1e3e87921198939917c1f50b4b6af7 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 11 Jan 2013 22:58:28 +0000 Subject: ASoC: wm_adsp: Add basic firmware selection support There are many firmwares available for ADSP devices. Add basic support for selecting between them, including a couple of feature sets in the set of available firmware to start off with. Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index 5015ff2..1f8e8e2 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -143,6 +143,71 @@ #define ADSP2_RAM_RDY_SHIFT 0 #define ADSP2_RAM_RDY_WIDTH 1 +#define WM_ADSP_NUM_FW 3 + +static const char *wm_adsp_fw_text[WM_ADSP_NUM_FW] = { + "MBC/VSS", "Tx", "Rx ANC" +}; + +static struct { + const char *file; +} wm_adsp_fw[WM_ADSP_NUM_FW] = { + { .file = "mbc-vss" }, + { .file = "tx" }, + { .file = "rx-anc" }, +}; + +static int wm_adsp_fw_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + struct wm_adsp *adsp = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = adsp[e->shift_l].fw; + + return 0; +} + +static int wm_adsp_fw_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + struct wm_adsp *adsp = snd_soc_codec_get_drvdata(codec); + + if (ucontrol->value.integer.value[0] == adsp[e->shift_l].fw) + return 0; + + if (ucontrol->value.integer.value[0] >= WM_ADSP_NUM_FW) + return -EINVAL; + + if (adsp[e->shift_l].running) + return -EBUSY; + + adsp->fw = ucontrol->value.integer.value[0]; + + return 0; +} + +static const struct soc_enum wm_adsp_fw_enum[] = { + SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), + SOC_ENUM_SINGLE(0, 1, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), + SOC_ENUM_SINGLE(0, 2, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), + SOC_ENUM_SINGLE(0, 3, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), +}; + +const struct snd_kcontrol_new wm_adsp_fw_controls[] = { + SOC_ENUM_EXT("DSP1 Firmware", wm_adsp_fw_enum[0], + wm_adsp_fw_get, wm_adsp_fw_put), + SOC_ENUM_EXT("DSP2 Firmware", wm_adsp_fw_enum[1], + wm_adsp_fw_get, wm_adsp_fw_put), + SOC_ENUM_EXT("DSP3 Firmware", wm_adsp_fw_enum[2], + wm_adsp_fw_get, wm_adsp_fw_put), + SOC_ENUM_EXT("DSP4 Firmware", wm_adsp_fw_enum[3], + wm_adsp_fw_get, wm_adsp_fw_put), +}; +EXPORT_SYMBOL_GPL(wm_adsp_fw_controls); static struct wm_adsp_region const *wm_adsp_find_region(struct wm_adsp *dsp, int type) @@ -197,7 +262,8 @@ static int wm_adsp_load(struct wm_adsp *dsp) if (file == NULL) return -ENOMEM; - snprintf(file, PAGE_SIZE, "%s-dsp%d.wmfw", dsp->part, dsp->num); + snprintf(file, PAGE_SIZE, "%s-dsp%d-%s.wmfw", dsp->part, dsp->num, + wm_adsp_fw[dsp->fw].file); file[PAGE_SIZE - 1] = '\0'; ret = request_firmware(&firmware, file, dsp->dev); @@ -596,7 +662,8 @@ static int wm_adsp_load_coeff(struct wm_adsp *dsp) if (file == NULL) return -ENOMEM; - snprintf(file, PAGE_SIZE, "%s-dsp%d.bin", dsp->part, dsp->num); + snprintf(file, PAGE_SIZE, "%s-dsp%d-%s.bin", dsp->part, dsp->num, + wm_adsp_fw[dsp->fw].file); file[PAGE_SIZE - 1] = '\0'; ret = request_firmware(&firmware, file, dsp->dev); @@ -886,9 +953,13 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w, ADSP2_CORE_ENA | ADSP2_START); if (ret != 0) goto err; + + dsp->running = true; break; case SND_SOC_DAPM_PRE_PMD: + dsp->running = false; + regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, ADSP2_SYS_ENA | ADSP2_CORE_ENA | ADSP2_START, 0); diff --git a/sound/soc/codecs/wm_adsp.h b/sound/soc/codecs/wm_adsp.h index 4881419..5e71410 100644 --- a/sound/soc/codecs/wm_adsp.h +++ b/sound/soc/codecs/wm_adsp.h @@ -46,6 +46,9 @@ struct wm_adsp { const struct wm_adsp_region *mem; int num_mems; + int fw; + bool running; + struct regulator *dvfs; }; @@ -59,6 +62,8 @@ struct wm_adsp { .shift = num, .event = wm_adsp2_event, \ .event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD } +extern const struct snd_kcontrol_new wm_adsp_fw_controls[]; + int wm_adsp2_init(struct wm_adsp *adsp, bool dvfs); int wm_adsp1_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event); -- cgit v0.10.2 From f4d8ada2a0d4634d6b334ec85328f04c2d01fc98 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 11 Jan 2013 17:01:50 +0100 Subject: ASoC: tlv320dac33: Remove suspend/resume soc driver operations With idle_bias_off these are no longer needed. Signed-off-by: Peter Ujfalusi Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/tlv320dac33.c b/sound/soc/codecs/tlv320dac33.c index 782b0cd..4f35839 100644 --- a/sound/soc/codecs/tlv320dac33.c +++ b/sound/soc/codecs/tlv320dac33.c @@ -1452,20 +1452,6 @@ static int dac33_soc_remove(struct snd_soc_codec *codec) return 0; } -static int dac33_soc_suspend(struct snd_soc_codec *codec) -{ - dac33_set_bias_level(codec, SND_SOC_BIAS_OFF); - - return 0; -} - -static int dac33_soc_resume(struct snd_soc_codec *codec) -{ - dac33_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - - return 0; -} - static struct snd_soc_codec_driver soc_codec_dev_tlv320dac33 = { .read = dac33_read_reg_cache, .write = dac33_write_locked, @@ -1476,8 +1462,6 @@ static struct snd_soc_codec_driver soc_codec_dev_tlv320dac33 = { .reg_cache_default = dac33_reg, .probe = dac33_soc_probe, .remove = dac33_soc_remove, - .suspend = dac33_soc_suspend, - .resume = dac33_soc_resume, .controls = dac33_snd_controls, .num_controls = ARRAY_SIZE(dac33_snd_controls), -- cgit v0.10.2 From a4a2992c531f6ca0aa00ce0deb31e51c1b7ae69b Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 10 Jan 2013 16:49:11 -0800 Subject: ASoC: simple-card: add asoc_simple_dai for initializing Current simple-card driver calls asoc_simple_card_dai_init() if platform had a asoc_simple_card_dai_init pointer. And, this initialization function works only when platform has an applicable initial value for each dai settings. And basically, almost all sound card requires certain initialization. This means that almost all platform has initialization settings, and driver do nothing if it doesn't have settings. And additionally, current simple-card supports sysclk settings but it was only for codec. In order to abolish deviation between cpu and codec, and in order to simplify processing, this patch adds asoc_simple_dai, and removed pointless struct asoc_simple_dai_init_info which was trigger of calling asoc_simple_card_dai_init(). Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown diff --git a/arch/arm/mach-shmobile/board-ap4evb.c b/arch/arm/mach-shmobile/board-ap4evb.c index 4c97903..08294fa 100644 --- a/arch/arm/mach-shmobile/board-ap4evb.c +++ b/arch/arm/mach-shmobile/board-ap4evb.c @@ -686,21 +686,21 @@ static struct platform_device fsi_device = { }, }; -static struct asoc_simple_dai_init_info fsi2_ak4643_init_info = { - .fmt = SND_SOC_DAIFMT_LEFT_J, - .codec_daifmt = SND_SOC_DAIFMT_CBM_CFM, - .cpu_daifmt = SND_SOC_DAIFMT_CBS_CFS, - .sysclk = 11289600, -}; - static struct asoc_simple_card_info fsi2_ak4643_info = { .name = "AK4643", .card = "FSI2A-AK4643", - .cpu_dai = "fsia-dai", .codec = "ak4642-codec.0-0013", .platform = "sh_fsi2", - .codec_dai = "ak4642-hifi", - .init = &fsi2_ak4643_init_info, + .daifmt = SND_SOC_DAIFMT_LEFT_J, + .cpu_dai = { + .name = "fsia-dai", + .fmt = SND_SOC_DAIFMT_CBS_CFS, + }, + .codec_dai = { + .name = "ak4642-hifi", + .fmt = SND_SOC_DAIFMT_CBM_CFM, + .sysclk = 11289600, + }, }; static struct platform_device fsi_ak4643_device = { @@ -809,19 +809,18 @@ static struct platform_device lcdc1_device = { }, }; -static struct asoc_simple_dai_init_info fsi2_hdmi_init_info = { - .cpu_daifmt = SND_SOC_DAIFMT_CBM_CFM | - SND_SOC_DAIFMT_IB_NF, -}; - static struct asoc_simple_card_info fsi2_hdmi_info = { .name = "HDMI", .card = "FSI2B-HDMI", - .cpu_dai = "fsib-dai", .codec = "sh-mobile-hdmi", .platform = "sh_fsi2", - .codec_dai = "sh_mobile_hdmi-hifi", - .init = &fsi2_hdmi_init_info, + .cpu_dai = { + .name = "fsib-dai", + .fmt = SND_SOC_DAIFMT_CBM_CFM | SND_SOC_DAIFMT_IB_NF, + }, + .codec_dai = { + .name = "sh_mobile_hdmi-hifi", + }, }; static struct platform_device fsi_hdmi_device = { diff --git a/arch/arm/mach-shmobile/board-armadillo800eva.c b/arch/arm/mach-shmobile/board-armadillo800eva.c index 5353adf..0679ca6 100644 --- a/arch/arm/mach-shmobile/board-armadillo800eva.c +++ b/arch/arm/mach-shmobile/board-armadillo800eva.c @@ -806,21 +806,21 @@ static struct platform_device fsi_device = { }; /* FSI-WM8978 */ -static struct asoc_simple_dai_init_info fsi_wm8978_init_info = { - .fmt = SND_SOC_DAIFMT_I2S, - .codec_daifmt = SND_SOC_DAIFMT_CBM_CFM | SND_SOC_DAIFMT_NB_NF, - .cpu_daifmt = SND_SOC_DAIFMT_CBS_CFS, - .sysclk = 12288000, -}; - static struct asoc_simple_card_info fsi_wm8978_info = { .name = "wm8978", .card = "FSI2A-WM8978", - .cpu_dai = "fsia-dai", .codec = "wm8978.0-001a", .platform = "sh_fsi2", - .codec_dai = "wm8978-hifi", - .init = &fsi_wm8978_init_info, + .daifmt = SND_SOC_DAIFMT_I2S, + .cpu_dai = { + .name = "fsia-dai", + .fmt = SND_SOC_DAIFMT_CBS_CFS | SND_SOC_DAIFMT_IB_NF, + }, + .codec_dai = { + .name = "wm8978-hifi", + .fmt = SND_SOC_DAIFMT_CBM_CFM | SND_SOC_DAIFMT_NB_NF, + .sysclk = 12288000, + }, }; static struct platform_device fsi_wm8978_device = { @@ -832,18 +832,18 @@ static struct platform_device fsi_wm8978_device = { }; /* FSI-HDMI */ -static struct asoc_simple_dai_init_info fsi2_hdmi_init_info = { - .cpu_daifmt = SND_SOC_DAIFMT_CBM_CFM, -}; - static struct asoc_simple_card_info fsi2_hdmi_info = { .name = "HDMI", .card = "FSI2B-HDMI", - .cpu_dai = "fsib-dai", .codec = "sh-mobile-hdmi", .platform = "sh_fsi2", - .codec_dai = "sh_mobile_hdmi-hifi", - .init = &fsi2_hdmi_init_info, + .cpu_dai = { + .name = "fsib-dai", + .fmt = SND_SOC_DAIFMT_CBM_CFM, + }, + .codec_dai = { + .name = "sh_mobile_hdmi-hifi", + }, }; static struct platform_device fsi_hdmi_device = { diff --git a/arch/arm/mach-shmobile/board-kzm9g.c b/arch/arm/mach-shmobile/board-kzm9g.c index c02448d..f41b71e 100644 --- a/arch/arm/mach-shmobile/board-kzm9g.c +++ b/arch/arm/mach-shmobile/board-kzm9g.c @@ -525,21 +525,21 @@ static struct platform_device fsi_device = { }, }; -static struct asoc_simple_dai_init_info fsi2_ak4648_init_info = { - .fmt = SND_SOC_DAIFMT_LEFT_J, - .codec_daifmt = SND_SOC_DAIFMT_CBM_CFM, - .cpu_daifmt = SND_SOC_DAIFMT_CBS_CFS, - .sysclk = 11289600, -}; - static struct asoc_simple_card_info fsi2_ak4648_info = { .name = "AK4648", .card = "FSI2A-AK4648", - .cpu_dai = "fsia-dai", .codec = "ak4642-codec.0-0012", .platform = "sh_fsi2", - .codec_dai = "ak4642-hifi", - .init = &fsi2_ak4648_init_info, + .daifmt = SND_SOC_DAIFMT_LEFT_J, + .cpu_dai = { + .name = "fsia-dai", + .fmt = SND_SOC_DAIFMT_CBS_CFS, + }, + .codec_dai = { + .name = "ak4642-hifi", + .fmt = SND_SOC_DAIFMT_CBM_CFM, + .sysclk = 11289600, + }, }; static struct platform_device fsi_ak4648_device = { diff --git a/arch/arm/mach-shmobile/board-mackerel.c b/arch/arm/mach-shmobile/board-mackerel.c index b5d210b..3fd716d 100644 --- a/arch/arm/mach-shmobile/board-mackerel.c +++ b/arch/arm/mach-shmobile/board-mackerel.c @@ -502,19 +502,18 @@ static struct platform_device hdmi_lcdc_device = { }, }; -static struct asoc_simple_dai_init_info fsi2_hdmi_init_info = { - .cpu_daifmt = SND_SOC_DAIFMT_CBM_CFM | - SND_SOC_DAIFMT_IB_NF, -}; - static struct asoc_simple_card_info fsi2_hdmi_info = { .name = "HDMI", .card = "FSI2B-HDMI", - .cpu_dai = "fsib-dai", .codec = "sh-mobile-hdmi", .platform = "sh_fsi2", - .codec_dai = "sh_mobile_hdmi-hifi", - .init = &fsi2_hdmi_init_info, + .cpu_dai = { + .name = "fsib-dai", + .fmt = SND_SOC_DAIFMT_CBM_CFM | SND_SOC_DAIFMT_IB_NF, + }, + .codec_dai = { + .name = "sh_mobile_hdmi-hifi", + }, }; static struct platform_device fsi_hdmi_device = { @@ -893,21 +892,21 @@ static struct platform_device fsi_device = { }, }; -static struct asoc_simple_dai_init_info fsi2_ak4643_init_info = { - .fmt = SND_SOC_DAIFMT_LEFT_J, - .codec_daifmt = SND_SOC_DAIFMT_CBM_CFM, - .cpu_daifmt = SND_SOC_DAIFMT_CBS_CFS, - .sysclk = 11289600, -}; - static struct asoc_simple_card_info fsi2_ak4643_info = { .name = "AK4643", .card = "FSI2A-AK4643", - .cpu_dai = "fsia-dai", .codec = "ak4642-codec.0-0013", .platform = "sh_fsi2", - .codec_dai = "ak4642-hifi", - .init = &fsi2_ak4643_init_info, + .daifmt = SND_SOC_DAIFMT_LEFT_J, + .cpu_dai = { + .name = "fsia-dai", + .fmt = SND_SOC_DAIFMT_CBS_CFS, + }, + .codec_dai = { + .name = "ak4642-hifi", + .fmt = SND_SOC_DAIFMT_CBM_CFM, + .sysclk = 11289600, + }, }; static struct platform_device fsi_ak4643_device = { diff --git a/arch/sh/boards/mach-ecovec24/setup.c b/arch/sh/boards/mach-ecovec24/setup.c index 8ebe4c7..065e9600 100644 --- a/arch/sh/boards/mach-ecovec24/setup.c +++ b/arch/sh/boards/mach-ecovec24/setup.c @@ -897,21 +897,20 @@ static struct platform_device fsi_device = { .resource = fsi_resources, }; -static struct asoc_simple_dai_init_info fsi_da7210_init_info = { - .fmt = SND_SOC_DAIFMT_I2S, - .codec_daifmt = SND_SOC_DAIFMT_CBM_CFM, - .cpu_daifmt = SND_SOC_DAIFMT_CBS_CFS | - SND_SOC_DAIFMT_IB_NF, -}; - static struct asoc_simple_card_info fsi_da7210_info = { .name = "DA7210", .card = "FSIB-DA7210", - .cpu_dai = "fsib-dai", .codec = "da7210.0-001a", .platform = "sh_fsi.0", - .codec_dai = "da7210-hifi", - .init = &fsi_da7210_init_info, + .daifmt = SND_SOC_DAIFMT_I2S, + .cpu_dai = { + .name = "fsib-dai", + .fmt = SND_SOC_DAIFMT_CBS_CFS | SND_SOC_DAIFMT_IB_NF, + }, + .codec_dai = { + .name = "da7210-hifi", + .fmt = SND_SOC_DAIFMT_CBM_CFM, + }, }; static struct platform_device fsi_da7210_device = { diff --git a/arch/sh/boards/mach-se/7724/setup.c b/arch/sh/boards/mach-se/7724/setup.c index 975608f..4010e63 100644 --- a/arch/sh/boards/mach-se/7724/setup.c +++ b/arch/sh/boards/mach-se/7724/setup.c @@ -299,22 +299,21 @@ static struct platform_device fsi_device = { .resource = fsi_resources, }; -static struct asoc_simple_dai_init_info fsi2_ak4642_init_info = { - .fmt = SND_SOC_DAIFMT_LEFT_J, - .codec_daifmt = SND_SOC_DAIFMT_CBM_CFM, - .cpu_daifmt = SND_SOC_DAIFMT_CBS_CFS | - SND_SOC_DAIFMT_IB_NF, - .sysclk = 11289600, -}; - static struct asoc_simple_card_info fsi_ak4642_info = { .name = "AK4642", .card = "FSIA-AK4642", - .cpu_dai = "fsia-dai", .codec = "ak4642-codec.0-0012", .platform = "sh_fsi.0", - .codec_dai = "ak4642-hifi", - .init = &fsi2_ak4642_init_info, + .daifmt = SND_SOC_DAIFMT_LEFT_J, + .cpu_dai = { + .name = "fsia-dai", + .fmt = SND_SOC_DAIFMT_CBS_CFS | SND_SOC_DAIFMT_IB_NF, + }, + .codec_dai = { + .name = "ak4642-hifi", + .fmt = SND_SOC_DAIFMT_CBM_CFM, + .sysclk = 11289600, + }, }; static struct platform_device fsi_ak4642_device = { diff --git a/include/sound/simple_card.h b/include/sound/simple_card.h index 4b62b8d..6c74527 100644 --- a/include/sound/simple_card.h +++ b/include/sound/simple_card.h @@ -14,21 +14,21 @@ #include -struct asoc_simple_dai_init_info { +struct asoc_simple_dai { + const char *name; unsigned int fmt; - unsigned int cpu_daifmt; - unsigned int codec_daifmt; unsigned int sysclk; }; struct asoc_simple_card_info { const char *name; const char *card; - const char *cpu_dai; const char *codec; const char *platform; - const char *codec_dai; - struct asoc_simple_dai_init_info *init; /* for snd_link.init */ + + unsigned int daifmt; + struct asoc_simple_dai cpu_dai; + struct asoc_simple_dai codec_dai; /* used in simple-card.c */ struct snd_soc_dai_link snd_link; diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c index bc050ec..6cf8355 100644 --- a/sound/soc/generic/simple-card.c +++ b/sound/soc/generic/simple-card.c @@ -16,33 +16,38 @@ #define asoc_simple_get_card_info(p) \ container_of(p->dai_link, struct asoc_simple_card_info, snd_link) +static int __asoc_simple_card_dai_init(struct snd_soc_dai *dai, + struct asoc_simple_dai *set, + unsigned int daifmt) +{ + int ret = 0; + + daifmt |= set->fmt; + + if (!ret && daifmt) + ret = snd_soc_dai_set_fmt(dai, daifmt); + + if (!ret && set->sysclk) + ret = snd_soc_dai_set_sysclk(dai, 0, set->sysclk, 0); + + return ret; +} + static int asoc_simple_card_dai_init(struct snd_soc_pcm_runtime *rtd) { - struct asoc_simple_card_info *cinfo = asoc_simple_get_card_info(rtd); - struct asoc_simple_dai_init_info *iinfo = cinfo->init; + struct asoc_simple_card_info *info = asoc_simple_get_card_info(rtd); struct snd_soc_dai *codec = rtd->codec_dai; struct snd_soc_dai *cpu = rtd->cpu_dai; - unsigned int cpu_daifmt = iinfo->fmt | iinfo->cpu_daifmt; - unsigned int codec_daifmt = iinfo->fmt | iinfo->codec_daifmt; + unsigned int daifmt = info->daifmt; int ret; - if (codec_daifmt) { - ret = snd_soc_dai_set_fmt(codec, codec_daifmt); - if (ret < 0) - return ret; - } - - if (iinfo->sysclk) { - ret = snd_soc_dai_set_sysclk(codec, 0, iinfo->sysclk, 0); - if (ret < 0) - return ret; - } + ret = __asoc_simple_card_dai_init(codec, &info->codec_dai, daifmt); + if (ret < 0) + return ret; - if (cpu_daifmt) { - ret = snd_soc_dai_set_fmt(cpu, cpu_daifmt); - if (ret < 0) - return ret; - } + ret = __asoc_simple_card_dai_init(cpu, &info->cpu_dai, daifmt); + if (ret < 0) + return ret; return 0; } @@ -59,10 +64,10 @@ static int asoc_simple_card_probe(struct platform_device *pdev) if (!cinfo->name || !cinfo->card || - !cinfo->cpu_dai || !cinfo->codec || !cinfo->platform || - !cinfo->codec_dai) { + !cinfo->cpu_dai.name || + !cinfo->codec_dai.name) { dev_err(dev, "insufficient asoc_simple_card_info settings\n"); return -EINVAL; } @@ -72,14 +77,11 @@ static int asoc_simple_card_probe(struct platform_device *pdev) */ cinfo->snd_link.name = cinfo->name; cinfo->snd_link.stream_name = cinfo->name; - cinfo->snd_link.cpu_dai_name = cinfo->cpu_dai; + cinfo->snd_link.cpu_dai_name = cinfo->cpu_dai.name; cinfo->snd_link.platform_name = cinfo->platform; cinfo->snd_link.codec_name = cinfo->codec; - cinfo->snd_link.codec_dai_name = cinfo->codec_dai; - - /* enable snd_link.init if cinfo has settings */ - if (cinfo->init) - cinfo->snd_link.init = asoc_simple_card_dai_init; + cinfo->snd_link.codec_dai_name = cinfo->codec_dai.name; + cinfo->snd_link.init = asoc_simple_card_dai_init; /* * init snd_soc_card -- cgit v0.10.2 From 13aec722f3c14aa6019c800465aa3ddd3638d305 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 10 Jan 2013 17:06:15 +0100 Subject: ASoC: Constify ops and compr_ops fields of snd_soc_dai_link The core does not modify these fields, so they can be made const. This allows drivers to declare their op tables as const. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/include/sound/soc.h b/include/sound/soc.h index 769e27c..bedf3da 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -904,8 +904,8 @@ struct snd_soc_dai_link { struct snd_pcm_hw_params *params); /* machine stream operations */ - struct snd_soc_ops *ops; - struct snd_soc_compr_ops *compr_ops; + const struct snd_soc_ops *ops; + const struct snd_soc_compr_ops *compr_ops; }; struct snd_soc_codec_conf { -- cgit v0.10.2 From 609dad9bdf970da0952cea29a4442318cd4a090e Mon Sep 17 00:00:00 2001 From: Lucas Stach Date: Sat, 5 Jan 2013 02:18:43 +0100 Subject: ASoC: tegra: add ac97 host driver This adds the driver for the Tegra 2x AC97 host controller. Signed-off-by: Lucas Stach Reviewed-by: Stephen Warren Signed-off-by: Mark Brown diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra20-ac97.txt b/Documentation/devicetree/bindings/sound/nvidia,tegra20-ac97.txt new file mode 100644 index 0000000..c145497 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/nvidia,tegra20-ac97.txt @@ -0,0 +1,22 @@ +NVIDIA Tegra 20 AC97 controller + +Required properties: +- compatible : "nvidia,tegra20-ac97" +- reg : Should contain AC97 controller registers location and length +- interrupts : Should contain AC97 interrupt +- nvidia,dma-request-selector : The Tegra DMA controller's phandle and + request selector for the AC97 controller +- nvidia,codec-reset-gpio : The Tegra GPIO controller's phandle and the number + of the GPIO used to reset the external AC97 codec +- nvidia,codec-sync-gpio : The Tegra GPIO controller's phandle and the number + of the GPIO corresponding with the AC97 DAP _FS line +Example: + +ac97@70002000 { + compatible = "nvidia,tegra20-ac97"; + reg = <0x70002000 0x200>; + interrupts = <0 81 0x04>; + nvidia,dma-request-selector = <&apbdma 12>; + nvidia,codec-reset-gpio = <&gpio 170 0>; + nvidia,codec-sync-gpio = <&gpio 120 0>; +}; diff --git a/sound/soc/tegra/Kconfig b/sound/soc/tegra/Kconfig index 19e5fe7..4b3a2b8 100644 --- a/sound/soc/tegra/Kconfig +++ b/sound/soc/tegra/Kconfig @@ -6,6 +6,16 @@ config SND_SOC_TEGRA help Say Y or M here if you want support for SoC audio on Tegra. +config SND_SOC_TEGRA20_AC97 + tristate + depends on SND_SOC_TEGRA && ARCH_TEGRA_2x_SOC + select SND_SOC_AC97_BUS + select SND_SOC_TEGRA20_DAS + help + Say Y or M if you want to add support for codecs attached to the + Tegra20 AC97 interface. You will also need to select the individual + machine drivers to support below. + config SND_SOC_TEGRA20_DAS tristate depends on SND_SOC_TEGRA && ARCH_TEGRA_2x_SOC diff --git a/sound/soc/tegra/Makefile b/sound/soc/tegra/Makefile index 391e78a..02513d9 100644 --- a/sound/soc/tegra/Makefile +++ b/sound/soc/tegra/Makefile @@ -1,6 +1,7 @@ # Tegra platform Support snd-soc-tegra-pcm-objs := tegra_pcm.o snd-soc-tegra-utils-objs += tegra_asoc_utils.o +snd-soc-tegra20-ac97-objs := tegra20_ac97.o snd-soc-tegra20-das-objs := tegra20_das.o snd-soc-tegra20-i2s-objs := tegra20_i2s.o snd-soc-tegra20-spdif-objs := tegra20_spdif.o @@ -9,6 +10,7 @@ snd-soc-tegra30-i2s-objs := tegra30_i2s.o obj-$(CONFIG_SND_SOC_TEGRA) += snd-soc-tegra-pcm.o obj-$(CONFIG_SND_SOC_TEGRA) += snd-soc-tegra-utils.o +obj-$(CONFIG_SND_SOC_TEGRA20_AC97) += snd-soc-tegra20-ac97.o obj-$(CONFIG_SND_SOC_TEGRA20_DAS) += snd-soc-tegra20-das.o obj-$(CONFIG_SND_SOC_TEGRA20_I2S) += snd-soc-tegra20-i2s.o obj-$(CONFIG_SND_SOC_TEGRA20_SPDIF) += snd-soc-tegra20-spdif.o diff --git a/sound/soc/tegra/tegra20_ac97.c b/sound/soc/tegra/tegra20_ac97.c new file mode 100644 index 0000000..1bae73b --- /dev/null +++ b/sound/soc/tegra/tegra20_ac97.c @@ -0,0 +1,480 @@ +/* + * tegra20_ac97.c - Tegra20 AC97 platform driver + * + * Copyright (c) 2012 Lucas Stach + * + * Partly based on code copyright/by: + * + * Copyright (c) 2011,2012 Toradex Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tegra_asoc_utils.h" +#include "tegra20_ac97.h" + +#define DRV_NAME "tegra20-ac97" + +static struct tegra20_ac97 *workdata; + +static void tegra20_ac97_codec_reset(struct snd_ac97 *ac97) +{ + u32 readback; + unsigned long timeout; + + /* reset line is not driven by DAC pad group, have to toggle GPIO */ + gpio_set_value(workdata->reset_gpio, 0); + udelay(2); + + gpio_set_value(workdata->reset_gpio, 1); + udelay(2); + + timeout = jiffies + msecs_to_jiffies(100); + + do { + regmap_read(workdata->regmap, TEGRA20_AC97_STATUS1, &readback); + if (readback & TEGRA20_AC97_STATUS1_CODEC1_RDY) + break; + usleep_range(1000, 2000); + } while (!time_after(jiffies, timeout)); +} + +static void tegra20_ac97_codec_warm_reset(struct snd_ac97 *ac97) +{ + u32 readback; + unsigned long timeout; + + /* + * although sync line is driven by the DAC pad group warm reset using + * the controller cmd is not working, have to toggle sync line + * manually. + */ + gpio_request(workdata->sync_gpio, "codec-sync"); + + gpio_direction_output(workdata->sync_gpio, 1); + + udelay(2); + gpio_set_value(workdata->sync_gpio, 0); + udelay(2); + gpio_free(workdata->sync_gpio); + + timeout = jiffies + msecs_to_jiffies(100); + + do { + regmap_read(workdata->regmap, TEGRA20_AC97_STATUS1, &readback); + if (readback & TEGRA20_AC97_STATUS1_CODEC1_RDY) + break; + usleep_range(1000, 2000); + } while (!time_after(jiffies, timeout)); +} + +static unsigned short tegra20_ac97_codec_read(struct snd_ac97 *ac97_snd, + unsigned short reg) +{ + u32 readback; + unsigned long timeout; + + regmap_write(workdata->regmap, TEGRA20_AC97_CMD, + (((reg | 0x80) << TEGRA20_AC97_CMD_CMD_ADDR_SHIFT) & + TEGRA20_AC97_CMD_CMD_ADDR_MASK) | + TEGRA20_AC97_CMD_BUSY); + + timeout = jiffies + msecs_to_jiffies(100); + + do { + regmap_read(workdata->regmap, TEGRA20_AC97_STATUS1, &readback); + if (readback & TEGRA20_AC97_STATUS1_STA_VALID1) + break; + usleep_range(1000, 2000); + } while (!time_after(jiffies, timeout)); + + return ((readback & TEGRA20_AC97_STATUS1_STA_DATA1_MASK) >> + TEGRA20_AC97_STATUS1_STA_DATA1_SHIFT); +} + +static void tegra20_ac97_codec_write(struct snd_ac97 *ac97_snd, + unsigned short reg, unsigned short val) +{ + u32 readback; + unsigned long timeout; + + regmap_write(workdata->regmap, TEGRA20_AC97_CMD, + ((reg << TEGRA20_AC97_CMD_CMD_ADDR_SHIFT) & + TEGRA20_AC97_CMD_CMD_ADDR_MASK) | + ((val << TEGRA20_AC97_CMD_CMD_DATA_SHIFT) & + TEGRA20_AC97_CMD_CMD_DATA_MASK) | + TEGRA20_AC97_CMD_BUSY); + + timeout = jiffies + msecs_to_jiffies(100); + + do { + regmap_read(workdata->regmap, TEGRA20_AC97_CMD, &readback); + if (!(readback & TEGRA20_AC97_CMD_BUSY)) + break; + usleep_range(1000, 2000); + } while (!time_after(jiffies, timeout)); +} + +struct snd_ac97_bus_ops soc_ac97_ops = { + .read = tegra20_ac97_codec_read, + .write = tegra20_ac97_codec_write, + .reset = tegra20_ac97_codec_reset, + .warm_reset = tegra20_ac97_codec_warm_reset, +}; +EXPORT_SYMBOL_GPL(soc_ac97_ops); + +static inline void tegra20_ac97_start_playback(struct tegra20_ac97 *ac97) +{ + regmap_update_bits(ac97->regmap, TEGRA20_AC97_FIFO1_SCR, + TEGRA20_AC97_FIFO_SCR_PB_QRT_MT_EN, + TEGRA20_AC97_FIFO_SCR_PB_QRT_MT_EN); + + regmap_update_bits(ac97->regmap, TEGRA20_AC97_CTRL, + TEGRA20_AC97_CTRL_PCM_DAC_EN | + TEGRA20_AC97_CTRL_STM_EN, + TEGRA20_AC97_CTRL_PCM_DAC_EN | + TEGRA20_AC97_CTRL_STM_EN); +} + +static inline void tegra20_ac97_stop_playback(struct tegra20_ac97 *ac97) +{ + regmap_update_bits(ac97->regmap, TEGRA20_AC97_FIFO1_SCR, + TEGRA20_AC97_FIFO_SCR_PB_QRT_MT_EN, 0); + + regmap_update_bits(ac97->regmap, TEGRA20_AC97_CTRL, + TEGRA20_AC97_CTRL_PCM_DAC_EN, 0); +} + +static inline void tegra20_ac97_start_capture(struct tegra20_ac97 *ac97) +{ + regmap_update_bits(ac97->regmap, TEGRA20_AC97_FIFO1_SCR, + TEGRA20_AC97_FIFO_SCR_REC_FULL_EN, + TEGRA20_AC97_FIFO_SCR_REC_FULL_EN); +} + +static inline void tegra20_ac97_stop_capture(struct tegra20_ac97 *ac97) +{ + regmap_update_bits(ac97->regmap, TEGRA20_AC97_FIFO1_SCR, + TEGRA20_AC97_FIFO_SCR_REC_FULL_EN, 0); +} + +static int tegra20_ac97_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct tegra20_ac97 *ac97 = snd_soc_dai_get_drvdata(dai); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + case SNDRV_PCM_TRIGGER_RESUME: + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + tegra20_ac97_start_playback(ac97); + else + tegra20_ac97_start_capture(ac97); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_SUSPEND: + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + tegra20_ac97_stop_playback(ac97); + else + tegra20_ac97_stop_capture(ac97); + break; + default: + return -EINVAL; + } + + return 0; +} + +static const struct snd_soc_dai_ops tegra20_ac97_dai_ops = { + .trigger = tegra20_ac97_trigger, +}; + +static int tegra20_ac97_probe(struct snd_soc_dai *dai) +{ + struct tegra20_ac97 *ac97 = snd_soc_dai_get_drvdata(dai); + + dai->capture_dma_data = &ac97->capture_dma_data; + dai->playback_dma_data = &ac97->playback_dma_data; + + return 0; +} + +static struct snd_soc_dai_driver tegra20_ac97_dai = { + .name = "tegra-ac97-pcm", + .ac97_control = 1, + .probe = tegra20_ac97_probe, + .playback = { + .stream_name = "PCM Playback", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .capture = { + .stream_name = "PCM Capture", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .ops = &tegra20_ac97_dai_ops, +}; + +static bool tegra20_ac97_wr_rd_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case TEGRA20_AC97_CTRL: + case TEGRA20_AC97_CMD: + case TEGRA20_AC97_STATUS1: + case TEGRA20_AC97_FIFO1_SCR: + case TEGRA20_AC97_FIFO_TX1: + case TEGRA20_AC97_FIFO_RX1: + return true; + default: + break; + } + + return false; +} + +static bool tegra20_ac97_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case TEGRA20_AC97_STATUS1: + case TEGRA20_AC97_FIFO1_SCR: + case TEGRA20_AC97_FIFO_TX1: + case TEGRA20_AC97_FIFO_RX1: + return true; + default: + break; + } + + return false; +} + +static bool tegra20_ac97_precious_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case TEGRA20_AC97_FIFO_TX1: + case TEGRA20_AC97_FIFO_RX1: + return true; + default: + break; + } + + return false; +} + +static const struct regmap_config tegra20_ac97_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = TEGRA20_AC97_FIFO_RX1, + .writeable_reg = tegra20_ac97_wr_rd_reg, + .readable_reg = tegra20_ac97_wr_rd_reg, + .volatile_reg = tegra20_ac97_volatile_reg, + .precious_reg = tegra20_ac97_precious_reg, + .cache_type = REGCACHE_RBTREE, +}; + +static int tegra20_ac97_platform_probe(struct platform_device *pdev) +{ + struct tegra20_ac97 *ac97; + struct resource *mem, *memregion; + u32 of_dma[2]; + void __iomem *regs; + int ret = 0; + + ac97 = devm_kzalloc(&pdev->dev, sizeof(struct tegra20_ac97), + GFP_KERNEL); + if (!ac97) { + dev_err(&pdev->dev, "Can't allocate tegra20_ac97\n"); + ret = -ENOMEM; + goto err; + } + dev_set_drvdata(&pdev->dev, ac97); + + ac97->clk_ac97 = clk_get(&pdev->dev, NULL); + if (IS_ERR(ac97->clk_ac97)) { + dev_err(&pdev->dev, "Can't retrieve ac97 clock\n"); + ret = PTR_ERR(ac97->clk_ac97); + goto err; + } + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!mem) { + dev_err(&pdev->dev, "No memory resource\n"); + ret = -ENODEV; + goto err_clk_put; + } + + memregion = devm_request_mem_region(&pdev->dev, mem->start, + resource_size(mem), DRV_NAME); + if (!memregion) { + dev_err(&pdev->dev, "Memory region already claimed\n"); + ret = -EBUSY; + goto err_clk_put; + } + + regs = devm_ioremap(&pdev->dev, mem->start, resource_size(mem)); + if (!regs) { + dev_err(&pdev->dev, "ioremap failed\n"); + ret = -ENOMEM; + goto err_clk_put; + } + + ac97->regmap = devm_regmap_init_mmio(&pdev->dev, regs, + &tegra20_ac97_regmap_config); + if (IS_ERR(ac97->regmap)) { + dev_err(&pdev->dev, "regmap init failed\n"); + ret = PTR_ERR(ac97->regmap); + goto err_clk_put; + } + + if (of_property_read_u32_array(pdev->dev.of_node, + "nvidia,dma-request-selector", + of_dma, 2) < 0) { + dev_err(&pdev->dev, "No DMA resource\n"); + ret = -ENODEV; + goto err_clk_put; + } + + ac97->reset_gpio = of_get_named_gpio(pdev->dev.of_node, + "nvidia,codec-reset-gpio", 0); + if (gpio_is_valid(ac97->reset_gpio)) { + ret = devm_gpio_request_one(&pdev->dev, ac97->reset_gpio, + GPIOF_OUT_INIT_HIGH, "codec-reset"); + if (ret) { + dev_err(&pdev->dev, "could not get codec-reset GPIO\n"); + goto err_clk_put; + } + } else { + dev_err(&pdev->dev, "no codec-reset GPIO supplied\n"); + goto err_clk_put; + } + + ac97->sync_gpio = of_get_named_gpio(pdev->dev.of_node, + "nvidia,codec-sync-gpio", 0); + if (!gpio_is_valid(ac97->sync_gpio)) { + dev_err(&pdev->dev, "no codec-sync GPIO supplied\n"); + goto err_clk_put; + } + + ac97->capture_dma_data.addr = mem->start + TEGRA20_AC97_FIFO_RX1; + ac97->capture_dma_data.wrap = 4; + ac97->capture_dma_data.width = 32; + ac97->capture_dma_data.req_sel = of_dma[1]; + + ac97->playback_dma_data.addr = mem->start + TEGRA20_AC97_FIFO_TX1; + ac97->playback_dma_data.wrap = 4; + ac97->playback_dma_data.width = 32; + ac97->playback_dma_data.req_sel = of_dma[1]; + + ret = snd_soc_register_dais(&pdev->dev, &tegra20_ac97_dai, 1); + if (ret) { + dev_err(&pdev->dev, "Could not register DAI: %d\n", ret); + ret = -ENOMEM; + goto err_clk_put; + } + + ret = tegra_pcm_platform_register(&pdev->dev); + if (ret) { + dev_err(&pdev->dev, "Could not register PCM: %d\n", ret); + goto err_unregister_dai; + } + + ret = tegra_asoc_utils_init(&ac97->util_data, &pdev->dev); + if (ret) + goto err_unregister_pcm; + + ret = tegra_asoc_utils_set_ac97_rate(&ac97->util_data); + if (ret) + goto err_asoc_utils_fini; + + ret = clk_prepare_enable(ac97->clk_ac97); + if (ret) { + dev_err(&pdev->dev, "clk_enable failed: %d\n", ret); + goto err_asoc_utils_fini; + } + + /* XXX: crufty ASoC AC97 API - only one AC97 codec allowed */ + workdata = ac97; + + return 0; + +err_asoc_utils_fini: + tegra_asoc_utils_fini(&ac97->util_data); +err_unregister_pcm: + tegra_pcm_platform_unregister(&pdev->dev); +err_unregister_dai: + snd_soc_unregister_dai(&pdev->dev); +err_clk_put: + clk_put(ac97->clk_ac97); +err: + return ret; +} + +static int tegra20_ac97_platform_remove(struct platform_device *pdev) +{ + struct tegra20_ac97 *ac97 = dev_get_drvdata(&pdev->dev); + + tegra_pcm_platform_unregister(&pdev->dev); + snd_soc_unregister_dai(&pdev->dev); + + tegra_asoc_utils_fini(&ac97->util_data); + + clk_disable_unprepare(ac97->clk_ac97); + clk_put(ac97->clk_ac97); + + return 0; +} + +static const struct of_device_id tegra20_ac97_of_match[] __devinitconst = { + { .compatible = "nvidia,tegra20-ac97", }, + {}, +}; + +static struct platform_driver tegra20_ac97_driver = { + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + .of_match_table = tegra20_ac97_of_match, + }, + .probe = tegra20_ac97_platform_probe, + .remove = tegra20_ac97_platform_remove, +}; +module_platform_driver(tegra20_ac97_driver); + +MODULE_AUTHOR("Lucas Stach"); +MODULE_DESCRIPTION("Tegra20 AC97 ASoC driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:" DRV_NAME); +MODULE_DEVICE_TABLE(of, tegra20_ac97_of_match); diff --git a/sound/soc/tegra/tegra20_ac97.h b/sound/soc/tegra/tegra20_ac97.h new file mode 100644 index 0000000..dddc682 --- /dev/null +++ b/sound/soc/tegra/tegra20_ac97.h @@ -0,0 +1,95 @@ +/* + * tegra20_ac97.h - Definitions for the Tegra20 AC97 controller driver + * + * Copyright (c) 2012 Lucas Stach + * + * Partly based on code copyright/by: + * + * Copyright (c) 2011,2012 Toradex Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + */ + +#ifndef __TEGRA20_AC97_H__ +#define __TEGRA20_AC97_H__ + +#include "tegra_pcm.h" + +#define TEGRA20_AC97_CTRL 0x00 +#define TEGRA20_AC97_CMD 0x04 +#define TEGRA20_AC97_STATUS1 0x08 +/* ... */ +#define TEGRA20_AC97_FIFO1_SCR 0x1c +/* ... */ +#define TEGRA20_AC97_FIFO_TX1 0x40 +#define TEGRA20_AC97_FIFO_RX1 0x80 + +/* TEGRA20_AC97_CTRL */ +#define TEGRA20_AC97_CTRL_STM2_EN (1 << 16) +#define TEGRA20_AC97_CTRL_DOUBLE_SAMPLING_EN (1 << 11) +#define TEGRA20_AC97_CTRL_IO_CNTRL_EN (1 << 10) +#define TEGRA20_AC97_CTRL_HSET_DAC_EN (1 << 9) +#define TEGRA20_AC97_CTRL_LINE2_DAC_EN (1 << 8) +#define TEGRA20_AC97_CTRL_PCM_LFE_EN (1 << 7) +#define TEGRA20_AC97_CTRL_PCM_SUR_EN (1 << 6) +#define TEGRA20_AC97_CTRL_PCM_CEN_DAC_EN (1 << 5) +#define TEGRA20_AC97_CTRL_LINE1_DAC_EN (1 << 4) +#define TEGRA20_AC97_CTRL_PCM_DAC_EN (1 << 3) +#define TEGRA20_AC97_CTRL_COLD_RESET (1 << 2) +#define TEGRA20_AC97_CTRL_WARM_RESET (1 << 1) +#define TEGRA20_AC97_CTRL_STM_EN (1 << 0) + +/* TEGRA20_AC97_CMD */ +#define TEGRA20_AC97_CMD_CMD_ADDR_SHIFT 24 +#define TEGRA20_AC97_CMD_CMD_ADDR_MASK (0xff << TEGRA20_AC97_CMD_CMD_ADDR_SHIFT) +#define TEGRA20_AC97_CMD_CMD_DATA_SHIFT 8 +#define TEGRA20_AC97_CMD_CMD_DATA_MASK (0xffff << TEGRA20_AC97_CMD_CMD_DATA_SHIFT) +#define TEGRA20_AC97_CMD_CMD_ID_SHIFT 2 +#define TEGRA20_AC97_CMD_CMD_ID_MASK (0x3 << TEGRA20_AC97_CMD_CMD_ID_SHIFT) +#define TEGRA20_AC97_CMD_BUSY (1 << 0) + +/* TEGRA20_AC97_STATUS1 */ +#define TEGRA20_AC97_STATUS1_STA_ADDR1_SHIFT 24 +#define TEGRA20_AC97_STATUS1_STA_ADDR1_MASK (0xff << TEGRA20_AC97_STATUS1_STA_ADDR1_SHIFT) +#define TEGRA20_AC97_STATUS1_STA_DATA1_SHIFT 8 +#define TEGRA20_AC97_STATUS1_STA_DATA1_MASK (0xffff << TEGRA20_AC97_STATUS1_STA_DATA1_SHIFT) +#define TEGRA20_AC97_STATUS1_STA_VALID1 (1 << 2) +#define TEGRA20_AC97_STATUS1_STANDBY1 (1 << 1) +#define TEGRA20_AC97_STATUS1_CODEC1_RDY (1 << 0) + +/* TEGRA20_AC97_FIFO1_SCR */ +#define TEGRA20_AC97_FIFO_SCR_REC_MT_CNT_SHIFT 27 +#define TEGRA20_AC97_FIFO_SCR_REC_MT_CNT_MASK (0x1f << TEGRA20_AC97_FIFO_SCR_REC_MT_CNT_SHIFT) +#define TEGRA20_AC97_FIFO_SCR_PB_MT_CNT_SHIFT 22 +#define TEGRA20_AC97_FIFO_SCR_PB_MT_CNT_MASK (0x1f << TEGRA20_AC97_FIFO_SCR_PB_MT_CNT_SHIFT) +#define TEGRA20_AC97_FIFO_SCR_REC_OVERRUN_INT_STA (1 << 19) +#define TEGRA20_AC97_FIFO_SCR_PB_UNDERRUN_INT_STA (1 << 18) +#define TEGRA20_AC97_FIFO_SCR_REC_FORCE_MT (1 << 17) +#define TEGRA20_AC97_FIFO_SCR_PB_FORCE_MT (1 << 16) +#define TEGRA20_AC97_FIFO_SCR_REC_FULL_EN (1 << 15) +#define TEGRA20_AC97_FIFO_SCR_REC_3QRT_FULL_EN (1 << 14) +#define TEGRA20_AC97_FIFO_SCR_REC_QRT_FULL_EN (1 << 13) +#define TEGRA20_AC97_FIFO_SCR_REC_EMPTY_EN (1 << 12) +#define TEGRA20_AC97_FIFO_SCR_PB_NOT_FULL_EN (1 << 11) +#define TEGRA20_AC97_FIFO_SCR_PB_QRT_MT_EN (1 << 10) +#define TEGRA20_AC97_FIFO_SCR_PB_3QRT_MT_EN (1 << 9) +#define TEGRA20_AC97_FIFO_SCR_PB_EMPTY_MT_EN (1 << 8) + +struct tegra20_ac97 { + struct clk *clk_ac97; + struct tegra_pcm_dma_params capture_dma_data; + struct tegra_pcm_dma_params playback_dma_data; + struct regmap *regmap; + int reset_gpio; + int sync_gpio; + struct tegra_asoc_utils_data util_data; +}; +#endif /* __TEGRA20_AC97_H__ */ -- cgit v0.10.2 From 9e7b6d60d880a463b17e4eae0d61c9f9a12f22bb Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 10 Jan 2013 00:34:08 -0800 Subject: ASoC: fsi: add device tree support Support for loading the Renesas FSI driver via devicetree. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown diff --git a/Documentation/devicetree/bindings/sound/renesas,fsi.txt b/Documentation/devicetree/bindings/sound/renesas,fsi.txt new file mode 100644 index 0000000..c5be003 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/renesas,fsi.txt @@ -0,0 +1,26 @@ +Renesas FSI + +Required properties: +- compatible : "renesas,sh_fsi2" or "renesas,sh_fsi" +- reg : Should contain the register physical address and length +- interrupts : Should contain FSI interrupt + +- fsia,spdif-connection : FSI is connected by S/PDFI +- fsia,stream-mode-support : FSI supports 16bit stream mode. +- fsia,use-internal-clock : FSI uses internal clock when master mode. + +- fsib,spdif-connection : same as fsia +- fsib,stream-mode-support : same as fsia +- fsib,use-internal-clock : same as fsia + +Example: + +sh_fsi2: sh_fsi2@0xec230000 { + compatible = "renesas,sh_fsi2"; + reg = <0xec230000 0x400>; + interrupts = <0 146 0x4>; + + fsia,spdif-connection; + fsia,stream-mode-support; + fsia,use-internal-clock; +}; diff --git a/sound/soc/sh/fsi.c b/sound/soc/sh/fsi.c index ef34ef8..9157612 100644 --- a/sound/soc/sh/fsi.c +++ b/sound/soc/sh/fsi.c @@ -16,6 +16,8 @@ #include #include #include +#include +#include #include #include #include @@ -297,7 +299,7 @@ struct fsi_master { int irq; struct fsi_priv fsia; struct fsi_priv fsib; - struct fsi_core *core; + const struct fsi_core *core; spinlock_t lock; }; @@ -1887,6 +1889,33 @@ static struct snd_soc_platform_driver fsi_soc_platform = { /* * platform function */ +static void fsi_of_parse(char *name, + struct device_node *np, + struct sh_fsi_port_info *info, + struct device *dev) +{ + int i; + char prop[128]; + unsigned long flags = 0; + struct { + char *name; + unsigned int val; + } of_parse_property[] = { + { "spdif-connection", SH_FSI_FMT_SPDIF }, + { "stream-mode-support", SH_FSI_ENABLE_STREAM_MODE }, + { "use-internal-clock", SH_FSI_CLK_CPG }, + }; + + for (i = 0; i < ARRAY_SIZE(of_parse_property); i++) { + sprintf(prop, "%s,%s", name, of_parse_property[i].name); + if (of_get_property(np, prop, NULL)) + flags |= of_parse_property[i].val; + } + info->flags = flags; + + dev_dbg(dev, "%s flags : %lx\n", name, info->flags); +} + static void fsi_port_info_init(struct fsi_priv *fsi, struct sh_fsi_port_info *info) { @@ -1914,22 +1943,40 @@ static void fsi_handler_init(struct fsi_priv *fsi, } } +static struct of_device_id fsi_of_match[]; static int fsi_probe(struct platform_device *pdev) { struct fsi_master *master; - const struct platform_device_id *id_entry; + struct device_node *np = pdev->dev.of_node; struct sh_fsi_platform_info info; + const struct fsi_core *core; struct fsi_priv *fsi; struct resource *res; unsigned int irq; int ret; memset(&info, 0, sizeof(info)); - if (pdev->dev.platform_data) - memcpy(&info, pdev->dev.platform_data, sizeof(info)); - id_entry = pdev->id_entry; - if (!id_entry) { + core = NULL; + if (np) { + const struct of_device_id *of_id; + + of_id = of_match_device(fsi_of_match, &pdev->dev); + if (of_id) { + core = of_id->data; + fsi_of_parse("fsia", np, &info.port_a, &pdev->dev); + fsi_of_parse("fsib", np, &info.port_b, &pdev->dev); + } + } else { + const struct platform_device_id *id_entry = pdev->id_entry; + if (id_entry) + core = (struct fsi_core *)id_entry->driver_data; + + if (pdev->dev.platform_data) + memcpy(&info, pdev->dev.platform_data, sizeof(info)); + } + + if (!core) { dev_err(&pdev->dev, "unknown fsi device\n"); return -ENODEV; } @@ -1956,7 +2003,7 @@ static int fsi_probe(struct platform_device *pdev) /* master setting */ master->irq = irq; - master->core = (struct fsi_core *)id_entry->driver_data; + master->core = core; spin_lock_init(&master->lock); /* FSI A setting */ @@ -1987,7 +2034,7 @@ static int fsi_probe(struct platform_device *pdev) dev_set_drvdata(&pdev->dev, master); ret = devm_request_irq(&pdev->dev, irq, &fsi_interrupt, 0, - id_entry->name, master); + dev_name(&pdev->dev), master); if (ret) { dev_err(&pdev->dev, "irq request err\n"); goto exit_fsib; @@ -2113,6 +2160,13 @@ static struct fsi_core fsi2_core = { .b_mclk = B_MST_CTLR, }; +static struct of_device_id fsi_of_match[] __devinitconst = { + { .compatible = "renesas,sh_fsi", .data = &fsi1_core}, + { .compatible = "renesas,sh_fsi2", .data = &fsi2_core}, + {}, +}; +MODULE_DEVICE_TABLE(of, fsi_of_match); + static struct platform_device_id fsi_id_table[] = { { "sh_fsi", (kernel_ulong_t)&fsi1_core }, { "sh_fsi2", (kernel_ulong_t)&fsi2_core }, @@ -2124,6 +2178,7 @@ static struct platform_driver fsi_driver = { .driver = { .name = "fsi-pcm-audio", .pm = &fsi_pm_ops, + .of_match_table = fsi_of_match, }, .probe = fsi_probe, .remove = fsi_remove, -- cgit v0.10.2 From c890caee54cd1fa2dd63d7f0c51d6b43eca63db3 Mon Sep 17 00:00:00 2001 From: Stephen Rothwell Date: Mon, 14 Jan 2013 12:04:00 +1100 Subject: ASoC: ak4642: remove __devinitconst annotation CONFIG_HOTPLUG is always true now and the __dev* macros have been removed. Signed-off-by: Stephen Rothwell Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/ak4642.c b/sound/soc/codecs/ak4642.c index c78794d..2d03787 100644 --- a/sound/soc/codecs/ak4642.c +++ b/sound/soc/codecs/ak4642.c @@ -547,7 +547,7 @@ static int ak4642_i2c_remove(struct i2c_client *client) return 0; } -static struct of_device_id ak4642_of_match[] __devinitconst = { +static struct of_device_id ak4642_of_match[] = { { .compatible = "asahi-kasei,ak4642", .data = &soc_codec_dev_ak4642}, { .compatible = "asahi-kasei,ak4643", .data = &soc_codec_dev_ak4642}, { .compatible = "asahi-kasei,ak4648", .data = &soc_codec_dev_ak4648}, -- cgit v0.10.2 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 diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 4bc4cd9..932e6a1 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 bfa2d97..1ceaacd 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 v0.10.2 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 diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 9cc4cb9..e5dffd2 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 v0.10.2 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 diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index e5dffd2..4c98b30 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 v0.10.2 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 diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 4c98b30..533afb6 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 v0.10.2 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 diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 533afb6..f66c7d1 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 v0.10.2 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 diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index f66c7d1..60d43dd 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 v0.10.2 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 diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 60d43dd..796dfd1 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_auto_parser.c b/sound/pci/hda/hda_auto_parser.c index 33b3ece..a4810c7 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 v0.10.2 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 diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 796dfd1..4a2594a 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 v0.10.2 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 diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 4a2594a..3cb44c1 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 v0.10.2 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 diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 3cb44c1..fa16ff7 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 v0.10.2 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 diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index fa16ff7..3368f74 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 v0.10.2 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 diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 3368f74..ccad9ad 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 v0.10.2 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 diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index ccad9ad..a7eed73 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index b28e403..e6cdad7 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 v0.10.2 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 diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index 2fd3121..38ac07b 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 v0.10.2 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 diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index 38ac07b..e4e1684 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; } @@ -1419,6 +1445,21 @@ static int dspio_scp(struct hda_codec *codec, } /* + * 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 */ static int dspio_alloc_dma_chan(struct hda_codec *codec, unsigned int *dma_chan) @@ -2597,6 +2638,9 @@ static bool dspload_wait_loaded(struct hda_codec *codec) return false; } +/* + * Controls stuffs. + */ /* * Mixer controls helpers. @@ -2699,6 +2743,300 @@ static int ca0132_dig_playback_pcm_close(struct hda_pcm_stream *hinfo, } /* + * 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 = { .substreams = 1, @@ -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 v0.10.2 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 diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index e4e1684..91c4a50 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 */ @@ -2950,6 +2906,59 @@ static int ca0132_select_mic(struct hda_codec *codec) } /* + * 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 */ static int ca0132_effects_set(struct hda_codec *codec, hda_nid_t nid, long val) @@ -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 v0.10.2 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 diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index 91c4a50..748fca7 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; } /* @@ -2699,6 +2757,36 @@ static int ca0132_dig_playback_pcm_close(struct hda_pcm_stream *hinfo, } /* + * 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. * If jack inserted, headphone will be selected, else built-in speakers @@ -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 v0.10.2 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 diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index 748fca7..9ea5660 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 v0.10.2 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 diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index 9ea5660..c1391f4 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 v0.10.2 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 diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index c1391f4..77903a3 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -2684,41 +2684,6 @@ static bool dspload_wait_loaded(struct hda_codec *codec) } /* - * 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 */ static void ca0132_setup_stream(struct hda_codec *codec, hda_nid_t nid, @@ -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 v0.10.2 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 diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index 77903a3..4c9b95e 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 v0.10.2 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 diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index 4c9b95e..2b026e2 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 v0.10.2 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 diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index 2b026e2..0d2c2f8 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 v0.10.2 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 diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index 0d2c2f8..467c9a1 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 v0.10.2 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 diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index 467c9a1..7668388 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 v0.10.2 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 diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index 7668388..f6c9490 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 v0.10.2 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 diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index f6c9490..ee2b9c6 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 v0.10.2 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 diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index ee2b9c6..cc6c8a5 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 v0.10.2 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 diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index cc6c8a5..70a2c55 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 v0.10.2 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 diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index 70a2c55..daf5ee3 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 932e6a1..e878a9e 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 1ceaacd..6ba5805 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 fab31d2..71a8894 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 v0.10.2 From 5e7a7a221fbae313a8635411b557e736ba044c98 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 16 Jan 2013 10:03:56 +0900 Subject: ASoC: wm_adsp: Add initialisation function for ADSP1 Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index 1f8e8e2..58cac07 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -780,6 +780,14 @@ out: return 0; } +int wm_adsp1_init(struct wm_adsp *adsp) +{ + INIT_LIST_HEAD(&adsp->alg_regions); + + return 0; +} +EXPORT_SYMBOL_GPL(wm_adsp1_init); + int wm_adsp1_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) diff --git a/sound/soc/codecs/wm_adsp.h b/sound/soc/codecs/wm_adsp.h index 5e71410..41206d7 100644 --- a/sound/soc/codecs/wm_adsp.h +++ b/sound/soc/codecs/wm_adsp.h @@ -64,6 +64,7 @@ struct wm_adsp { extern const struct snd_kcontrol_new wm_adsp_fw_controls[]; +int wm_adsp1_init(struct wm_adsp *adsp); int wm_adsp2_init(struct wm_adsp *adsp, bool dvfs); int wm_adsp1_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event); -- cgit v0.10.2 From 5851cb3daf31a7865983ac131be87af92ab4ff7a Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 16 Jan 2013 10:04:57 +0900 Subject: ASoC: wm2200: Initialise the ADSPs Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm2200.c b/sound/soc/codecs/wm2200.c index afcf31d..90aae49 100644 --- a/sound/soc/codecs/wm2200.c +++ b/sound/soc/codecs/wm2200.c @@ -2224,6 +2224,9 @@ static int wm2200_i2c_probe(struct i2c_client *i2c, wm2200->dsp[1].mem = wm2200_dsp2_regions; wm2200->dsp[1].num_mems = ARRAY_SIZE(wm2200_dsp2_regions); + for (i = 0; i < ARRAY_SIZE(wm2200->dsp); i++) + wm_adsp1_init(&wm2200->dsp[i]); + if (pdata) wm2200->pdata = *pdata; -- cgit v0.10.2 From 0c2bc7c7d8306bad0ec534852f1900140b80c956 Mon Sep 17 00:00:00 2001 From: Adrian Knoth Date: Tue, 15 Jan 2013 18:52:20 +0100 Subject: ALSA: hdsp - Fix detection for RME RPM/Multiface/Digiface ioboxes The current iobox detection code reportedly fails for various users, so simply do what the Win32 driver does instead. Patch originally by Karl Grill and then modified to comply with kernel coding guidelines + current HEAD. Signed-off-by: Adrian Knoth Signed-off-by: Takashi Iwai diff --git a/sound/pci/rme9652/hdsp.c b/sound/pci/rme9652/hdsp.c index 4fae81f..866d684 100644 --- a/sound/pci/rme9652/hdsp.c +++ b/sound/pci/rme9652/hdsp.c @@ -154,10 +154,13 @@ MODULE_FIRMWARE("digiface_firmware_rev11.bin"); #define HDSP_BIGENDIAN_MODE 0x200 #define HDSP_RD_MULTIPLE 0x400 #define HDSP_9652_ENABLE_MIXER 0x800 +#define HDSP_S200 0x800 +#define HDSP_S300 (0x100 | HDSP_S200) /* dummy, purpose of 0x100 unknown */ +#define HDSP_CYCLIC_MODE 0x1000 #define HDSP_TDO 0x10000000 -#define HDSP_S_PROGRAM (HDSP_PROGRAM|HDSP_CONFIG_MODE_0) -#define HDSP_S_LOAD (HDSP_PROGRAM|HDSP_CONFIG_MODE_1) +#define HDSP_S_PROGRAM (HDSP_CYCLIC_MODE|HDSP_PROGRAM|HDSP_CONFIG_MODE_0) +#define HDSP_S_LOAD (HDSP_CYCLIC_MODE|HDSP_PROGRAM|HDSP_CONFIG_MODE_1) /* Control Register bits */ @@ -671,13 +674,23 @@ static unsigned int hdsp_read(struct hdsp *hdsp, int reg) static int hdsp_check_for_iobox (struct hdsp *hdsp) { + int i; + if (hdsp->io_type == H9652 || hdsp->io_type == H9632) return 0; - if (hdsp_read (hdsp, HDSP_statusRegister) & HDSP_ConfigError) { - snd_printk("Hammerfall-DSP: no IO box connected!\n"); - hdsp->state &= ~HDSP_FirmwareLoaded; - return -EIO; + for (i = 0; i < 500; i++) { + if (0 == (hdsp_read(hdsp, HDSP_statusRegister) & + HDSP_ConfigError)) { + if (i) { + snd_printd("Hammerfall-DSP: IO box found after %d ms\n", + (20 * i)); + } + return 0; + } + msleep(20); } - return 0; + snd_printk(KERN_ERR "Hammerfall-DSP: no IO box connected!\n"); + hdsp->state &= ~HDSP_FirmwareLoaded; + return -EIO; } static int hdsp_wait_for_iobox(struct hdsp *hdsp, unsigned int loops, @@ -728,6 +741,7 @@ static int snd_hdsp_load_firmware_from_cache(struct hdsp *hdsp) { if (hdsp_fifo_wait (hdsp, 0, HDSP_LONG_WAIT)) { snd_printk ("Hammerfall-DSP: timeout waiting for download preparation\n"); + hdsp_write(hdsp, HDSP_control2Reg, HDSP_S200); return -EIO; } @@ -737,17 +751,15 @@ static int snd_hdsp_load_firmware_from_cache(struct hdsp *hdsp) { hdsp_write(hdsp, HDSP_fifoData, cache[i]); if (hdsp_fifo_wait (hdsp, 127, HDSP_LONG_WAIT)) { snd_printk ("Hammerfall-DSP: timeout during firmware loading\n"); + hdsp_write(hdsp, HDSP_control2Reg, HDSP_S200); return -EIO; } } - ssleep(3); - - if (hdsp_fifo_wait (hdsp, 0, HDSP_LONG_WAIT)) { - snd_printk ("Hammerfall-DSP: timeout at end of firmware loading\n"); - return -EIO; - } + hdsp_fifo_wait(hdsp, 3, HDSP_LONG_WAIT); + hdsp_write(hdsp, HDSP_control2Reg, HDSP_S200); + ssleep(3); #ifdef SNDRV_BIG_ENDIAN hdsp->control2_register = HDSP_BIGENDIAN_MODE; #else @@ -773,24 +785,51 @@ static int hdsp_get_iobox_version (struct hdsp *hdsp) { if ((hdsp_read (hdsp, HDSP_statusRegister) & HDSP_DllError) != 0) { - hdsp_write (hdsp, HDSP_control2Reg, HDSP_PROGRAM); - hdsp_write (hdsp, HDSP_fifoData, 0); - if (hdsp_fifo_wait (hdsp, 0, HDSP_SHORT_WAIT) < 0) - return -EIO; + hdsp_write(hdsp, HDSP_control2Reg, HDSP_S_LOAD); + hdsp_write(hdsp, HDSP_fifoData, 0); - hdsp_write (hdsp, HDSP_control2Reg, HDSP_S_LOAD); + if (hdsp_fifo_wait(hdsp, 0, HDSP_SHORT_WAIT) < 0) { + hdsp_write(hdsp, HDSP_control2Reg, HDSP_S300); + hdsp_write(hdsp, HDSP_control2Reg, HDSP_S_LOAD); + } + + hdsp_write(hdsp, HDSP_control2Reg, HDSP_S200 | HDSP_PROGRAM); hdsp_write (hdsp, HDSP_fifoData, 0); + if (hdsp_fifo_wait(hdsp, 0, HDSP_SHORT_WAIT) < 0) { + hdsp->io_type = Multiface; + snd_printk("Hammerfall-DSP: Multiface found\n"); + return 0; + } - if (hdsp_fifo_wait(hdsp, 0, HDSP_SHORT_WAIT)) { - hdsp_write(hdsp, HDSP_control2Reg, HDSP_VERSION_BIT); - hdsp_write(hdsp, HDSP_control2Reg, HDSP_S_LOAD); - if (hdsp_fifo_wait(hdsp, 0, HDSP_SHORT_WAIT)) - hdsp->io_type = RPM; - else - hdsp->io_type = Multiface; - } else { + hdsp_write(hdsp, HDSP_control2Reg, HDSP_S_LOAD); + hdsp_write(hdsp, HDSP_fifoData, 0); + if (hdsp_fifo_wait(hdsp, 0, HDSP_SHORT_WAIT) == 0) { hdsp->io_type = Digiface; + snd_printk("Hammerfall-DSP: Digiface found\n"); + return 0; } + + hdsp_write(hdsp, HDSP_control2Reg, HDSP_S300); + hdsp_write(hdsp, HDSP_control2Reg, HDSP_S_LOAD); + hdsp_write(hdsp, HDSP_fifoData, 0); + if (hdsp_fifo_wait(hdsp, 0, HDSP_SHORT_WAIT) == 0) { + hdsp->io_type = Multiface; + snd_printk("Hammerfall-DSP: Multiface found\n"); + return 0; + } + + hdsp_write(hdsp, HDSP_control2Reg, HDSP_S300); + hdsp_write(hdsp, HDSP_control2Reg, HDSP_S_LOAD); + hdsp_write(hdsp, HDSP_fifoData, 0); + if (hdsp_fifo_wait(hdsp, 0, HDSP_SHORT_WAIT) < 0) { + hdsp->io_type = Multiface; + snd_printk("Hammerfall-DSP: Multiface found\n"); + return 0; + } + + hdsp->io_type = RPM; + snd_printk("Hammerfall-DSP: RPM found\n"); + return 0; } else { /* firmware was already loaded, get iobox type */ if (hdsp_read(hdsp, HDSP_status2Register) & HDSP_version2) -- cgit v0.10.2 From 66d9244ec72392c1cc906a41545e6669a97a2c8e Mon Sep 17 00:00:00 2001 From: Adrian Knoth Date: Tue, 15 Jan 2013 18:52:21 +0100 Subject: ALSA: hdsp - Implement generic function to toggle settings The driver contains multiple similar functions that change only a single bit in the control register, only the bit position varies. This patch implements a generic function to toggle a certain bit position that will be used to replace the old code. Signed-off-by: Adrian Knoth Signed-off-by: Takashi Iwai diff --git a/sound/pci/rme9652/hdsp.c b/sound/pci/rme9652/hdsp.c index 866d684..4ebd283 100644 --- a/sound/pci/rme9652/hdsp.c +++ b/sound/pci/rme9652/hdsp.c @@ -1713,6 +1713,65 @@ static int snd_hdsp_put_spdif_in(struct snd_kcontrol *kcontrol, struct snd_ctl_e return change; } +#define HDSP_TOGGLE_SETTING(xname, xindex) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = xname, \ + .private_value = xindex, \ + .info = snd_hdsp_info_toggle_setting, \ + .get = snd_hdsp_get_toggle_setting, \ + .put = snd_hdsp_put_toggle_setting \ +} + +static int hdsp_toggle_setting(struct hdsp *hdsp, u32 regmask) +{ + return (hdsp->control_register & regmask) ? 1 : 0; +} + +static int hdsp_set_toggle_setting(struct hdsp *hdsp, u32 regmask, int out) +{ + if (out) + hdsp->control_register |= regmask; + else + hdsp->control_register &= ~regmask; + hdsp_write(hdsp, HDSP_controlRegister, hdsp->control_register); + + return 0; +} + +#define snd_hdsp_info_toggle_setting snd_ctl_boolean_mono_info + +static int snd_hdsp_get_toggle_setting(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hdsp *hdsp = snd_kcontrol_chip(kcontrol); + u32 regmask = kcontrol->private_value; + + spin_lock_irq(&hdsp->lock); + ucontrol->value.integer.value[0] = hdsp_toggle_setting(hdsp, regmask); + spin_unlock_irq(&hdsp->lock); + return 0; +} + +static int snd_hdsp_put_toggle_setting(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hdsp *hdsp = snd_kcontrol_chip(kcontrol); + u32 regmask = kcontrol->private_value; + int change; + unsigned int val; + + if (!snd_hdsp_use_is_exclusive(hdsp)) + return -EBUSY; + val = ucontrol->value.integer.value[0] & 1; + spin_lock_irq(&hdsp->lock); + change = (int) val != hdsp_toggle_setting(hdsp, regmask); + if (change) + hdsp_set_toggle_setting(hdsp, regmask, val); + spin_unlock_irq(&hdsp->lock); + return change; +} + + #define HDSP_SPDIF_OUT(xname, xindex) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ .info = snd_hdsp_info_spdif_bits, \ -- cgit v0.10.2 From 4833c673dee69ac352cd03cc97823a210c5cffcb Mon Sep 17 00:00:00 2001 From: Adrian Knoth Date: Tue, 15 Jan 2013 18:52:22 +0100 Subject: ALSA: hdsp - Use HDSP_TOGGLE_SETTING to alter settings HDSP_TOGGLE_SETTING and its corresponding functions allow to change settings in the control register. Instead of using the specialised functions, use the generic code to make the code DRY. Signed-off-by: Adrian Knoth Signed-off-by: Takashi Iwai diff --git a/sound/pci/rme9652/hdsp.c b/sound/pci/rme9652/hdsp.c index 4ebd283..ae79695 100644 --- a/sound/pci/rme9652/hdsp.c +++ b/sound/pci/rme9652/hdsp.c @@ -3288,7 +3288,7 @@ static struct snd_kcontrol_new snd_hdsp_9632_controls[] = { HDSP_DA_GAIN("DA Gain", 0), HDSP_AD_GAIN("AD Gain", 0), HDSP_PHONE_GAIN("Phones Gain", 0), -HDSP_XLR_BREAKOUT_CABLE("XLR Breakout Cable", 0), +HDSP_TOGGLE_SETTING("XLR Breakout Cable", HDSP_XLRBreakoutCable), HDSP_DDS_OFFSET("DDS Sample Rate Offset", 0) }; @@ -3330,10 +3330,10 @@ static struct snd_kcontrol_new snd_hdsp_controls[] = { }, HDSP_MIXER("Mixer", 0), HDSP_SPDIF_IN("IEC958 Input Connector", 0), -HDSP_SPDIF_OUT("IEC958 Output also on ADAT1", 0), -HDSP_SPDIF_PROFESSIONAL("IEC958 Professional Bit", 0), -HDSP_SPDIF_EMPHASIS("IEC958 Emphasis Bit", 0), -HDSP_SPDIF_NON_AUDIO("IEC958 Non-audio Bit", 0), +HDSP_TOGGLE_SETTING("IEC958 Output also on ADAT1", HDSP_SPDIFOpticalOut), +HDSP_TOGGLE_SETTING("IEC958 Professional Bit", HDSP_SPDIFProfessional), +HDSP_TOGGLE_SETTING("IEC958 Emphasis Bit", HDSP_SPDIFEmphasis), +HDSP_TOGGLE_SETTING("IEC958 Non-audio Bit", HDSP_SPDIFNonAudio), /* 'Sample Clock Source' complies with the alsa control naming scheme */ HDSP_CLOCK_SOURCE("Sample Clock Source", 0), { @@ -3353,7 +3353,7 @@ HDSP_AUTOSYNC_SAMPLE_RATE("External Rate", 0), HDSP_WC_SYNC_CHECK("Word Clock Lock Status", 0), HDSP_SPDIF_SYNC_CHECK("SPDIF Lock Status", 0), HDSP_ADATSYNC_SYNC_CHECK("ADAT Sync Lock Status", 0), -HDSP_LINE_OUT("Line Out", 0), +HDSP_TOGGLE_SETTING("Line Out", HDSP_LineOut), HDSP_PRECISE_POINTER("Precise Pointer", 0), HDSP_USE_MIDI_TASKLET("Use Midi Tasklet", 0), }; @@ -3670,7 +3670,9 @@ static struct snd_kcontrol_new snd_hdsp_rpm_controls[] = { HDSP_MIXER("Mixer", 0) }; -static struct snd_kcontrol_new snd_hdsp_96xx_aeb = HDSP_AEB("Analog Extension Board", 0); +static struct snd_kcontrol_new snd_hdsp_96xx_aeb = + HDSP_TOGGLE_SETTING("Analog Extension Board", + HDSP_AnalogExtensionBoard); static struct snd_kcontrol_new snd_hdsp_adat_sync_check = HDSP_ADAT_SYNC_CHECK; static int snd_hdsp_create_controls(struct snd_card *card, struct hdsp *hdsp) @@ -4093,7 +4095,9 @@ snd_hdsp_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer) } snd_iprintf(buffer, "Phones Gain : %s\n", tmp); - snd_iprintf(buffer, "XLR Breakout Cable : %s\n", hdsp_xlr_breakout_cable(hdsp) ? "yes" : "no"); + snd_iprintf(buffer, "XLR Breakout Cable : %s\n", + hdsp_toggle_setting(hdsp, HDSP_XLRBreakoutCable) ? + "yes" : "no"); if (hdsp->control_register & HDSP_AnalogExtensionBoard) snd_iprintf(buffer, "AEB : on (ADAT1 internal)\n"); @@ -5124,29 +5128,38 @@ static int snd_hdsp_hwdep_ioctl(struct snd_hwdep *hw, struct file *file, unsigne for (i = 0; i < ((hdsp->io_type != Multiface && hdsp->io_type != RPM && hdsp->io_type != H9632) ? 3 : 1); ++i) info.adat_sync_check[i] = (unsigned char)hdsp_adat_sync_check(hdsp, i); info.spdif_in = (unsigned char)hdsp_spdif_in(hdsp); - info.spdif_out = (unsigned char)hdsp_spdif_out(hdsp); - info.spdif_professional = (unsigned char)hdsp_spdif_professional(hdsp); - info.spdif_emphasis = (unsigned char)hdsp_spdif_emphasis(hdsp); - info.spdif_nonaudio = (unsigned char)hdsp_spdif_nonaudio(hdsp); + info.spdif_out = (unsigned char)hdsp_toggle_setting(hdsp, + HDSP_SPDIFOpticalOut); + info.spdif_professional = (unsigned char) + hdsp_toggle_setting(hdsp, HDSP_SPDIFProfessional); + info.spdif_emphasis = (unsigned char) + hdsp_toggle_setting(hdsp, HDSP_SPDIFEmphasis); + info.spdif_nonaudio = (unsigned char) + hdsp_toggle_setting(hdsp, HDSP_SPDIFNonAudio); info.spdif_sample_rate = hdsp_spdif_sample_rate(hdsp); info.system_sample_rate = hdsp->system_sample_rate; info.autosync_sample_rate = hdsp_external_sample_rate(hdsp); info.system_clock_mode = (unsigned char)hdsp_system_clock_mode(hdsp); info.clock_source = (unsigned char)hdsp_clock_source(hdsp); info.autosync_ref = (unsigned char)hdsp_autosync_ref(hdsp); - info.line_out = (unsigned char)hdsp_line_out(hdsp); + info.line_out = (unsigned char) + hdsp_toggle_setting(hdsp, HDSP_LineOut); if (hdsp->io_type == H9632) { info.da_gain = (unsigned char)hdsp_da_gain(hdsp); info.ad_gain = (unsigned char)hdsp_ad_gain(hdsp); info.phone_gain = (unsigned char)hdsp_phone_gain(hdsp); - info.xlr_breakout_cable = (unsigned char)hdsp_xlr_breakout_cable(hdsp); + info.xlr_breakout_cable = + (unsigned char)hdsp_toggle_setting(hdsp, + HDSP_XLRBreakoutCable); } else if (hdsp->io_type == RPM) { info.da_gain = (unsigned char) hdsp_rpm_input12(hdsp); info.ad_gain = (unsigned char) hdsp_rpm_input34(hdsp); } if (hdsp->io_type == H9632 || hdsp->io_type == H9652) - info.analog_extension_board = (unsigned char)hdsp_aeb(hdsp); + info.analog_extension_board = + (unsigned char)hdsp_toggle_setting(hdsp, + HDSP_AnalogExtensionBoard); spin_unlock_irqrestore(&hdsp->lock, flags); if (copy_to_user(argp, &info, sizeof(info))) return -EFAULT; -- cgit v0.10.2 From 49ba4f94bddb7f5272c4596d505f94355cc3fbd2 Mon Sep 17 00:00:00 2001 From: Adrian Knoth Date: Tue, 15 Jan 2013 18:52:23 +0100 Subject: ALSA: hdsp - Remove obsolete settings functions With HDSP_TOGGLE_SETTING in place, these functions are no longer required. Removing them makes the code DRY and considerably shorter. Signed-off-by: Adrian Knoth Signed-off-by: Takashi Iwai diff --git a/sound/pci/rme9652/hdsp.c b/sound/pci/rme9652/hdsp.c index ae79695..94084cd 100644 --- a/sound/pci/rme9652/hdsp.c +++ b/sound/pci/rme9652/hdsp.c @@ -1771,185 +1771,6 @@ static int snd_hdsp_put_toggle_setting(struct snd_kcontrol *kcontrol, return change; } - -#define HDSP_SPDIF_OUT(xname, xindex) \ -{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ - .info = snd_hdsp_info_spdif_bits, \ - .get = snd_hdsp_get_spdif_out, .put = snd_hdsp_put_spdif_out } - -static int hdsp_spdif_out(struct hdsp *hdsp) -{ - return (hdsp->control_register & HDSP_SPDIFOpticalOut) ? 1 : 0; -} - -static int hdsp_set_spdif_output(struct hdsp *hdsp, int out) -{ - if (out) - hdsp->control_register |= HDSP_SPDIFOpticalOut; - else - hdsp->control_register &= ~HDSP_SPDIFOpticalOut; - hdsp_write(hdsp, HDSP_controlRegister, hdsp->control_register); - return 0; -} - -#define snd_hdsp_info_spdif_bits snd_ctl_boolean_mono_info - -static int snd_hdsp_get_spdif_out(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) -{ - struct hdsp *hdsp = snd_kcontrol_chip(kcontrol); - - ucontrol->value.integer.value[0] = hdsp_spdif_out(hdsp); - return 0; -} - -static int snd_hdsp_put_spdif_out(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) -{ - struct hdsp *hdsp = snd_kcontrol_chip(kcontrol); - int change; - unsigned int val; - - if (!snd_hdsp_use_is_exclusive(hdsp)) - return -EBUSY; - val = ucontrol->value.integer.value[0] & 1; - spin_lock_irq(&hdsp->lock); - change = (int)val != hdsp_spdif_out(hdsp); - hdsp_set_spdif_output(hdsp, val); - spin_unlock_irq(&hdsp->lock); - return change; -} - -#define HDSP_SPDIF_PROFESSIONAL(xname, xindex) \ -{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ - .info = snd_hdsp_info_spdif_bits, \ - .get = snd_hdsp_get_spdif_professional, .put = snd_hdsp_put_spdif_professional } - -static int hdsp_spdif_professional(struct hdsp *hdsp) -{ - return (hdsp->control_register & HDSP_SPDIFProfessional) ? 1 : 0; -} - -static int hdsp_set_spdif_professional(struct hdsp *hdsp, int val) -{ - if (val) - hdsp->control_register |= HDSP_SPDIFProfessional; - else - hdsp->control_register &= ~HDSP_SPDIFProfessional; - hdsp_write(hdsp, HDSP_controlRegister, hdsp->control_register); - return 0; -} - -static int snd_hdsp_get_spdif_professional(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) -{ - struct hdsp *hdsp = snd_kcontrol_chip(kcontrol); - - ucontrol->value.integer.value[0] = hdsp_spdif_professional(hdsp); - return 0; -} - -static int snd_hdsp_put_spdif_professional(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) -{ - struct hdsp *hdsp = snd_kcontrol_chip(kcontrol); - int change; - unsigned int val; - - if (!snd_hdsp_use_is_exclusive(hdsp)) - return -EBUSY; - val = ucontrol->value.integer.value[0] & 1; - spin_lock_irq(&hdsp->lock); - change = (int)val != hdsp_spdif_professional(hdsp); - hdsp_set_spdif_professional(hdsp, val); - spin_unlock_irq(&hdsp->lock); - return change; -} - -#define HDSP_SPDIF_EMPHASIS(xname, xindex) \ -{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ - .info = snd_hdsp_info_spdif_bits, \ - .get = snd_hdsp_get_spdif_emphasis, .put = snd_hdsp_put_spdif_emphasis } - -static int hdsp_spdif_emphasis(struct hdsp *hdsp) -{ - return (hdsp->control_register & HDSP_SPDIFEmphasis) ? 1 : 0; -} - -static int hdsp_set_spdif_emphasis(struct hdsp *hdsp, int val) -{ - if (val) - hdsp->control_register |= HDSP_SPDIFEmphasis; - else - hdsp->control_register &= ~HDSP_SPDIFEmphasis; - hdsp_write(hdsp, HDSP_controlRegister, hdsp->control_register); - return 0; -} - -static int snd_hdsp_get_spdif_emphasis(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) -{ - struct hdsp *hdsp = snd_kcontrol_chip(kcontrol); - - ucontrol->value.integer.value[0] = hdsp_spdif_emphasis(hdsp); - return 0; -} - -static int snd_hdsp_put_spdif_emphasis(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) -{ - struct hdsp *hdsp = snd_kcontrol_chip(kcontrol); - int change; - unsigned int val; - - if (!snd_hdsp_use_is_exclusive(hdsp)) - return -EBUSY; - val = ucontrol->value.integer.value[0] & 1; - spin_lock_irq(&hdsp->lock); - change = (int)val != hdsp_spdif_emphasis(hdsp); - hdsp_set_spdif_emphasis(hdsp, val); - spin_unlock_irq(&hdsp->lock); - return change; -} - -#define HDSP_SPDIF_NON_AUDIO(xname, xindex) \ -{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ - .info = snd_hdsp_info_spdif_bits, \ - .get = snd_hdsp_get_spdif_nonaudio, .put = snd_hdsp_put_spdif_nonaudio } - -static int hdsp_spdif_nonaudio(struct hdsp *hdsp) -{ - return (hdsp->control_register & HDSP_SPDIFNonAudio) ? 1 : 0; -} - -static int hdsp_set_spdif_nonaudio(struct hdsp *hdsp, int val) -{ - if (val) - hdsp->control_register |= HDSP_SPDIFNonAudio; - else - hdsp->control_register &= ~HDSP_SPDIFNonAudio; - hdsp_write(hdsp, HDSP_controlRegister, hdsp->control_register); - return 0; -} - -static int snd_hdsp_get_spdif_nonaudio(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) -{ - struct hdsp *hdsp = snd_kcontrol_chip(kcontrol); - - ucontrol->value.integer.value[0] = hdsp_spdif_nonaudio(hdsp); - return 0; -} - -static int snd_hdsp_put_spdif_nonaudio(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) -{ - struct hdsp *hdsp = snd_kcontrol_chip(kcontrol); - int change; - unsigned int val; - - if (!snd_hdsp_use_is_exclusive(hdsp)) - return -EBUSY; - val = ucontrol->value.integer.value[0] & 1; - spin_lock_irq(&hdsp->lock); - change = (int)val != hdsp_spdif_nonaudio(hdsp); - hdsp_set_spdif_nonaudio(hdsp, val); - spin_unlock_irq(&hdsp->lock); - return change; -} - #define HDSP_SPDIF_SAMPLE_RATE(xname, xindex) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ .name = xname, \ @@ -2549,114 +2370,6 @@ static int snd_hdsp_put_phone_gain(struct snd_kcontrol *kcontrol, struct snd_ctl return change; } -#define HDSP_XLR_BREAKOUT_CABLE(xname, xindex) \ -{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ - .name = xname, \ - .index = xindex, \ - .info = snd_hdsp_info_xlr_breakout_cable, \ - .get = snd_hdsp_get_xlr_breakout_cable, \ - .put = snd_hdsp_put_xlr_breakout_cable \ -} - -static int hdsp_xlr_breakout_cable(struct hdsp *hdsp) -{ - if (hdsp->control_register & HDSP_XLRBreakoutCable) - return 1; - return 0; -} - -static int hdsp_set_xlr_breakout_cable(struct hdsp *hdsp, int mode) -{ - if (mode) - hdsp->control_register |= HDSP_XLRBreakoutCable; - else - hdsp->control_register &= ~HDSP_XLRBreakoutCable; - hdsp_write(hdsp, HDSP_controlRegister, hdsp->control_register); - return 0; -} - -#define snd_hdsp_info_xlr_breakout_cable snd_ctl_boolean_mono_info - -static int snd_hdsp_get_xlr_breakout_cable(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) -{ - struct hdsp *hdsp = snd_kcontrol_chip(kcontrol); - - ucontrol->value.enumerated.item[0] = hdsp_xlr_breakout_cable(hdsp); - return 0; -} - -static int snd_hdsp_put_xlr_breakout_cable(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) -{ - struct hdsp *hdsp = snd_kcontrol_chip(kcontrol); - int change; - int val; - - if (!snd_hdsp_use_is_exclusive(hdsp)) - return -EBUSY; - val = ucontrol->value.integer.value[0] & 1; - spin_lock_irq(&hdsp->lock); - change = (int)val != hdsp_xlr_breakout_cable(hdsp); - hdsp_set_xlr_breakout_cable(hdsp, val); - spin_unlock_irq(&hdsp->lock); - return change; -} - -/* (De)activates old RME Analog Extension Board - These are connected to the internal ADAT connector - Switching this on desactivates external ADAT -*/ -#define HDSP_AEB(xname, xindex) \ -{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ - .name = xname, \ - .index = xindex, \ - .info = snd_hdsp_info_aeb, \ - .get = snd_hdsp_get_aeb, \ - .put = snd_hdsp_put_aeb \ -} - -static int hdsp_aeb(struct hdsp *hdsp) -{ - if (hdsp->control_register & HDSP_AnalogExtensionBoard) - return 1; - return 0; -} - -static int hdsp_set_aeb(struct hdsp *hdsp, int mode) -{ - if (mode) - hdsp->control_register |= HDSP_AnalogExtensionBoard; - else - hdsp->control_register &= ~HDSP_AnalogExtensionBoard; - hdsp_write(hdsp, HDSP_controlRegister, hdsp->control_register); - return 0; -} - -#define snd_hdsp_info_aeb snd_ctl_boolean_mono_info - -static int snd_hdsp_get_aeb(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) -{ - struct hdsp *hdsp = snd_kcontrol_chip(kcontrol); - - ucontrol->value.enumerated.item[0] = hdsp_aeb(hdsp); - return 0; -} - -static int snd_hdsp_put_aeb(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) -{ - struct hdsp *hdsp = snd_kcontrol_chip(kcontrol); - int change; - int val; - - if (!snd_hdsp_use_is_exclusive(hdsp)) - return -EBUSY; - val = ucontrol->value.integer.value[0] & 1; - spin_lock_irq(&hdsp->lock); - change = (int)val != hdsp_aeb(hdsp); - hdsp_set_aeb(hdsp, val); - spin_unlock_irq(&hdsp->lock); - return change; -} - #define HDSP_PREF_SYNC_REF(xname, xindex) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ .name = xname, \ @@ -2845,58 +2558,6 @@ static int snd_hdsp_get_autosync_ref(struct snd_kcontrol *kcontrol, struct snd_c return 0; } -#define HDSP_LINE_OUT(xname, xindex) \ -{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ - .name = xname, \ - .index = xindex, \ - .info = snd_hdsp_info_line_out, \ - .get = snd_hdsp_get_line_out, \ - .put = snd_hdsp_put_line_out \ -} - -static int hdsp_line_out(struct hdsp *hdsp) -{ - return (hdsp->control_register & HDSP_LineOut) ? 1 : 0; -} - -static int hdsp_set_line_output(struct hdsp *hdsp, int out) -{ - if (out) - hdsp->control_register |= HDSP_LineOut; - else - hdsp->control_register &= ~HDSP_LineOut; - hdsp_write(hdsp, HDSP_controlRegister, hdsp->control_register); - return 0; -} - -#define snd_hdsp_info_line_out snd_ctl_boolean_mono_info - -static int snd_hdsp_get_line_out(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) -{ - struct hdsp *hdsp = snd_kcontrol_chip(kcontrol); - - spin_lock_irq(&hdsp->lock); - ucontrol->value.integer.value[0] = hdsp_line_out(hdsp); - spin_unlock_irq(&hdsp->lock); - return 0; -} - -static int snd_hdsp_put_line_out(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) -{ - struct hdsp *hdsp = snd_kcontrol_chip(kcontrol); - int change; - unsigned int val; - - if (!snd_hdsp_use_is_exclusive(hdsp)) - return -EBUSY; - val = ucontrol->value.integer.value[0] & 1; - spin_lock_irq(&hdsp->lock); - change = (int)val != hdsp_line_out(hdsp); - hdsp_set_line_output(hdsp, val); - spin_unlock_irq(&hdsp->lock); - return change; -} - #define HDSP_PRECISE_POINTER(xname, xindex) \ { .iface = SNDRV_CTL_ELEM_IFACE_CARD, \ .name = xname, \ -- cgit v0.10.2 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 diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index daf5ee3..483850f 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 v0.10.2 From c98137bfcb4afd4415864258dd8ecf05c2bb5843 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 16 Jan 2013 15:03:52 +0900 Subject: ASoC: arizona: Don't request FLL lock IRQ We only log the result and since the interrupt triggers on loss of lock during shutdown this may lead to spurious interrupts during shutdown delaying the process. Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c index f919a3a..a12dd16 100644 --- a/sound/soc/codecs/arizona.c +++ b/sound/soc/codecs/arizona.c @@ -897,17 +897,6 @@ int arizona_init_dai(struct arizona_priv *priv, int id) } EXPORT_SYMBOL_GPL(arizona_init_dai); -static irqreturn_t arizona_fll_lock(int irq, void *data) -{ - struct arizona_fll *fll = data; - - arizona_fll_dbg(fll, "Lock status changed\n"); - - complete(&fll->lock); - - return IRQ_HANDLED; -} - static irqreturn_t arizona_fll_clock_ok(int irq, void *data) { struct arizona_fll *fll = data; @@ -1147,7 +1136,6 @@ int arizona_init_fll(struct arizona *arizona, int id, int base, int lock_irq, { int ret; - init_completion(&fll->lock); init_completion(&fll->ok); fll->id = id; @@ -1158,13 +1146,6 @@ int arizona_init_fll(struct arizona *arizona, int id, int base, int lock_irq, snprintf(fll->clock_ok_name, sizeof(fll->clock_ok_name), "FLL%d clock OK", id); - ret = arizona_request_irq(arizona, lock_irq, fll->lock_name, - arizona_fll_lock, fll); - if (ret != 0) { - dev_err(arizona->dev, "Failed to get FLL%d lock IRQ: %d\n", - id, ret); - } - ret = arizona_request_irq(arizona, ok_irq, fll->clock_ok_name, arizona_fll_clock_ok, fll); if (ret != 0) { diff --git a/sound/soc/codecs/arizona.h b/sound/soc/codecs/arizona.h index 697ff6f..116372c 100644 --- a/sound/soc/codecs/arizona.h +++ b/sound/soc/codecs/arizona.h @@ -197,7 +197,6 @@ struct arizona_fll { int id; unsigned int base; unsigned int vco_mult; - struct completion lock; struct completion ok; unsigned int fref; unsigned int fout; -- cgit v0.10.2 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 diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index e878a9e..1fa71ac 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 1fa71ac..68947fa 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 68947fa..c33e019 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 v0.10.2 From 5d163336a77af9c1b4d6d08cbc8b1279df5f579e Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 15 Jan 2013 20:18:23 -0800 Subject: ASoC: SND_SOC_DAIFMT_NB_NF become 0 as default settings Current soc-dai.h defines SND_SOC_DAIFMT_NB_NF as (1 << 8), but normal bit clock / normal frame should be default settings (= 0). This patch fixup SND_SOC_DAIFMT_NB_NF as (0 << 8). Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown diff --git a/include/sound/soc-dai.h b/include/sound/soc-dai.h index 3953cea..90dc004 100644 --- a/include/sound/soc-dai.h +++ b/include/sound/soc-dai.h @@ -53,7 +53,7 @@ struct snd_compr_stream; * Specifies whether the DAI can also support inverted clocks for the specified * format. */ -#define SND_SOC_DAIFMT_NB_NF (1 << 8) /* normal bit clock + frame */ +#define SND_SOC_DAIFMT_NB_NF (0 << 8) /* normal bit clock + frame */ #define SND_SOC_DAIFMT_NB_IF (2 << 8) /* normal BCLK + inv FRM */ #define SND_SOC_DAIFMT_IB_NF (3 << 8) /* invert BCLK + nor FRM */ #define SND_SOC_DAIFMT_IB_IF (4 << 8) /* invert BCLK + FRM */ -- cgit v0.10.2 From e43fc6af2538a7a30e695227928eed8a29aa4f63 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Wed, 16 Jan 2013 12:09:45 +0100 Subject: ASoC: fsi: Remove __devinitconst __devinitconst and friends have recently been removed and must not be used anymore. Signed-off-by: Thierry Reding Signed-off-by: Mark Brown diff --git a/sound/soc/sh/fsi.c b/sound/soc/sh/fsi.c index 9157612..c724026 100644 --- a/sound/soc/sh/fsi.c +++ b/sound/soc/sh/fsi.c @@ -2160,7 +2160,7 @@ static struct fsi_core fsi2_core = { .b_mclk = B_MST_CTLR, }; -static struct of_device_id fsi_of_match[] __devinitconst = { +static struct of_device_id fsi_of_match[] = { { .compatible = "renesas,sh_fsi", .data = &fsi1_core}, { .compatible = "renesas,sh_fsi2", .data = &fsi2_core}, {}, -- cgit v0.10.2 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 diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index c33e019..05dfeb7 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 05dfeb7..171364a 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 171364a..73900d9 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 73900d9..0d6c4f7 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 0d6c4f7..186a554 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 6ba5805..d4abf87 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 186a554..1ea9c2d 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 d4abf87..59d08c6 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h index 59d08c6..dd0818b 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 v0.10.2 From 6995b8cb9622bf574ac6f309e69288e7d0f76ece Mon Sep 17 00:00:00 2001 From: Lucas Stach Date: Wed, 16 Jan 2013 13:05:12 +0100 Subject: ASoC: tegra: add tegra machine driver using wm9712 codec This adds a very simple machine driver using the Wolfson wm9712 AC97 codec. Signed-off-by: Lucas Stach Reviewed-by: Stephen Warren Signed-off-by: Mark Brown diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-wm9712.txt b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-wm9712.txt new file mode 100644 index 0000000..be35d34 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-wm9712.txt @@ -0,0 +1,51 @@ +NVIDIA Tegra audio complex + +Required properties: +- compatible : "nvidia,tegra-audio-wm9712" +- nvidia,model : The user-visible name of this sound complex. +- nvidia,audio-routing : A list of the connections between audio components. + Each entry is a pair of strings, the first being the connection's sink, + the second being the connection's source. Valid names for sources and + sinks are the WM9712's pins, and the jacks on the board: + + WM9712 pins: + + * MONOOUT + * HPOUTL + * HPOUTR + * LOUT2 + * ROUT2 + * OUT3 + * LINEINL + * LINEINR + * PHONE + * PCBEEP + * MIC1 + * MIC2 + * Mic Bias + + Board connectors: + + * Headphone + * LineIn + * Mic + +- nvidia,ac97-controller : The phandle of the Tegra AC97 controller + + +Example: + +sound { + compatible = "nvidia,tegra-audio-wm9712-colibri_t20", + "nvidia,tegra-audio-wm9712"; + nvidia,model = "Toradex Colibri T20"; + + nvidia,audio-routing = + "Headphone", "HPOUTL", + "Headphone", "HPOUTR", + "LineIn", "LINEINL", + "LineIn", "LINEINR", + "Mic", "MIC1"; + + nvidia,ac97-controller = <&ac97>; +}; diff --git a/sound/soc/tegra/Kconfig b/sound/soc/tegra/Kconfig index 4b3a2b8..dbc27ce 100644 --- a/sound/soc/tegra/Kconfig +++ b/sound/soc/tegra/Kconfig @@ -80,6 +80,15 @@ config SND_SOC_TEGRA_WM8903 boards using the WM8093 codec. Currently, the supported boards are Harmony, Ventana, Seaboard, Kaen, and Aebl. +config SND_SOC_TEGRA_WM9712 + tristate "SoC Audio support for Tegra boards using a WM9712 codec" + depends on SND_SOC_TEGRA && ARCH_TEGRA_2x_SOC + select SND_SOC_TEGRA20_AC97 + select SND_SOC_WM9712 + help + Say Y or M here if you want to add support for SoC audio on Tegra + boards using the WM9712 (or compatible) codec. + config SND_SOC_TEGRA_TRIMSLICE tristate "SoC Audio support for TrimSlice board" depends on SND_SOC_TEGRA && I2C diff --git a/sound/soc/tegra/Makefile b/sound/soc/tegra/Makefile index 02513d9..416a14b 100644 --- a/sound/soc/tegra/Makefile +++ b/sound/soc/tegra/Makefile @@ -20,10 +20,12 @@ obj-$(CONFIG_SND_SOC_TEGRA30_I2S) += snd-soc-tegra30-i2s.o # Tegra machine Support snd-soc-tegra-wm8753-objs := tegra_wm8753.o snd-soc-tegra-wm8903-objs := tegra_wm8903.o +snd-soc-tegra-wm9712-objs := tegra_wm9712.o snd-soc-tegra-trimslice-objs := trimslice.o snd-soc-tegra-alc5632-objs := tegra_alc5632.o obj-$(CONFIG_SND_SOC_TEGRA_WM8753) += snd-soc-tegra-wm8753.o obj-$(CONFIG_SND_SOC_TEGRA_WM8903) += snd-soc-tegra-wm8903.o +obj-$(CONFIG_SND_SOC_TEGRA_WM9712) += snd-soc-tegra-wm9712.o obj-$(CONFIG_SND_SOC_TEGRA_TRIMSLICE) += snd-soc-tegra-trimslice.o obj-$(CONFIG_SND_SOC_TEGRA_ALC5632) += snd-soc-tegra-alc5632.o diff --git a/sound/soc/tegra/tegra_wm9712.c b/sound/soc/tegra/tegra_wm9712.c new file mode 100644 index 0000000..cdbd2f0 --- /dev/null +++ b/sound/soc/tegra/tegra_wm9712.c @@ -0,0 +1,176 @@ +/* + * tegra20_wm9712.c - Tegra machine ASoC driver for boards using WM9712 codec. + * + * Copyright 2012 Lucas Stach + * + * Partly based on code copyright/by: + * Copyright 2011,2012 Toradex Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define DRV_NAME "tegra-snd-wm9712" + +struct tegra_wm9712 { + struct platform_device *codec; +}; + +static const struct snd_soc_dapm_widget tegra_wm9712_dapm_widgets[] = { + SND_SOC_DAPM_HP("Headphone", NULL), + SND_SOC_DAPM_LINE("LineIn", NULL), + SND_SOC_DAPM_MIC("Mic", NULL), +}; + +static int tegra_wm9712_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_codec *codec = codec_dai->codec; + struct snd_soc_dapm_context *dapm = &codec->dapm; + + snd_soc_dapm_force_enable_pin(dapm, "Mic Bias"); + + return snd_soc_dapm_sync(dapm); +} + +static struct snd_soc_dai_link tegra_wm9712_dai = { + .name = "AC97 HiFi", + .stream_name = "AC97 HiFi", + .cpu_dai_name = "tegra-ac97-pcm", + .codec_dai_name = "wm9712-hifi", + .codec_name = "wm9712-codec", + .init = tegra_wm9712_init, +}; + +static struct snd_soc_card snd_soc_tegra_wm9712 = { + .name = "tegra-wm9712", + .owner = THIS_MODULE, + .dai_link = &tegra_wm9712_dai, + .num_links = 1, + + .dapm_widgets = tegra_wm9712_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(tegra_wm9712_dapm_widgets), + .fully_routed = true, +}; + +static int tegra_wm9712_driver_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct snd_soc_card *card = &snd_soc_tegra_wm9712; + struct tegra_wm9712 *machine; + int ret; + + if (!pdev->dev.of_node) { + dev_err(&pdev->dev, "No platform data supplied\n"); + return -EINVAL; + } + + machine = devm_kzalloc(&pdev->dev, sizeof(struct tegra_wm9712), + GFP_KERNEL); + if (!machine) { + dev_err(&pdev->dev, "Can't allocate tegra_wm9712 struct\n"); + return -ENOMEM; + } + + card->dev = &pdev->dev; + platform_set_drvdata(pdev, card); + snd_soc_card_set_drvdata(card, machine); + + machine->codec = platform_device_alloc("wm9712-codec", -1); + if (!machine->codec) { + dev_err(&pdev->dev, "Can't allocate wm9712 platform device\n"); + return -ENOMEM; + } + + ret = platform_device_add(machine->codec); + if (ret) + goto codec_put; + + ret = snd_soc_of_parse_card_name(card, "nvidia,model"); + if (ret) + goto codec_unregister; + + ret = snd_soc_of_parse_audio_routing(card, "nvidia,audio-routing"); + if (ret) + goto codec_unregister; + + tegra_wm9712_dai.cpu_of_node = of_parse_phandle(np, + "nvidia,ac97-controller", 0); + if (!tegra_wm9712_dai.cpu_of_node) { + dev_err(&pdev->dev, + "Property 'nvidia,ac97-controller' missing or invalid\n"); + ret = -EINVAL; + goto codec_unregister; + } + + tegra_wm9712_dai.platform_of_node = tegra_wm9712_dai.cpu_of_node; + + ret = snd_soc_register_card(card); + if (ret) { + dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", + ret); + goto codec_unregister; + } + + return 0; + +codec_unregister: + platform_device_del(machine->codec); +codec_put: + platform_device_put(machine->codec); + return ret; +} + +static int tegra_wm9712_driver_remove(struct platform_device *pdev) +{ + struct snd_soc_card *card = platform_get_drvdata(pdev); + struct tegra_wm9712 *machine = snd_soc_card_get_drvdata(card); + + snd_soc_unregister_card(card); + + platform_device_unregister(machine->codec); + + return 0; +} + +static const struct of_device_id tegra_wm9712_of_match[] __devinitconst = { + { .compatible = "nvidia,tegra-audio-wm9712", }, + {}, +}; + +static struct platform_driver tegra_wm9712_driver = { + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + .pm = &snd_soc_pm_ops, + .of_match_table = tegra_wm9712_of_match, + }, + .probe = tegra_wm9712_driver_probe, + .remove = tegra_wm9712_driver_remove, +}; +module_platform_driver(tegra_wm9712_driver); + +MODULE_AUTHOR("Lucas Stach"); +MODULE_DESCRIPTION("Tegra+WM9712 machine ASoC driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:" DRV_NAME); +MODULE_DEVICE_TABLE(of, tegra_wm9712_of_match); -- cgit v0.10.2 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 diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index a7eed73..f975735 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 1ea9c2d..878556b 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 dd0818b..142a571 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 878556b..9c06749 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 142a571..43a8482 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 9c06749..fc3f241 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 43a8482..a0486b1 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index fc3f241..f946714 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 a0486b1..7b14e9c 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index f946714..ef4c04a 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 v0.10.2 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 diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig index 4004d40..4466bf6 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 f975735..9d2dfad 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 v0.10.2 From c712326d6c1e74678791d5864cd2ed283e1cc572 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 16 Jan 2013 16:59:04 +0900 Subject: ASoC: wm_adsp: Implement support for coefficeint file format 1 Implement support for a new revision of the coefficeint file format for ADSP cores. Since coefficient file format 0 has not been widely deployed and is very unlikely to ever be used with this driver code support for it has been removed. Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index 58cac07..5841285 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -686,6 +686,16 @@ static int wm_adsp_load_coeff(struct wm_adsp *dsp) return -EINVAL; } + switch (be32_to_cpu(hdr->rev) & 0xff) { + case 1: + break; + default: + adsp_err(dsp, "%s: Unsupported coefficient file format %d\n", + file, be32_to_cpu(hdr->rev) & 0xff); + ret = -EINVAL; + goto out_fw; + } + adsp_dbg(dsp, "%s: v%d.%d.%d\n", file, (le32_to_cpu(hdr->ver) >> 16) & 0xff, (le32_to_cpu(hdr->ver) >> 8) & 0xff, @@ -698,8 +708,8 @@ static int wm_adsp_load_coeff(struct wm_adsp *dsp) pos - firmware->size > sizeof(*blk)) { blk = (void*)(&firmware->data[pos]); - type = be32_to_cpu(blk->type) & 0xff; - offset = le32_to_cpu(blk->offset) & 0xffffff; + type = le16_to_cpu(blk->type); + offset = le16_to_cpu(blk->offset); adsp_dbg(dsp, "%s.%d: %x v%d.%d.%d\n", file, blocks, le32_to_cpu(blk->id), @@ -712,10 +722,10 @@ static int wm_adsp_load_coeff(struct wm_adsp *dsp) reg = 0; region_name = "Unknown"; switch (type) { - case WMFW_NAME_TEXT: - case WMFW_INFO_TEXT: + case (WMFW_NAME_TEXT << 8): + case (WMFW_INFO_TEXT << 8): break; - case WMFW_ABSOLUTE: + case (WMFW_ABSOLUTE << 8): region_name = "register"; reg = offset; break; diff --git a/sound/soc/codecs/wmfw.h b/sound/soc/codecs/wmfw.h index 5632ded..ef16336 100644 --- a/sound/soc/codecs/wmfw.h +++ b/sound/soc/codecs/wmfw.h @@ -93,15 +93,20 @@ struct wmfw_adsp2_alg_hdr { struct wmfw_coeff_hdr { u8 magic[4]; __le32 len; - __le32 ver; + union { + __be32 rev; + __le32 ver; + }; + union { + __be32 core; + __le32 core_ver; + }; u8 data[]; } __packed; struct wmfw_coeff_item { - union { - __be32 type; - __le32 offset; - }; + __le16 offset; + __le16 type; __le32 id; __le32 ver; __le32 sr; -- cgit v0.10.2 From 82e993fac48674b0231b835516e0fdae94285b9b Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 11 Jan 2013 22:59:48 +0000 Subject: ASoC: wm2200: Add controls for firmware enumeration Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm2200.c b/sound/soc/codecs/wm2200.c index 90aae49..d400291 100644 --- a/sound/soc/codecs/wm2200.c +++ b/sound/soc/codecs/wm2200.c @@ -1551,6 +1551,10 @@ static int wm2200_probe(struct snd_soc_codec *codec) return ret; } + ret = snd_soc_add_codec_controls(codec, wm_adsp_fw_controls, 2); + if (ret != 0) + return ret; + return ret; } -- cgit v0.10.2 From e5ddd303215d1e5e89d06056d17673b18219ace3 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 11 Jan 2013 22:59:35 +0000 Subject: ASoC: wm5102: Add controls for firmware selection Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm5102.c b/sound/soc/codecs/wm5102.c index 2adcfba..5e85b64 100644 --- a/sound/soc/codecs/wm5102.c +++ b/sound/soc/codecs/wm5102.c @@ -1579,6 +1579,10 @@ static int wm5102_codec_probe(struct snd_soc_codec *codec) if (ret != 0) return ret; + ret = snd_soc_add_codec_controls(codec, wm_adsp_fw_controls, 1); + if (ret != 0) + return ret; + snd_soc_dapm_disable_pin(&codec->dapm, "HAPTICS"); priv->core.arizona->dapm = &codec->dapm; -- cgit v0.10.2 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 diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index 02fe0d1..6feaec4 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 8d09325..db7635c 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 04dd3b6..087cabb 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 v0.10.2 From 94e205bfb73b6d19028dbd40404219fdeb27175e Mon Sep 17 00:00:00 2001 From: Chris Rattray Date: Fri, 18 Jan 2013 08:43:09 +0000 Subject: ASoC: wm_adsp: Set ADSP1 clock rate to match sys clock Sets the ADSP1 clock rate to match the system clock rate. To support this the codec driver provides details of register containing the system clock control bits. Signed-off-by: Chris Rattray Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index 5841285..9e31162 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -103,6 +103,13 @@ #define ADSP1_START_SHIFT 0 /* DSP1_START */ #define ADSP1_START_WIDTH 1 /* DSP1_START */ +/* + * ADSP1 Control 31 + */ +#define ADSP1_CLK_SEL_MASK 0x0007 /* CLK_SEL_ENA */ +#define ADSP1_CLK_SEL_SHIFT 0 /* CLK_SEL_ENA */ +#define ADSP1_CLK_SEL_WIDTH 3 /* CLK_SEL_ENA */ + #define ADSP2_CONTROL 0 #define ADSP2_CLOCKING 1 #define ADSP2_STATUS1 4 @@ -806,12 +813,38 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w, struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec); struct wm_adsp *dsp = &dsps[w->shift]; int ret; + int val; switch (event) { case SND_SOC_DAPM_POST_PMU: regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, ADSP1_SYS_ENA, ADSP1_SYS_ENA); + /* + * For simplicity set the DSP clock rate to be the + * SYSCLK rate rather than making it configurable. + */ + if(dsp->sysclk_reg) { + ret = regmap_read(dsp->regmap, dsp->sysclk_reg, &val); + if (ret != 0) { + adsp_err(dsp, "Failed to read SYSCLK state: %d\n", + ret); + return ret; + } + + val = (val & dsp->sysclk_mask) + >> dsp->sysclk_shift; + + ret = regmap_update_bits(dsp->regmap, + dsp->base + ADSP1_CONTROL_31, + ADSP1_CLK_SEL_MASK, val); + if (ret != 0) { + adsp_err(dsp, "Failed to set clock rate: %d\n", + ret); + return ret; + } + } + ret = wm_adsp_load(dsp); if (ret != 0) goto err; diff --git a/sound/soc/codecs/wm_adsp.h b/sound/soc/codecs/wm_adsp.h index 41206d7..cb8871a 100644 --- a/sound/soc/codecs/wm_adsp.h +++ b/sound/soc/codecs/wm_adsp.h @@ -40,6 +40,9 @@ struct wm_adsp { struct regmap *regmap; int base; + int sysclk_reg; + int sysclk_mask; + int sysclk_shift; struct list_head alg_regions; -- cgit v0.10.2 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 diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index ef4c04a..7444d2e 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 7444d2e..ebb5584 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 7b14e9c..f6b88cd 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index ebb5584..edec98f 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index edec98f..cadfe65 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index cadfe65..e9af9ab 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index e9af9ab..f0d83b2 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 f6b88cd..594a9cc 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 71a8894..70b0e32 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 9d2dfad..456ebc7 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 v0.10.2 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 diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 456ebc7..fe3e082 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index f0d83b2..174806e 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 v0.10.2 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 diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 70b0e32..79ff34d 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 174806e..10b14a9 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 594a9cc..eacfca9 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 10b14a9..e4e71fa 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_auto_parser.c b/sound/pci/hda/hda_auto_parser.c index a4810c7..0088bb0 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index e4e71fa..29f37c9 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h index eacfca9..696b606 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 v0.10.2 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 diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index 2f94acb..2e6e2b0 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 v0.10.2 From 20fc48632f6de2f8cb35cd7a4de2b4c1649fcac7 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sun, 20 Jan 2013 21:56:21 +0900 Subject: ASoC: wm5100: Implement DRC, EQ and LHPF coefficient configuration Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm5100.c b/sound/soc/codecs/wm5100.c index 54397a5..ac1745d 100644 --- a/sound/soc/codecs/wm5100.c +++ b/sound/soc/codecs/wm5100.c @@ -563,6 +563,19 @@ SOC_DOUBLE_R("IN3 Switch", WM5100_ADC_DIGITAL_VOLUME_3L, SOC_DOUBLE_R("IN4 Switch", WM5100_ADC_DIGITAL_VOLUME_4L, WM5100_ADC_DIGITAL_VOLUME_4R, WM5100_IN4L_MUTE_SHIFT, 1, 1), +SND_SOC_BYTES_MASK("EQ1 Coefficients", WM5100_EQ1_1, 20, WM5100_EQ1_ENA), +SND_SOC_BYTES_MASK("EQ2 Coefficients", WM5100_EQ2_1, 20, WM5100_EQ2_ENA), +SND_SOC_BYTES_MASK("EQ3 Coefficients", WM5100_EQ3_1, 20, WM5100_EQ3_ENA), +SND_SOC_BYTES_MASK("EQ4 Coefficients", WM5100_EQ4_1, 20, WM5100_EQ4_ENA), + +SND_SOC_BYTES_MASK("DRC Coefficients", WM5100_DRC1_CTRL1, 5, + WM5100_DRCL_ENA | WM5100_DRCR_ENA), + +SND_SOC_BYTES("LHPF1 Coefficeints", WM5100_HPLPF1_2, 1), +SND_SOC_BYTES("LHPF2 Coefficeints", WM5100_HPLPF2_2, 1), +SND_SOC_BYTES("LHPF3 Coefficeints", WM5100_HPLPF3_2, 1), +SND_SOC_BYTES("LHPF4 Coefficeints", WM5100_HPLPF4_2, 1), + SOC_SINGLE("HPOUT1 High Performance Switch", WM5100_OUT_VOLUME_1L, WM5100_OUT1_OSR_SHIFT, 1, 0), SOC_SINGLE("HPOUT2 High Performance Switch", WM5100_OUT_VOLUME_2L, -- cgit v0.10.2 From 908a5741abc40f46cbb51704031d92ef43413df0 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sun, 20 Jan 2013 21:55:55 +0900 Subject: ASoC: wm2200: Implement EQ and LHPF coefficient configuration Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm2200.c b/sound/soc/codecs/wm2200.c index d400291..0e11184 100644 --- a/sound/soc/codecs/wm2200.c +++ b/sound/soc/codecs/wm2200.c @@ -1144,6 +1144,12 @@ SOC_DOUBLE_R_TLV("IN3 Digital Volume", WM2200_ADC_DIGITAL_VOLUME_3L, WM2200_ADC_DIGITAL_VOLUME_3R, WM2200_IN3L_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv), +SND_SOC_BYTES_MASK("EQL Coefficients", WM2200_EQL_1, 20, WM2200_EQL_ENA), +SND_SOC_BYTES_MASK("EQR Coefficients", WM2200_EQR_1, 20, WM2200_EQR_ENA), + +SND_SOC_BYTES("LHPF1 Coefficeints", WM2200_HPLPF1_2, 1), +SND_SOC_BYTES("LHPF2 Coefficeints", WM2200_HPLPF2_2, 1), + SOC_SINGLE("OUT1 High Performance Switch", WM2200_DAC_DIGITAL_VOLUME_1L, WM2200_OUT1_OSR_SHIFT, 1, 0), SOC_SINGLE("OUT2 High Performance Switch", WM2200_DAC_DIGITAL_VOLUME_2L, -- cgit v0.10.2 From 25c62f7e7014d64c0308d0dedb99282ed8ade2ce Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sun, 20 Jan 2013 19:02:19 +0900 Subject: ASoC: wm_adsp: Make region identification errors more informative Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index 9e31162..084ea5f 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -768,7 +768,8 @@ static int wm_adsp_load_coeff(struct wm_adsp *dsp) break; default: - adsp_err(dsp, "Unknown region type %x\n", type); + adsp_err(dsp, "%s.%d: Unknown region type %x at %d\n", + file, blocks, type, pos); break; } -- cgit v0.10.2 From f2a93e2a4c048dfb5c7ee3f159a4a1d1cb61b4b5 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sun, 20 Jan 2013 22:17:30 +0900 Subject: ASoC: wm_adsp: Use GFP_DMA for algorithm readback Normally kmalloc() returns things that are DMA safe so not visible on all platforms but we do need to explicitly request DMA safe memory. Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index 084ea5f..edb6713 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -553,7 +553,7 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp) adsp_warn(dsp, "Algorithm list end %x 0x%x != 0xbeadead\n", term, be32_to_cpu(val)); - alg = kzalloc((term - pos) * 2, GFP_KERNEL); + alg = kzalloc((term - pos) * 2, GFP_KERNEL | GFP_DMA); if (!alg) return -ENOMEM; -- cgit v0.10.2 From f2c26d48d9c6e91c6b9c914f215952400e6f889b Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 21 Jan 2013 16:09:36 +0900 Subject: ASoC: arizona: Support clearing clocks Some systems may wish to support switching between telephony and CD audio clock rates but this is restricted by enforcement of constraints on the current DAI clock. Support setting clocks to zero and don't enforce any constraints in that case in order to facilitate this use case. Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c index 316f074..845d256 100644 --- a/sound/soc/codecs/arizona.c +++ b/sound/soc/codecs/arizona.c @@ -474,6 +474,10 @@ int arizona_set_sysclk(struct snd_soc_codec *codec, int clk_id, case 147456000: val |= 6 << ARIZONA_SYSCLK_FREQ_SHIFT; break; + case 0: + dev_dbg(arizona->dev, "%s cleared\n", name); + *clk = freq; + return 0; default: return -EINVAL; } @@ -692,6 +696,9 @@ static int arizona_startup(struct snd_pcm_substream *substream, return 0; } + if (base_rate == 0) + return 0; + if (base_rate % 8000) constraint = &arizona_44k1_constraint; else -- cgit v0.10.2 From 20da6d5ac05905833db4b339b57b9dcbd21b6152 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sat, 12 Jan 2013 19:58:17 +0000 Subject: ASoC: wm_adsp: Provide explicit trace of coefficient writes Helpful for debugging. Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index 76ca176..bc5e383 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -793,6 +793,9 @@ static int wm_adsp_load_coeff(struct wm_adsp *dsp) return -ENOMEM; } + adsp_dbg(dsp, "%s.%d: Writing %d bytes at %x\n", + file, blocks, le32_to_cpu(blk->len), + reg); ret = regmap_raw_write(regmap, reg, blk->data, le32_to_cpu(blk->len)); if (ret != 0) { -- cgit v0.10.2 From c94aa30edac4d328674e9c127918317009d30c1a Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 17 Jan 2013 16:35:14 +0900 Subject: ASoC: arizona: Allow number of channels clocked to be restricted Place a cap on the number of channels clocks are generated for. This is intended for use with systems which have the WM5102 master an I2S bus with multiple data lines. Signed-off-by: Mark Brown diff --git a/include/linux/mfd/arizona/pdata.h b/include/linux/mfd/arizona/pdata.h index 8b1d1da..ec3e2a2 100644 --- a/include/linux/mfd/arizona/pdata.h +++ b/include/linux/mfd/arizona/pdata.h @@ -62,6 +62,8 @@ #define ARIZONA_MAX_OUTPUT 6 +#define ARIZONA_MAX_AIF 3 + #define ARIZONA_HAP_ACT_ERM 0 #define ARIZONA_HAP_ACT_LRA 2 @@ -96,6 +98,13 @@ struct arizona_pdata { /** Pin state for GPIO pins */ int gpio_defaults[ARIZONA_MAX_GPIO]; + /** + * Maximum number of channels clocks will be generated for, + * useful for systems where and I2S bus with multiple data + * lines is mastered. + */ + int max_channels_clocked[ARIZONA_MAX_AIF]; + /** GPIO for mic detection polarity */ int micd_pol_gpio; diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c index 845d256..d855a6c 100644 --- a/sound/soc/codecs/arizona.c +++ b/sound/soc/codecs/arizona.c @@ -762,18 +762,28 @@ static int arizona_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct snd_soc_codec *codec = dai->codec; + struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec); + struct arizona *arizona = priv->arizona; int base = dai->driver->base; const int *rates; int i, ret; - int bclk, lrclk, wl, frame; + int chan_limit = arizona->pdata.max_channels_clocked[dai->id - 1]; + int bclk, lrclk, wl, frame, bclk_target; if (params_rate(params) % 8000) rates = &arizona_44k1_bclk_rates[0]; else rates = &arizona_48k_bclk_rates[0]; + bclk_target = snd_soc_params_to_bclk(params); + if (chan_limit && chan_limit < params_channels(params)) { + arizona_aif_dbg(dai, "Limiting to %d channels\n", chan_limit); + bclk_target /= params_channels(params); + bclk_target *= chan_limit; + } + for (i = 0; i < ARRAY_SIZE(arizona_44k1_bclk_rates); i++) { - if (rates[i] >= snd_soc_params_to_bclk(params) && + if (rates[i] >= bclk_target && rates[i] % params_rate(params) == 0) { bclk = i; break; -- cgit v0.10.2 From b5a8fe439ab343631d905a51438db3de54ba94df Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sun, 20 Jan 2013 21:42:22 +0900 Subject: ASoC: core: Ensure SND_SOC_BYTES writes are from DMA safe memory With some buses the transfers may DMAed, especially for larger blocks. Signed-off-by: Mark Brown diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index e0e8ce0..02d826e 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -3097,9 +3097,12 @@ int snd_soc_bytes_put(struct snd_kcontrol *kcontrol, if (!codec->using_regmap) return -EINVAL; - data = ucontrol->value.bytes.data; len = params->num_regs * codec->val_bytes; + data = kmemdup(ucontrol->value.bytes.data, len, GFP_KERNEL | GFP_DMA); + if (!data) + return -ENOMEM; + /* * If we've got a mask then we need to preserve the register * bits. We shouldn't modify the incoming data so take a @@ -3112,10 +3115,6 @@ int snd_soc_bytes_put(struct snd_kcontrol *kcontrol, val &= params->mask; - data = kmemdup(data, len, GFP_KERNEL); - if (!data) - return -ENOMEM; - switch (codec->val_bytes) { case 1: ((u8 *)data)[0] &= ~params->mask; @@ -3137,8 +3136,7 @@ int snd_soc_bytes_put(struct snd_kcontrol *kcontrol, ret = regmap_raw_write(codec->control_data, params->base, data, len); - if (params->mask) - kfree(data); + kfree(data); return ret; } -- cgit v0.10.2 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 diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 29f37c9..37d7ed7 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 37d7ed7..7b739b5 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 7b739b5..4e9761a 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 4e9761a..e26e8d3 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index e26e8d3..6d1e843 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 696b606..9c63555 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 v0.10.2 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 diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index eade21c..9d9583c 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 v0.10.2 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 diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index 6feaec4..162bc2f 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 db7635c..30b3a4b 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 b9dfbd8..72ebb8a 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 087cabb..9c6ce73 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 79ff34d..6eb9551 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 fe3e082..fd29f49 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 v0.10.2 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 diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index fd29f49..c53b6f9 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 v0.10.2 From f63d944b71e8c4df9daaf44c7a9756549735140c Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Mon, 21 Jan 2013 09:02:31 +0000 Subject: ASoC: wm_adsp: Release firmware on error This patch correctly releases the firmware if the magic string in the firmware header does not match. Signed-off-by: Charles Keepax Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index ffc89fa..046b70b 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -384,7 +384,7 @@ static int wm_adsp_load_coeff(struct wm_adsp *dsp) hdr = (void*)&firmware->data[0]; if (memcmp(hdr->magic, "WMDR", 4) != 0) { adsp_err(dsp, "%s: invalid magic\n", file); - return -EINVAL; + goto out_fw; } adsp_dbg(dsp, "%s: v%d.%d.%d\n", file, -- cgit v0.10.2 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 diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index 162bc2f..a186b3d 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 6d1e843..63d12ef 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 9c63555..980707f 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 63d12ef..258fb5e 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 v0.10.2 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 diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index c53b6f9..0aa0ceb 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 @@ -885,6 +891,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; +} + +/* */ static const struct hda_verb stac9200_core_init[] = { @@ -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 v0.10.2 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 diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 258fb5e..374fd6c 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 374fd6c..b301952 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index e6cdad7..77ddd34 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 61085b3..cc73287 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 b301952..758dcc1 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 9e6353a..f92979c 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 6eb9551..feb8cef 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 v0.10.2 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 diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index a186b3d..5d8328a 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 v0.10.2 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 diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index 5d8328a..98cbc98 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 v0.10.2 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 diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index 98cbc98..9692265 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; @@ -1196,6 +1197,34 @@ static int alloc_ad_spec(struct hda_codec *codec) } /* + * 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 v0.10.2 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 diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index 9692265..9d82aab 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 v0.10.2 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 diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 604fe5e..515f83b 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 758dcc1..06e203d 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 980707f..d226856 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 9d82aab..df8014b 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 06e203d..921582d 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 v0.10.2 From 338c5188f6fbccf3ef34a6ae46ada8f64565e1d0 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 24 Jan 2013 00:35:48 +0800 Subject: ASoC: wm_adsp: Correct handling of some coefficeint blocks Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index bc5e383..55a0089 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -768,9 +768,10 @@ static int wm_adsp_load_coeff(struct wm_adsp *dsp) &dsp->alg_regions, list) { if (le32_to_cpu(blk->id) == alg_region->alg && type == alg_region->type) { - reg = alg_region->base + offset; + reg = alg_region->base; reg = wm_adsp_region_to_reg(mem, reg); + reg += offset; } } -- cgit v0.10.2 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 diff --git a/sound/pci/hda/hda_auto_parser.c b/sound/pci/hda/hda_auto_parser.c index 0088bb0..96a05c4 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 f92979c..2ff62dc 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 v0.10.2 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 diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 446ed11..dc75607 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 v0.10.2 From 0098389564fbdfbeb08181f772205d87a18f5b37 Mon Sep 17 00:00:00 2001 From: Chris Rattray Date: Thu, 17 Jan 2013 13:11:47 +0000 Subject: ASoC: wm2200: Set system clock control register is adsp structs Allows ADSP control code to set the dsp clock rate to match the sys clock rate. Signed-off-by: Chris Rattray Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm2200.c b/sound/soc/codecs/wm2200.c index 0e11184..fc05553 100644 --- a/sound/soc/codecs/wm2200.c +++ b/sound/soc/codecs/wm2200.c @@ -2224,6 +2224,9 @@ static int wm2200_i2c_probe(struct i2c_client *i2c, wm2200->dsp[i].num = i + 1; wm2200->dsp[i].dev = &i2c->dev; wm2200->dsp[i].regmap = wm2200->regmap; + wm2200->dsp[i].sysclk_reg = WM2200_CLOCKING_3; + wm2200->dsp[i].sysclk_mask = WM2200_SYSCLK_FREQ_MASK; + wm2200->dsp[i].sysclk_shift = WM2200_SYSCLK_FREQ_SHIFT; } wm2200->dsp[0].base = WM2200_DSP1_CONTROL_1; -- cgit v0.10.2 From 7c62eebbf7d1cdaec68ab9d2d4017007f9312391 Mon Sep 17 00:00:00 2001 From: Padmavathi Venna Date: Fri, 18 Jan 2013 17:17:00 +0530 Subject: ASoC: samsung: Rename samsung i2s secondary device name All Samsung SoCs has max 3 i2s controllers. So the i2s secondary fifo interface device id was named as samsung-i2s.4. Renaming this to "samsung-i2s-sec" to support device tree in i2s driver. Signed-off-by: Padmavathi Venna Signed-off-by: Mark Brown diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c index d2d124f..ed5eeae 100644 --- a/sound/soc/samsung/i2s.c +++ b/sound/soc/samsung/i2s.c @@ -29,6 +29,11 @@ #define msecs_to_loops(t) (loops_per_jiffy / 1000 * HZ * t) +enum samsung_dai_type { + TYPE_PRI, + TYPE_SEC, +}; + struct i2s_dai { /* Platform device for this DAI */ struct platform_device *pdev; @@ -981,8 +986,7 @@ static struct i2s_dai *i2s_alloc_dai(struct platform_device *pdev, bool sec) i2s->i2s_dai_drv.capture.formats = SAMSUNG_I2S_FMTS; } else { /* Create a new platform_device for Secondary */ i2s->pdev = platform_device_register_resndata(NULL, - pdev->name, pdev->id + SAMSUNG_I2S_SECOFF, - NULL, 0, NULL, 0); + "samsung-i2s-sec", -1, NULL, 0, NULL, 0); if (IS_ERR(i2s->pdev)) return NULL; } @@ -993,6 +997,11 @@ static struct i2s_dai *i2s_alloc_dai(struct platform_device *pdev, bool sec) return i2s; } +static inline int samsung_i2s_get_driver_data(struct platform_device *pdev) +{ + return platform_get_device_id(pdev)->driver_data; +} + static int samsung_i2s_probe(struct platform_device *pdev) { u32 dma_pl_chan, dma_cp_chan, dma_pl_sec_chan; @@ -1001,10 +1010,13 @@ static int samsung_i2s_probe(struct platform_device *pdev) struct samsung_i2s *i2s_cfg; struct resource *res; u32 regs_base, quirks; + enum samsung_dai_type samsung_dai_type; int ret = 0; /* Call during Seconday interface registration */ - if (pdev->id >= SAMSUNG_I2S_SECOFF) { + samsung_dai_type = samsung_i2s_get_driver_data(pdev); + + if (samsung_dai_type == TYPE_SEC) { sec_dai = dev_get_drvdata(&pdev->dev); snd_soc_register_dai(&sec_dai->pdev->dev, &sec_dai->i2s_dai_drv); @@ -1143,9 +1155,22 @@ static int samsung_i2s_remove(struct platform_device *pdev) return 0; } +static struct platform_device_id samsung_i2s_driver_ids[] = { + { + .name = "samsung-i2s", + .driver_data = TYPE_PRI, + }, { + .name = "samsung-i2s-sec", + .driver_data = TYPE_SEC, + }, + {}, +}; +MODULE_DEVICE_TABLE(platform, samsung-i2s-driver-ids); + static struct platform_driver samsung_i2s_driver = { .probe = samsung_i2s_probe, .remove = samsung_i2s_remove, + .id_table = samsung_i2s_driver_ids, .driver = { .name = "samsung-i2s", .owner = THIS_MODULE, diff --git a/sound/soc/samsung/i2s.h b/sound/soc/samsung/i2s.h index d420a7c..7966afc 100644 --- a/sound/soc/samsung/i2s.h +++ b/sound/soc/samsung/i2s.h @@ -13,13 +13,6 @@ #ifndef __SND_SOC_SAMSUNG_I2S_H #define __SND_SOC_SAMSUNG_I2S_H -/* - * Maximum number of I2S blocks that any SoC can have. - * The secondary interface of a CPU dai(if there exists any), - * is indexed at [cpu-dai's ID + SAMSUNG_I2S_SECOFF] - */ -#define SAMSUNG_I2S_SECOFF 4 - #define SAMSUNG_I2S_DIV_BCLK 1 #define SAMSUNG_I2S_RCLKSRC_0 0 diff --git a/sound/soc/samsung/smdk_wm8580.c b/sound/soc/samsung/smdk_wm8580.c index 7e2b710..7a16b32 100644 --- a/sound/soc/samsung/smdk_wm8580.c +++ b/sound/soc/samsung/smdk_wm8580.c @@ -193,9 +193,9 @@ static struct snd_soc_dai_link smdk_dai[] = { [SEC_PLAYBACK] = { /* Sec_Fifo Playback i/f */ .name = "Sec_FIFO TX", .stream_name = "Playback", - .cpu_dai_name = "samsung-i2s.x", + .cpu_dai_name = "samsung-i2s-sec", .codec_dai_name = "wm8580-hifi-playback", - .platform_name = "samsung-i2s.x", + .platform_name = "samsung-i2s-sec", .codec_name = "wm8580.0-001b", .ops = &smdk_ops, }, @@ -223,9 +223,6 @@ static int __init smdk_audio_init(void) if (machine_is_smdkc100() || machine_is_smdkv210() || machine_is_smdkc110()) { smdk.num_links = 3; - /* Secondary is at offset SAMSUNG_I2S_SECOFF from Primary */ - str = (char *)smdk_dai[SEC_PLAYBACK].cpu_dai_name; - str[strlen(str) - 1] = '0' + SAMSUNG_I2S_SECOFF; } else if (machine_is_smdk6410()) { str = (char *)smdk_dai[PRI_PLAYBACK].cpu_dai_name; str[strlen(str) - 1] = '2'; diff --git a/sound/soc/samsung/smdk_wm8994.c b/sound/soc/samsung/smdk_wm8994.c index b0d0ab8..cc2f407 100644 --- a/sound/soc/samsung/smdk_wm8994.c +++ b/sound/soc/samsung/smdk_wm8994.c @@ -134,9 +134,9 @@ static struct snd_soc_dai_link smdk_dai[] = { }, { /* Sec_Fifo Playback i/f */ .name = "Sec_FIFO TX", .stream_name = "Sec_Dai", - .cpu_dai_name = "samsung-i2s.4", + .cpu_dai_name = "samsung-i2s-sec", .codec_dai_name = "wm8994-aif1", - .platform_name = "samsung-i2s.4", + .platform_name = "samsung-i2s-sec", .codec_name = "wm8994-codec", .ops = &smdk_ops, }, -- cgit v0.10.2 From 33e7546e199f123808699c65274283606114e225 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 22 Jan 2013 15:51:08 +0900 Subject: ASoC: wm2000: Expose some additional registers Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm2000.c b/sound/soc/codecs/wm2000.c index 85550dc..627c454 100644 --- a/sound/soc/codecs/wm2000.c +++ b/sound/soc/codecs/wm2000.c @@ -717,6 +717,9 @@ static bool wm2000_readable_reg(struct device *dev, unsigned int reg) { switch (reg) { case WM2000_REG_SYS_START: + case WM2000_REG_ANC_GAIN_CTRL: + case WM2000_REG_MSE_TH1: + case WM2000_REG_MSE_TH2: case WM2000_REG_SPEECH_CLARITY: case WM2000_REG_SYS_WATCHDOG: case WM2000_REG_ANA_VMID_PD_TIME: diff --git a/sound/soc/codecs/wm2000.h b/sound/soc/codecs/wm2000.h index abcd82a..fb812cd 100644 --- a/sound/soc/codecs/wm2000.h +++ b/sound/soc/codecs/wm2000.h @@ -10,6 +10,9 @@ #define _WM2000_H #define WM2000_REG_SYS_START 0x8000 +#define WM2000_REG_ANC_GAIN_CTRL 0x8fa2 +#define WM2000_REG_MSE_TH2 0x8fdf +#define WM2000_REG_MSE_TH1 0x8fe0 #define WM2000_REG_SPEECH_CLARITY 0x8fef #define WM2000_REG_SYS_WATCHDOG 0x8ff6 #define WM2000_REG_ANA_VMID_PD_TIME 0x8ff7 -- cgit v0.10.2 From 8c3d2aa4cfeaba66be68ef8c351b2e099e50c25b Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Thu, 24 Jan 2013 09:44:28 +0000 Subject: ASoC: soc-compress: Add missing brackets around else Signed-off-by: Charles Keepax Acked-by: Vinod Koul Tested-by: Jeeja KP Signed-off-by: Mark Brown diff --git a/sound/soc/soc-compress.c b/sound/soc/soc-compress.c index 5fbfb06..80040f0 100644 --- a/sound/soc/soc-compress.c +++ b/sound/soc/soc-compress.c @@ -112,10 +112,11 @@ static int soc_compr_free(struct snd_compr_stream *cstream) snd_soc_dapm_stream_event(rtd, SNDRV_PCM_STREAM_PLAYBACK, SND_SOC_DAPM_STREAM_STOP); - } else + } else { rtd->pop_wait = 1; schedule_delayed_work(&rtd->delayed_work, msecs_to_jiffies(rtd->pmdown_time)); + } } else { /* capture streams can be powered down now */ snd_soc_dapm_stream_event(rtd, -- cgit v0.10.2 From 15e2e6194a3ae13ffeea9b7c368b54b143f31594 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Thu, 24 Jan 2013 09:44:29 +0000 Subject: ASoC: soc-compress: Serialise compressed ops Use the pcm_mutex to serialise the compressed ops. Signed-off-by: Charles Keepax Acked-by: Vinod Koul Tested-by: Jeeja KP Signed-off-by: Mark Brown diff --git a/sound/soc/soc-compress.c b/sound/soc/soc-compress.c index 80040f0..c48db63 100644 --- a/sound/soc/soc-compress.c +++ b/sound/soc/soc-compress.c @@ -33,6 +33,8 @@ static int soc_compr_open(struct snd_compr_stream *cstream) struct snd_soc_dai *codec_dai = rtd->codec_dai; int ret = 0; + mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); + if (platform->driver->compr_ops && platform->driver->compr_ops->open) { ret = platform->driver->compr_ops->open(cstream); if (ret < 0) { @@ -61,12 +63,15 @@ static int soc_compr_open(struct snd_compr_stream *cstream) codec_dai->active++; rtd->codec->active++; + mutex_unlock(&rtd->pcm_mutex); + return 0; machine_err: if (platform->driver->compr_ops && platform->driver->compr_ops->free) platform->driver->compr_ops->free(cstream); out: + mutex_unlock(&rtd->pcm_mutex); return ret; } @@ -78,6 +83,8 @@ static int soc_compr_free(struct snd_compr_stream *cstream) struct snd_soc_dai *codec_dai = rtd->codec_dai; struct snd_soc_codec *codec = rtd->codec; + mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); + if (cstream->direction == SND_COMPRESS_PLAYBACK) { cpu_dai->playback_active--; codec_dai->playback_active--; @@ -124,6 +131,7 @@ static int soc_compr_free(struct snd_compr_stream *cstream) SND_SOC_DAPM_STREAM_STOP); } + mutex_unlock(&rtd->pcm_mutex); return 0; } @@ -135,10 +143,12 @@ static int soc_compr_trigger(struct snd_compr_stream *cstream, int cmd) struct snd_soc_dai *codec_dai = rtd->codec_dai; int ret = 0; + mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); + if (platform->driver->compr_ops && platform->driver->compr_ops->trigger) { ret = platform->driver->compr_ops->trigger(cstream, cmd); if (ret < 0) - return ret; + goto out; } if (cmd == SNDRV_PCM_TRIGGER_START) @@ -146,6 +156,8 @@ static int soc_compr_trigger(struct snd_compr_stream *cstream, int cmd) else if (cmd == SNDRV_PCM_TRIGGER_STOP) snd_soc_dai_digital_mute(codec_dai, 1); +out: + mutex_unlock(&rtd->pcm_mutex); return ret; } @@ -156,6 +168,8 @@ static int soc_compr_set_params(struct snd_compr_stream *cstream, struct snd_soc_platform *platform = rtd->platform; int ret = 0; + mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); + /* first we call set_params for the platform driver * this should configure the soc side * if the machine has compressed ops then we call that as well @@ -165,18 +179,20 @@ static int soc_compr_set_params(struct snd_compr_stream *cstream, if (platform->driver->compr_ops && platform->driver->compr_ops->set_params) { ret = platform->driver->compr_ops->set_params(cstream, params); if (ret < 0) - return ret; + goto out; } if (rtd->dai_link->compr_ops && rtd->dai_link->compr_ops->set_params) { ret = rtd->dai_link->compr_ops->set_params(cstream); if (ret < 0) - return ret; + goto out; } snd_soc_dapm_stream_event(rtd, SNDRV_PCM_STREAM_PLAYBACK, SND_SOC_DAPM_STREAM_START); +out: + mutex_unlock(&rtd->pcm_mutex); return ret; } @@ -187,9 +203,12 @@ static int soc_compr_get_params(struct snd_compr_stream *cstream, struct snd_soc_platform *platform = rtd->platform; int ret = 0; + mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); + if (platform->driver->compr_ops && platform->driver->compr_ops->get_params) ret = platform->driver->compr_ops->get_params(cstream, params); + mutex_unlock(&rtd->pcm_mutex); return ret; } @@ -200,9 +219,12 @@ static int soc_compr_get_caps(struct snd_compr_stream *cstream, struct snd_soc_platform *platform = rtd->platform; int ret = 0; + mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); + if (platform->driver->compr_ops && platform->driver->compr_ops->get_caps) ret = platform->driver->compr_ops->get_caps(cstream, caps); + mutex_unlock(&rtd->pcm_mutex); return ret; } @@ -213,9 +235,12 @@ static int soc_compr_get_codec_caps(struct snd_compr_stream *cstream, struct snd_soc_platform *platform = rtd->platform; int ret = 0; + mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); + if (platform->driver->compr_ops && platform->driver->compr_ops->get_codec_caps) ret = platform->driver->compr_ops->get_codec_caps(cstream, codec); + mutex_unlock(&rtd->pcm_mutex); return ret; } @@ -225,9 +250,12 @@ static int soc_compr_ack(struct snd_compr_stream *cstream, size_t bytes) struct snd_soc_platform *platform = rtd->platform; int ret = 0; + mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); + if (platform->driver->compr_ops && platform->driver->compr_ops->ack) ret = platform->driver->compr_ops->ack(cstream, bytes); + mutex_unlock(&rtd->pcm_mutex); return ret; } @@ -237,9 +265,12 @@ static int soc_compr_pointer(struct snd_compr_stream *cstream, struct snd_soc_pcm_runtime *rtd = cstream->private_data; struct snd_soc_platform *platform = rtd->platform; + mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); + if (platform->driver->compr_ops && platform->driver->compr_ops->pointer) platform->driver->compr_ops->pointer(cstream, tstamp); + mutex_unlock(&rtd->pcm_mutex); return 0; } -- cgit v0.10.2 From 202c8f7082e87e09f861d06b1a03501047c017b5 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Thu, 24 Jan 2013 09:44:30 +0000 Subject: ASoC: soc-compress: Initialise delayed work to power down audio Delayed work was scheduled but not initialised, this patch adds the actual work and initialises it. Signed-off-by: Charles Keepax Acked-by: Vinod Koul Tested-by: Jeeja KP Signed-off-by: Mark Brown diff --git a/sound/soc/soc-compress.c b/sound/soc/soc-compress.c index c48db63..3ea7956 100644 --- a/sound/soc/soc-compress.c +++ b/sound/soc/soc-compress.c @@ -75,6 +75,34 @@ out: return ret; } +/* + * Power down the audio subsystem pmdown_time msecs after close is called. + * This is to ensure there are no pops or clicks in between any music tracks + * due to DAPM power cycling. + */ +static void close_delayed_work(struct work_struct *work) +{ + struct snd_soc_pcm_runtime *rtd = + container_of(work, struct snd_soc_pcm_runtime, delayed_work.work); + struct snd_soc_dai *codec_dai = rtd->codec_dai; + + mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); + + dev_dbg(rtd->dev, "ASoC: pop wq checking: %s status: %s waiting: %s\n", + codec_dai->driver->playback.stream_name, + codec_dai->playback_active ? "active" : "inactive", + rtd->pop_wait ? "yes" : "no"); + + /* are we waiting on this codec DAI stream */ + if (rtd->pop_wait == 1) { + rtd->pop_wait = 0; + snd_soc_dapm_stream_event(rtd, SNDRV_PCM_STREAM_PLAYBACK, + SND_SOC_DAPM_STREAM_STOP); + } + + mutex_unlock(&rtd->pcm_mutex); +} + static int soc_compr_free(struct snd_compr_stream *cstream) { struct snd_soc_pcm_runtime *rtd = cstream->private_data; @@ -317,6 +345,9 @@ int soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num) return ret; } + /* DAPM dai link stream work */ + INIT_DELAYED_WORK(&rtd->delayed_work, close_delayed_work); + rtd->compr = compr; compr->private_data = rtd; -- cgit v0.10.2 From b10fedf89269a444ddfc92371acc9721749e8b41 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Thu, 24 Jan 2013 14:51:15 +0530 Subject: ASoC: tegra_wm9712: Remove __devinitconst attribute MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This has been removed from the kernel recently and gives following build errors: sound/soc/tegra/tegra_wm9712.c:155:58: error: expected ‘=’, ‘,’, ‘;’, ‘asm’ or ‘__attribute__’ before ‘__devinitconst’ sound/soc/tegra/tegra_wm9712.c:165:21: error: ‘tegra_wm9712_of_match’ undeclared here (not in a function) Cc: Lucas Stach Signed-off-by: Sachin Kamat Reviewed-by: Stephen Warren Signed-off-by: Mark Brown diff --git a/sound/soc/tegra/tegra_wm9712.c b/sound/soc/tegra/tegra_wm9712.c index cdbd2f0..68d4240 100644 --- a/sound/soc/tegra/tegra_wm9712.c +++ b/sound/soc/tegra/tegra_wm9712.c @@ -152,7 +152,7 @@ static int tegra_wm9712_driver_remove(struct platform_device *pdev) return 0; } -static const struct of_device_id tegra_wm9712_of_match[] __devinitconst = { +static const struct of_device_id tegra_wm9712_of_match[] = { { .compatible = "nvidia,tegra-audio-wm9712", }, {}, }; -- cgit v0.10.2 From d58579e3c3f8b6ce623de0ca26580375852b574f Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Thu, 24 Jan 2013 14:51:16 +0530 Subject: ASoC: tegra20_ac97: Remove __devinitconst attribute __devinitconst has been removed from the kernel and gives the following build errors: sound/soc/tegra/tegra20_ac97.c:460:58: error: Expected ; at end of declaration sound/soc/tegra/tegra20_ac97.c:460:58: error: got __devinitconst Cc: Lucas Stach Signed-off-by: Sachin Kamat Reviewed-by: Stephen Warren Signed-off-by: Mark Brown diff --git a/sound/soc/tegra/tegra20_ac97.c b/sound/soc/tegra/tegra20_ac97.c index 1bae73b..336dcdd 100644 --- a/sound/soc/tegra/tegra20_ac97.c +++ b/sound/soc/tegra/tegra20_ac97.c @@ -457,7 +457,7 @@ static int tegra20_ac97_platform_remove(struct platform_device *pdev) return 0; } -static const struct of_device_id tegra20_ac97_of_match[] __devinitconst = { +static const struct of_device_id tegra20_ac97_of_match[] = { { .compatible = "nvidia,tegra20-ac97", }, {}, }; -- cgit v0.10.2 From ecb2c174346f9e1b2cb671b261a16f1b9aaa66c4 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Thu, 24 Jan 2013 14:51:17 +0530 Subject: ASoC: tegra: Use NULL instead of 0 for pointers Fixes the following sparse warnings: sound/soc/tegra/tegra30_ahub.c:583:16: warning: Using plain integer as NULL pointer sound/soc/tegra/tegra30_ahub.c:600:16: warning: Using plain integer as NULL pointer Signed-off-by: Sachin Kamat Reviewed-by: Stephen Warren Signed-off-by: Mark Brown diff --git a/sound/soc/tegra/tegra30_ahub.c b/sound/soc/tegra/tegra30_ahub.c index f354dc3..dd146f1 100644 --- a/sound/soc/tegra/tegra30_ahub.c +++ b/sound/soc/tegra/tegra30_ahub.c @@ -580,7 +580,7 @@ err_clk_put_apbif: clk_put(ahub->clk_apbif); err_clk_put_d_audio: clk_put(ahub->clk_d_audio); - ahub = 0; + ahub = NULL; err: return ret; } @@ -597,7 +597,7 @@ static int tegra30_ahub_remove(struct platform_device *pdev) clk_put(ahub->clk_apbif); clk_put(ahub->clk_d_audio); - ahub = 0; + ahub = NULL; return 0; } -- cgit v0.10.2 From ec05cc554eb7cfb7ca0d6d93a3afabff6bc80033 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Thu, 24 Jan 2013 14:51:18 +0530 Subject: ASoC: tegra: Staticize some functions in tegra30_i2s.c 'tegra30_i2s_startup' and 'tegra30_i2s_shutdown' are used only in this file and hence made static. Fixes the following sparse warnings: sound/soc/tegra/tegra30_i2s.c:74:5: warning: symbol 'tegra30_i2s_startup' was not declared. Should it be static? sound/soc/tegra/tegra30_i2s.c:101:6: warning: symbol 'tegra30_i2s_shutdown' was not declared. Should it be static? Signed-off-by: Sachin Kamat Reviewed-by: Stephen Warren Signed-off-by: Mark Brown diff --git a/sound/soc/tegra/tegra30_i2s.c b/sound/soc/tegra/tegra30_i2s.c index 27e91dd..f4e1ce8 100644 --- a/sound/soc/tegra/tegra30_i2s.c +++ b/sound/soc/tegra/tegra30_i2s.c @@ -71,7 +71,7 @@ static int tegra30_i2s_runtime_resume(struct device *dev) return 0; } -int tegra30_i2s_startup(struct snd_pcm_substream *substream, +static int tegra30_i2s_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct tegra30_i2s *i2s = snd_soc_dai_get_drvdata(dai); @@ -98,7 +98,7 @@ int tegra30_i2s_startup(struct snd_pcm_substream *substream, return ret; } -void tegra30_i2s_shutdown(struct snd_pcm_substream *substream, +static void tegra30_i2s_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct tegra30_i2s *i2s = snd_soc_dai_get_drvdata(dai); -- cgit v0.10.2 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 diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 921582d..31ffd66 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 v0.10.2 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 diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 9d9583c..5bd4b0c 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index e4e0501..19ff923 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 cc73287..fbedcf3 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 d98d470..7d941ef 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 0aa0ceb..5895d8f 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 19ff923..2311114 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 2311114..f82a64d 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 2ff62dc..05f1d59 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 5bd4b0c..9641c0e 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 31ffd66..19d014a 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 d226856..065fcc7 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 v0.10.2 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 diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 5895d8f..1c49861 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 v0.10.2 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 diff --git a/include/sound/core.h b/include/sound/core.h index 93896ad..7cede2d 100644 --- a/include/sound/core.h +++ b/include/sound/core.h @@ -394,8 +394,11 @@ void __snd_printk(unsigned int level, const char *file, int line, #else /* !CONFIG_SND_DEBUG */ -#define snd_printd(fmt, args...) do { } while (0) -#define _snd_printd(level, fmt, args...) do { } while (0) +__printf(1, 2) +static inline void snd_printd(const char *format, ...) {} +__printf(2, 3) +static inline void _snd_printd(int level, const char *format, ...) {} + #define snd_BUG() do { } while (0) static inline int __snd_bug_on(int cond) { @@ -416,7 +419,8 @@ static inline int __snd_bug_on(int cond) #define snd_printdd(format, args...) \ __snd_printk(2, __FILE__, __LINE__, format, ##args) #else -#define snd_printdd(format, args...) do { } while (0) +__printf(1, 2) +static inline void snd_printdd(const char *format, ...) {} #endif @@ -454,6 +458,7 @@ struct snd_pci_quirk { #define SND_PCI_QUIRK_MASK(vend, mask, dev, xname, val) \ {_SND_PCI_QUIRK_ID_MASK(vend, mask, dev), \ .value = (val), .name = (xname)} +#define snd_pci_quirk_name(q) ((q)->name) #else #define SND_PCI_QUIRK(vend,dev,xname,val) \ {_SND_PCI_QUIRK_ID(vend, dev), .value = (val)} @@ -461,6 +466,7 @@ struct snd_pci_quirk { {_SND_PCI_QUIRK_ID_MASK(vend, mask, dev), .value = (val)} #define SND_PCI_QUIRK_VENDOR(vend, xname, val) \ {_SND_PCI_QUIRK_ID_MASK(vend, 0, 0), .value = (val)} +#define snd_pci_quirk_name(q) "" #endif const struct snd_pci_quirk * diff --git a/sound/drivers/vx/vx_core.c b/sound/drivers/vx/vx_core.c index de5055a..c39961c 100644 --- a/sound/drivers/vx/vx_core.c +++ b/sound/drivers/vx/vx_core.c @@ -52,7 +52,6 @@ MODULE_LICENSE("GPL"); int snd_vx_check_reg_bit(struct vx_core *chip, int reg, int mask, int bit, int time) { unsigned long end_time = jiffies + (time * HZ + 999) / 1000; -#ifdef CONFIG_SND_DEBUG static char *reg_names[VX_REG_MAX] = { "ICR", "CVR", "ISR", "IVR", "RXH", "RXM", "RXL", "DMA", "CDSP", "RFREQ", "RUER/V2", "DATA", "MEMIRQ", @@ -60,7 +59,7 @@ int snd_vx_check_reg_bit(struct vx_core *chip, int reg, int mask, int bit, int t "MIC3", "INTCSR", "CNTRL", "GPIOC", "LOFREQ", "HIFREQ", "CSUER", "RUER" }; -#endif + do { if ((snd_vx_inb(chip, reg) & mask) == bit) return 0; diff --git a/sound/pci/atiixp.c b/sound/pci/atiixp.c index a677431..6e78c67 100644 --- a/sound/pci/atiixp.c +++ b/sound/pci/atiixp.c @@ -567,8 +567,9 @@ static int ac97_probing_bugs(struct pci_dev *pci) q = snd_pci_quirk_lookup(pci, atiixp_quirks); if (q) { - snd_printdd(KERN_INFO "Atiixp quirk for %s. " - "Forcing codec %d\n", q->name, q->value); + snd_printdd(KERN_INFO + "Atiixp quirk for %s. Forcing codec %d\n", + snd_pci_quirk_name(q), q->value); return q->value; } /* this hardware doesn't need workarounds. Probe for codec */ diff --git a/sound/pci/hda/hda_auto_parser.c b/sound/pci/hda/hda_auto_parser.c index 96a05c4..a3ea76a 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 19d014a..c4ba306 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", diff --git a/sound/pci/intel8x0.c b/sound/pci/intel8x0.c index 3b9be75..b8fe405 100644 --- a/sound/pci/intel8x0.c +++ b/sound/pci/intel8x0.c @@ -3266,11 +3266,13 @@ static int check_default_spdif_aclink(struct pci_dev *pci) w = snd_pci_quirk_lookup(pci, spdif_aclink_defaults); if (w) { if (w->value) - snd_printdd(KERN_INFO "intel8x0: Using SPDIF over " - "AC-Link for %s\n", w->name); + snd_printdd(KERN_INFO + "intel8x0: Using SPDIF over AC-Link for %s\n", + snd_pci_quirk_name(w)); else - snd_printdd(KERN_INFO "intel8x0: Using integrated " - "SPDIF DMA for %s\n", w->name); + snd_printdd(KERN_INFO + "intel8x0: Using integrated SPDIF DMA for %s\n", + snd_pci_quirk_name(w)); return w->value; } return 0; diff --git a/sound/pci/maestro3.c b/sound/pci/maestro3.c index 9387533..c76ac14 100644 --- a/sound/pci/maestro3.c +++ b/sound/pci/maestro3.c @@ -2586,8 +2586,9 @@ snd_m3_create(struct snd_card *card, struct pci_dev *pci, else { quirk = snd_pci_quirk_lookup(pci, m3_amp_quirk_list); if (quirk) { - snd_printdd(KERN_INFO "maestro3: set amp-gpio " - "for '%s'\n", quirk->name); + snd_printdd(KERN_INFO + "maestro3: set amp-gpio for '%s'\n", + snd_pci_quirk_name(quirk)); chip->amp_gpio = quirk->value; } else if (chip->allegro_flag) chip->amp_gpio = GPO_EXT_AMP_ALLEGRO; @@ -2597,8 +2598,9 @@ snd_m3_create(struct snd_card *card, struct pci_dev *pci, quirk = snd_pci_quirk_lookup(pci, m3_irda_quirk_list); if (quirk) { - snd_printdd(KERN_INFO "maestro3: enabled irda workaround " - "for '%s'\n", quirk->name); + snd_printdd(KERN_INFO + "maestro3: enabled irda workaround for '%s'\n", + snd_pci_quirk_name(quirk)); chip->irda_workaround = 1; } quirk = snd_pci_quirk_lookup(pci, m3_hv_quirk_list); diff --git a/sound/pci/nm256/nm256.c b/sound/pci/nm256/nm256.c index 563a193..6febedb 100644 --- a/sound/pci/nm256/nm256.c +++ b/sound/pci/nm256/nm256.c @@ -1660,7 +1660,8 @@ static int snd_nm256_probe(struct pci_dev *pci, q = snd_pci_quirk_lookup(pci, nm256_quirks); if (q) { - snd_printdd(KERN_INFO "nm256: Enabled quirk for %s.\n", q->name); + snd_printdd(KERN_INFO "nm256: Enabled quirk for %s.\n", + snd_pci_quirk_name(q)); switch (q->value) { case NM_BLACKLISTED: printk(KERN_INFO "nm256: The device is blacklisted. " diff --git a/sound/pci/pcxhr/pcxhr_core.c b/sound/pci/pcxhr/pcxhr_core.c index b33db1e..37b431b 100644 --- a/sound/pci/pcxhr/pcxhr_core.c +++ b/sound/pci/pcxhr/pcxhr_core.c @@ -1012,13 +1012,12 @@ static int pcxhr_handle_async_err(struct pcxhr_mgr *mgr, u32 err, enum pcxhr_async_err_src err_src, int pipe, int is_capture) { -#ifdef CONFIG_SND_DEBUG_VERBOSE static char* err_src_name[] = { [PCXHR_ERR_PIPE] = "Pipe", [PCXHR_ERR_STREAM] = "Stream", [PCXHR_ERR_AUDIO] = "Audio" }; -#endif + if (err & 0xfff) err &= 0xfff; else diff --git a/sound/pci/via82xx.c b/sound/pci/via82xx.c index 6442f61..d756a35 100644 --- a/sound/pci/via82xx.c +++ b/sound/pci/via82xx.c @@ -2517,7 +2517,7 @@ static int check_dxs_list(struct pci_dev *pci, int revision) w = snd_pci_quirk_lookup(pci, dxs_whitelist); if (w) { snd_printdd(KERN_INFO "via82xx: DXS white list for %s found\n", - w->name); + snd_pci_quirk_name(w)); return w->value; } diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index b839b60..81f70a7 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -1179,9 +1179,7 @@ static void retire_capture_urb(struct snd_usb_substream *subs, if (!subs->txfr_quirk) bytes = frames * stride; if (bytes % (runtime->sample_bits >> 3) != 0) { -#ifdef CONFIG_SND_DEBUG_VERBOSE int oldbytes = bytes; -#endif bytes = frames * stride; snd_printdd(KERN_ERR "Corrected urb data len. %d->%d\n", oldbytes, bytes); -- cgit v0.10.2 From 36e8fe990153d015d9bb1d36f5bd70ec5b205204 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 25 Jan 2013 17:47:48 +0800 Subject: ASoC: wm_adsp: Add speaker Tx as a firmware option Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index 55a0089..99f00f8 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -150,10 +150,10 @@ #define ADSP2_RAM_RDY_SHIFT 0 #define ADSP2_RAM_RDY_WIDTH 1 -#define WM_ADSP_NUM_FW 3 +#define WM_ADSP_NUM_FW 4 static const char *wm_adsp_fw_text[WM_ADSP_NUM_FW] = { - "MBC/VSS", "Tx", "Rx ANC" + "MBC/VSS", "Tx", "Tx Speaker", "Rx ANC" }; static struct { @@ -161,6 +161,7 @@ static struct { } wm_adsp_fw[WM_ADSP_NUM_FW] = { { .file = "mbc-vss" }, { .file = "tx" }, + { .file = "tx-spk" }, { .file = "rx-anc" }, }; -- cgit v0.10.2 From 7480800ea682b70b0a15cda00eed7eedc90ceb9c Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sat, 26 Jan 2013 00:29:51 +0800 Subject: ASoC: wm_adsp: Accept 0 as a parameter block address Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index 99f00f8..8883164 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -590,27 +590,21 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp) be32_to_cpu(adsp1_alg[i].dm), be32_to_cpu(adsp1_alg[i].zm)); - if (adsp1_alg[i].dm) { - region = kzalloc(sizeof(*region), GFP_KERNEL); - if (!region) - return -ENOMEM; - region->type = WMFW_ADSP1_DM; - region->alg = be32_to_cpu(adsp1_alg[i].alg.id); - region->base = be32_to_cpu(adsp1_alg[i].dm); - list_add_tail(®ion->list, - &dsp->alg_regions); - } + region = kzalloc(sizeof(*region), GFP_KERNEL); + if (!region) + return -ENOMEM; + region->type = WMFW_ADSP1_DM; + region->alg = be32_to_cpu(adsp1_alg[i].alg.id); + region->base = be32_to_cpu(adsp1_alg[i].dm); + list_add_tail(®ion->list, &dsp->alg_regions); - if (adsp1_alg[i].zm) { - region = kzalloc(sizeof(*region), GFP_KERNEL); - if (!region) - return -ENOMEM; - region->type = WMFW_ADSP1_ZM; - region->alg = be32_to_cpu(adsp1_alg[i].alg.id); - region->base = be32_to_cpu(adsp1_alg[i].zm); - list_add_tail(®ion->list, - &dsp->alg_regions); - } + region = kzalloc(sizeof(*region), GFP_KERNEL); + if (!region) + return -ENOMEM; + region->type = WMFW_ADSP1_ZM; + region->alg = be32_to_cpu(adsp1_alg[i].alg.id); + region->base = be32_to_cpu(adsp1_alg[i].zm); + list_add_tail(®ion->list, &dsp->alg_regions); break; case WMFW_ADSP2: @@ -624,38 +618,29 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp) be32_to_cpu(adsp2_alg[i].ym), be32_to_cpu(adsp2_alg[i].zm)); - if (adsp2_alg[i].xm) { - region = kzalloc(sizeof(*region), GFP_KERNEL); - if (!region) - return -ENOMEM; - region->type = WMFW_ADSP2_XM; - region->alg = be32_to_cpu(adsp2_alg[i].alg.id); - region->base = be32_to_cpu(adsp2_alg[i].xm); - list_add_tail(®ion->list, - &dsp->alg_regions); - } + region = kzalloc(sizeof(*region), GFP_KERNEL); + if (!region) + return -ENOMEM; + region->type = WMFW_ADSP2_XM; + region->alg = be32_to_cpu(adsp2_alg[i].alg.id); + region->base = be32_to_cpu(adsp2_alg[i].xm); + list_add_tail(®ion->list, &dsp->alg_regions); - if (adsp2_alg[i].ym) { - region = kzalloc(sizeof(*region), GFP_KERNEL); - if (!region) - return -ENOMEM; - region->type = WMFW_ADSP2_YM; - region->alg = be32_to_cpu(adsp2_alg[i].alg.id); - region->base = be32_to_cpu(adsp2_alg[i].ym); - list_add_tail(®ion->list, - &dsp->alg_regions); - } + region = kzalloc(sizeof(*region), GFP_KERNEL); + if (!region) + return -ENOMEM; + region->type = WMFW_ADSP2_YM; + region->alg = be32_to_cpu(adsp2_alg[i].alg.id); + region->base = be32_to_cpu(adsp2_alg[i].ym); + list_add_tail(®ion->list, &dsp->alg_regions); - if (adsp2_alg[i].zm) { - region = kzalloc(sizeof(*region), GFP_KERNEL); - if (!region) - return -ENOMEM; - region->type = WMFW_ADSP2_ZM; - region->alg = be32_to_cpu(adsp2_alg[i].alg.id); - region->base = be32_to_cpu(adsp2_alg[i].zm); - list_add_tail(®ion->list, - &dsp->alg_regions); - } + region = kzalloc(sizeof(*region), GFP_KERNEL); + if (!region) + return -ENOMEM; + region->type = WMFW_ADSP2_ZM; + region->alg = be32_to_cpu(adsp2_alg[i].alg.id); + region->base = be32_to_cpu(adsp2_alg[i].zm); + list_add_tail(®ion->list, &dsp->alg_regions); break; } } -- cgit v0.10.2 From 2d77828d9904494d3c7424189ee38cc07950df5e Mon Sep 17 00:00:00 2001 From: Padmavathi Venna Date: Thu, 24 Jan 2013 18:05:31 +0530 Subject: ASoC: Samsung: Add I2S S/W RST in startup function I2S module need to be reset after S2R. Keeping the S/W rst control part in resume didn't help in playing audio after resume. So this patch adds S/W RST control part in startup function which gets triggered for every new audio stream playback. Signed-off-by: Padmavathi Venna Signed-off-by: R. Chandrasekar Signed-off-by: Mark Brown diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c index ed5eeae..808df74 100644 --- a/sound/soc/samsung/i2s.c +++ b/sound/soc/samsung/i2s.c @@ -656,6 +656,9 @@ static int i2s_startup(struct snd_pcm_substream *substream, /* Enforce set_sysclk in Master mode */ i2s->rclk_srcrate = 0; + if (!any_active(i2s) && (i2s->quirks & QUIRK_NEED_RSTCLR)) + writel(CON_RSTCLR, i2s->addr + I2SCON); + spin_unlock_irqrestore(&lock, flags); return 0; -- cgit v0.10.2 From 0099d24c6bb584d3bac76ca81bb55f3fd8ddfb21 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 25 Jan 2013 09:43:43 +0300 Subject: ASoC: dwc: fix support for more than two channels MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There were missing break statements so everything used TWO_CHANNEL_SUPPORT. Also I added a return statement to silence a GCC warning: sound/soc/dwc/designware_i2s.c: In function ‘dw_i2s_hw_params’: sound/soc/dwc/designware_i2s.c:236:32: warning: ‘ch_reg’ may be used uninitialized in this function [-Wmaybe-uninitialized] Signed-off-by: Dan Carpenter Acked-by: Rajeev Kumar Signed-off-by: Mark Brown diff --git a/sound/soc/dwc/designware_i2s.c b/sound/soc/dwc/designware_i2s.c index 1aa5130..deb30d5 100644 --- a/sound/soc/dwc/designware_i2s.c +++ b/sound/soc/dwc/designware_i2s.c @@ -210,15 +210,19 @@ static int dw_i2s_hw_params(struct snd_pcm_substream *substream, switch (config->chan_nr) { case EIGHT_CHANNEL_SUPPORT: ch_reg = 3; + break; case SIX_CHANNEL_SUPPORT: ch_reg = 2; + break; case FOUR_CHANNEL_SUPPORT: ch_reg = 1; + break; case TWO_CHANNEL_SUPPORT: ch_reg = 0; break; default: dev_err(dev->dev, "channel not supported\n"); + return -EINVAL; } i2s_disable_channels(dev, substream->stream); -- cgit v0.10.2 From a7930ed458afeacb029cee2b22f77b2a15472ad6 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 14 Jan 2013 18:36:04 -0800 Subject: ASoC: add snd_soc_of_parse_daifmt() for DeviceTree This patch adds snd_soc_of_parse_daifmt() and supports below style on DT. [prefix]format = "i2c"; [prefix]clock-gating = "continuous"; [prefix]bitclock-inversion; [prefix]bitclock-master; [prefix]frame-master; Each driver can use specific [prefix] (ex simple-card,cpu,dai,format = xxx;) This sample will be SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CONT | SND_SOC_DAIFMT_IB_NF | SND_SOC_DAIFMT_CBM_CFM Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown diff --git a/include/sound/soc.h b/include/sound/soc.h index bc56738..5715623 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -1171,6 +1171,8 @@ int snd_soc_of_parse_card_name(struct snd_soc_card *card, const char *propname); int snd_soc_of_parse_audio_routing(struct snd_soc_card *card, const char *propname); +unsigned int snd_soc_of_parse_daifmt(struct device_node *np, + const char *prefix); #include diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 2370063..9d07dc0 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -4208,6 +4208,121 @@ int snd_soc_of_parse_audio_routing(struct snd_soc_card *card, } EXPORT_SYMBOL_GPL(snd_soc_of_parse_audio_routing); +unsigned int snd_soc_of_parse_daifmt(struct device_node *np, + const char *prefix) +{ + int ret, i; + char prop[128]; + unsigned int format = 0; + int bit, frame; + const char *str; + struct { + char *name; + unsigned int val; + } of_fmt_table[] = { + { "i2s", SND_SOC_DAIFMT_I2S }, + { "right_j", SND_SOC_DAIFMT_RIGHT_J }, + { "left_j", SND_SOC_DAIFMT_LEFT_J }, + { "dsp_a", SND_SOC_DAIFMT_DSP_A }, + { "dsp_b", SND_SOC_DAIFMT_DSP_B }, + { "ac97", SND_SOC_DAIFMT_AC97 }, + { "pdm", SND_SOC_DAIFMT_PDM}, + { "msb", SND_SOC_DAIFMT_MSB }, + { "lsb", SND_SOC_DAIFMT_LSB }, + }, of_clock_table[] = { + { "continuous", SND_SOC_DAIFMT_CONT }, + { "gated", SND_SOC_DAIFMT_GATED }, + }; + + if (!prefix) + prefix = ""; + + /* + * check "[prefix]format = xxx" + * SND_SOC_DAIFMT_FORMAT_MASK area + */ + snprintf(prop, sizeof(prop), "%sformat", prefix); + ret = of_property_read_string(np, prop, &str); + if (ret == 0) { + for (i = 0; i < ARRAY_SIZE(of_fmt_table); i++) { + if (strcmp(str, of_fmt_table[i].name) == 0) { + format |= of_fmt_table[i].val; + break; + } + } + } + + /* + * check "[prefix]clock-gating = xxx" + * SND_SOC_DAIFMT_CLOCK_MASK area + */ + snprintf(prop, sizeof(prop), "%sclock-gating", prefix); + ret = of_property_read_string(np, prop, &str); + if (ret == 0) { + for (i = 0; i < ARRAY_SIZE(of_clock_table); i++) { + if (strcmp(str, of_clock_table[i].name) == 0) { + format |= of_clock_table[i].val; + break; + } + } + } + + /* + * check "[prefix]bitclock-inversion" + * check "[prefix]frame-inversion" + * SND_SOC_DAIFMT_INV_MASK area + */ + snprintf(prop, sizeof(prop), "%sbitclock-inversion", prefix); + bit = !!of_get_property(np, prop, NULL); + + snprintf(prop, sizeof(prop), "%sframe-inversion", prefix); + frame = !!of_get_property(np, prop, NULL); + + switch ((bit << 4) + frame) { + case 0x11: + format |= SND_SOC_DAIFMT_IB_IF; + break; + case 0x10: + format |= SND_SOC_DAIFMT_IB_NF; + break; + case 0x01: + format |= SND_SOC_DAIFMT_NB_IF; + break; + default: + /* SND_SOC_DAIFMT_NB_NF is default */ + break; + } + + /* + * check "[prefix]bitclock-master" + * check "[prefix]frame-master" + * SND_SOC_DAIFMT_MASTER_MASK area + */ + snprintf(prop, sizeof(prop), "%sbitclock-master", prefix); + bit = !!of_get_property(np, prop, NULL); + + snprintf(prop, sizeof(prop), "%sframe-master", prefix); + frame = !!of_get_property(np, prop, NULL); + + switch ((bit << 4) + frame) { + case 0x11: + format |= SND_SOC_DAIFMT_CBM_CFM; + break; + case 0x10: + format |= SND_SOC_DAIFMT_CBM_CFS; + break; + case 0x01: + format |= SND_SOC_DAIFMT_CBS_CFM; + break; + default: + format |= SND_SOC_DAIFMT_CBS_CFS; + break; + } + + return format; +} +EXPORT_SYMBOL_GPL(snd_soc_of_parse_daifmt); + static int __init snd_soc_init(void) { #ifdef CONFIG_DEBUG_FS -- cgit v0.10.2 From dde109fb46256f61642ba9bf0199418dfaaca12b Mon Sep 17 00:00:00 2001 From: Michal Bachraty Date: Fri, 18 Jan 2013 10:17:00 +0100 Subject: ASoC: McASP: Fix data rotation for playback. Enables 24bit audio playback u32 rotate = (32 - word_length) / 4; This implementation is wrong, but it works only for 16, or 32 bit audio data. (rotation for 16 or 32 bit is same as in code I present) Mcasp rotated data in 4 bits (max value 0x7)and then masks them . That data are sended to i2s bus. For 24 bit or 20 bit or other data formats, this code rotates data badly and you hear somethink like noise. You need to use u32 rotate = (word_length / 4) & 0x7; to proper data rotation. Signed-off-by: Michal Bachraty Signed-off-by: Mark Brown diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c index 55e2bf6..9321e5c 100644 --- a/sound/soc/davinci/davinci-mcasp.c +++ b/sound/soc/davinci/davinci-mcasp.c @@ -626,7 +626,7 @@ static int davinci_config_channel_size(struct davinci_audio_dev *dev, int word_length) { u32 fmt; - u32 rotate = (32 - word_length) / 4; + u32 rotate = (word_length / 4) & 0x7; u32 mask = (1ULL << word_length) - 1; /* -- cgit v0.10.2 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 diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 1c49861..951fc0d 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 v0.10.2 From 2d30b5751d3fdcc589985929ffb3969520f6c828 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 28 Jan 2013 20:18:17 +0800 Subject: ASoC: wm_adsp: Ensure ADSP2 DMAs are quiesced when DSP is halted Maximise robustness for the widest range of firmwares by ensuring the DSP is in a consistent state when halted. Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index b6b6548..93d03bc 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -103,9 +103,12 @@ #define ADSP1_START_SHIFT 0 /* DSP1_START */ #define ADSP1_START_WIDTH 1 /* DSP1_START */ -#define ADSP2_CONTROL 0 -#define ADSP2_CLOCKING 1 -#define ADSP2_STATUS1 4 +#define ADSP2_CONTROL 0x0 +#define ADSP2_CLOCKING 0x1 +#define ADSP2_STATUS1 0x4 +#define ADSP2_WDMA_CONFIG_1 0x30 +#define ADSP2_WDMA_CONFIG_2 0x31 +#define ADSP2_RDMA_CONFIG_1 0x34 /* * ADSP2 Control @@ -642,6 +645,11 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w, ADSP2_SYS_ENA | ADSP2_CORE_ENA | ADSP2_START, 0); + /* Make sure DMAs are quiesced */ + regmap_write(dsp->regmap, dsp->base + ADSP2_WDMA_CONFIG_1, 0); + regmap_write(dsp->regmap, dsp->base + ADSP2_WDMA_CONFIG_2, 0); + regmap_write(dsp->regmap, dsp->base + ADSP2_RDMA_CONFIG_1, 0); + if (dsp->dvfs) { ret = regulator_set_voltage(dsp->dvfs, 1200000, 1800000); -- cgit v0.10.2 From 40476f61897933d524b7069a6df65629a469d922 Mon Sep 17 00:00:00 2001 From: Padmavathi Venna Date: Fri, 18 Jan 2013 17:17:01 +0530 Subject: ASoC: samsung: Add DT support for i2s Add support for device based discovery. Signed-off-by: Padmavathi Venna Signed-off-by: Mark Brown diff --git a/Documentation/devicetree/bindings/sound/samsung-i2s.txt b/Documentation/devicetree/bindings/sound/samsung-i2s.txt new file mode 100644 index 0000000..3070046 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/samsung-i2s.txt @@ -0,0 +1,63 @@ +* Samsung I2S controller + +Required SoC Specific Properties: + +- compatible : "samsung,i2s-v5" +- reg: physical base address of the controller and length of memory mapped + region. +- dmas: list of DMA controller phandle and DMA request line ordered pairs. +- dma-names: identifier string for each DMA request line in the dmas property. + These strings correspond 1:1 with the ordered pairs in dmas. + +Optional SoC Specific Properties: + +- samsung,supports-6ch: If the I2S Primary sound source has 5.1 Channel + support, this flag is enabled. +- samsung,supports-rstclr: This flag should be set if I2S software reset bit + control is required. When this flag is set I2S software reset bit will be + enabled or disabled based on need. +- samsung,supports-secdai:If I2S block has a secondary FIFO and internal DMA, + then this flag is enabled. +- samsung,idma-addr: Internal DMA register base address of the audio + sub system(used in secondary sound source). + +Required Board Specific Properties: + +- gpios: The gpio specifier for data out,data in, LRCLK, CDCLK and SCLK + interface lines. The format of the gpio specifier depends on the gpio + controller. + The syntax of samsung gpio specifier is + <[phandle of the gpio controller node] + [pin number within the gpio controller] + [mux function] + [flags and pull up/down] + [drive strength]> + +Example: + +- SoC Specific Portion: + +i2s@03830000 { + compatible = "samsung,i2s-v5"; + reg = <0x03830000 0x100>; + dmas = <&pdma0 10 + &pdma0 9 + &pdma0 8>; + dma-names = "tx", "rx", "tx-sec"; + samsung,supports-6ch; + samsung,supports-rstclr; + samsung,supports-secdai; + samsung,idma-addr = <0x03000000>; +}; + +- Board Specific Portion: + +i2s@03830000 { + gpios = <&gpz 0 2 0 0>, /* I2S_0_SCLK */ + <&gpz 1 2 0 0>, /* I2S_0_CDCLK */ + <&gpz 2 2 0 0>, /* I2S_0_LRCK */ + <&gpz 3 2 0 0>, /* I2S_0_SDI */ + <&gpz 4 2 0 0>, /* I2S_0_SDO[1] */ + <&gpz 5 2 0 0>, /* I2S_0_SDO[2] */ + <&gpz 6 2 0 0>; /* I2S_0_SDO[3] */ +}; diff --git a/sound/soc/samsung/dma.c b/sound/soc/samsung/dma.c index db87628..21b7926 100644 --- a/sound/soc/samsung/dma.c +++ b/sound/soc/samsung/dma.c @@ -174,7 +174,8 @@ static int dma_hw_params(struct snd_pcm_substream *substream, config.width = prtd->params->dma_size; config.fifo = prtd->params->dma_addr; prtd->params->ch = prtd->params->ops->request( - prtd->params->channel, &req); + prtd->params->channel, &req, rtd->cpu_dai->dev, + prtd->params->ch_name); prtd->params->ops->config(prtd->params->ch, &config); } diff --git a/sound/soc/samsung/dma.h b/sound/soc/samsung/dma.h index 73d8c7c..189a7a6 100644 --- a/sound/soc/samsung/dma.h +++ b/sound/soc/samsung/dma.h @@ -19,6 +19,7 @@ struct s3c_dma_params { int dma_size; /* Size of the DMA transfer */ unsigned ch; struct samsung_dma_ops *ops; + char *ch_name; }; int asoc_dma_platform_register(struct device *dev); diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c index 808df74..2fc42f9 100644 --- a/sound/soc/samsung/i2s.c +++ b/sound/soc/samsung/i2s.c @@ -15,11 +15,15 @@ #include #include #include +#include +#include #include #include #include +#include + #include #include "dma.h" @@ -34,6 +38,10 @@ enum samsung_dai_type { TYPE_SEC, }; +struct samsung_i2s_dai_data { + int dai_type; +}; + struct i2s_dai { /* Platform device for this DAI */ struct platform_device *pdev; @@ -71,6 +79,7 @@ struct i2s_dai { u32 suspend_i2smod; u32 suspend_i2scon; u32 suspend_i2spsr; + unsigned long gpios[7]; /* i2s gpio line numbers */ }; /* Lock for cross i/f checks */ @@ -1000,19 +1009,76 @@ static struct i2s_dai *i2s_alloc_dai(struct platform_device *pdev, bool sec) return i2s; } +#ifdef CONFIG_OF +static int samsung_i2s_parse_dt_gpio(struct i2s_dai *i2s) +{ + struct device *dev = &i2s->pdev->dev; + int index, gpio, ret; + + for (index = 0; index < 7; index++) { + gpio = of_get_gpio(dev->of_node, index); + if (!gpio_is_valid(gpio)) { + dev_err(dev, "invalid gpio[%d]: %d\n", index, gpio); + goto free_gpio; + } + + ret = gpio_request(gpio, dev_name(dev)); + if (ret) { + dev_err(dev, "gpio [%d] request failed\n", gpio); + goto free_gpio; + } + i2s->gpios[index] = gpio; + } + return 0; + +free_gpio: + while (--index >= 0) + gpio_free(i2s->gpios[index]); + return -EINVAL; +} + +static void samsung_i2s_dt_gpio_free(struct i2s_dai *i2s) +{ + unsigned int index; + for (index = 0; index < 7; index++) + gpio_free(i2s->gpios[index]); +} +#else +static int samsung_i2s_parse_dt_gpio(struct i2s_dai *dai) +{ + return -EINVAL; +} + +static void samsung_i2s_dt_gpio_free(struct i2s_dai *dai) +{ +} + +#endif + +static const struct of_device_id exynos_i2s_match[]; + static inline int samsung_i2s_get_driver_data(struct platform_device *pdev) { - return platform_get_device_id(pdev)->driver_data; +#ifdef CONFIG_OF + struct samsung_i2s_dai_data *data; + if (pdev->dev.of_node) { + const struct of_device_id *match; + match = of_match_node(exynos_i2s_match, pdev->dev.of_node); + data = (struct samsung_i2s_dai_data *) match->data; + return data->dai_type; + } else +#endif + return platform_get_device_id(pdev)->driver_data; } static int samsung_i2s_probe(struct platform_device *pdev) { - u32 dma_pl_chan, dma_cp_chan, dma_pl_sec_chan; struct i2s_dai *pri_dai, *sec_dai = NULL; - struct s3c_audio_pdata *i2s_pdata; - struct samsung_i2s *i2s_cfg; + struct s3c_audio_pdata *i2s_pdata = pdev->dev.platform_data; + struct samsung_i2s *i2s_cfg = NULL; struct resource *res; - u32 regs_base, quirks; + u32 regs_base, quirks = 0, idma_addr = 0; + struct device_node *np = pdev->dev.of_node; enum samsung_dai_type samsung_dai_type; int ret = 0; @@ -1027,31 +1093,60 @@ static int samsung_i2s_probe(struct platform_device *pdev) return 0; } - i2s_pdata = pdev->dev.platform_data; - if (i2s_pdata == NULL) { - dev_err(&pdev->dev, "Can't work without s3c_audio_pdata\n"); - return -EINVAL; + pri_dai = i2s_alloc_dai(pdev, false); + if (!pri_dai) { + dev_err(&pdev->dev, "Unable to alloc I2S_pri\n"); + return -ENOMEM; } - res = platform_get_resource(pdev, IORESOURCE_DMA, 0); - if (!res) { - dev_err(&pdev->dev, "Unable to get I2S-TX dma resource\n"); - return -ENXIO; - } - dma_pl_chan = res->start; + if (!np) { + res = platform_get_resource(pdev, IORESOURCE_DMA, 0); + if (!res) { + dev_err(&pdev->dev, + "Unable to get I2S-TX dma resource\n"); + return -ENXIO; + } + pri_dai->dma_playback.channel = res->start; - res = platform_get_resource(pdev, IORESOURCE_DMA, 1); - if (!res) { - dev_err(&pdev->dev, "Unable to get I2S-RX dma resource\n"); - return -ENXIO; - } - dma_cp_chan = res->start; + res = platform_get_resource(pdev, IORESOURCE_DMA, 1); + if (!res) { + dev_err(&pdev->dev, + "Unable to get I2S-RX dma resource\n"); + return -ENXIO; + } + pri_dai->dma_capture.channel = res->start; - res = platform_get_resource(pdev, IORESOURCE_DMA, 2); - if (res) - dma_pl_sec_chan = res->start; - else - dma_pl_sec_chan = 0; + if (i2s_pdata == NULL) { + dev_err(&pdev->dev, "Can't work without s3c_audio_pdata\n"); + return -EINVAL; + } + + if (&i2s_pdata->type) + i2s_cfg = &i2s_pdata->type.i2s; + + if (i2s_cfg) { + quirks = i2s_cfg->quirks; + idma_addr = i2s_cfg->idma_addr; + } + } else { + if (of_find_property(np, "samsung,supports-6ch", NULL)) + quirks |= QUIRK_PRI_6CHAN; + + if (of_find_property(np, "samsung,supports-secdai", NULL)) + quirks |= QUIRK_SEC_DAI; + + if (of_find_property(np, "samsung,supports-rstclr", NULL)) + quirks |= QUIRK_NEED_RSTCLR; + + if (of_property_read_u32(np, "samsung,idma-addr", + &idma_addr)) { + if (quirks & QUIRK_SEC_DAI) { + dev_err(&pdev->dev, "idma address is not"\ + "specified"); + return -EINVAL; + } + } + } res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { @@ -1066,24 +1161,14 @@ static int samsung_i2s_probe(struct platform_device *pdev) } regs_base = res->start; - i2s_cfg = &i2s_pdata->type.i2s; - quirks = i2s_cfg->quirks; - - pri_dai = i2s_alloc_dai(pdev, false); - if (!pri_dai) { - dev_err(&pdev->dev, "Unable to alloc I2S_pri\n"); - ret = -ENOMEM; - goto err; - } - pri_dai->dma_playback.dma_addr = regs_base + I2STXD; pri_dai->dma_capture.dma_addr = regs_base + I2SRXD; pri_dai->dma_playback.client = (struct s3c2410_dma_client *)&pri_dai->dma_playback; + pri_dai->dma_playback.ch_name = "tx"; pri_dai->dma_capture.client = (struct s3c2410_dma_client *)&pri_dai->dma_capture; - pri_dai->dma_playback.channel = dma_pl_chan; - pri_dai->dma_capture.channel = dma_cp_chan; + pri_dai->dma_capture.ch_name = "rx"; pri_dai->dma_playback.dma_size = 4; pri_dai->dma_capture.dma_size = 4; pri_dai->base = regs_base; @@ -1102,20 +1187,34 @@ static int samsung_i2s_probe(struct platform_device *pdev) sec_dai->dma_playback.dma_addr = regs_base + I2STXDS; sec_dai->dma_playback.client = (struct s3c2410_dma_client *)&sec_dai->dma_playback; - /* Use iDMA always if SysDMA not provided */ - sec_dai->dma_playback.channel = dma_pl_sec_chan ? : -1; + sec_dai->dma_playback.ch_name = "tx-sec"; + + if (!np) { + res = platform_get_resource(pdev, IORESOURCE_DMA, 2); + if (res) + sec_dai->dma_playback.channel = res->start; + } + sec_dai->dma_playback.dma_size = 4; sec_dai->base = regs_base; sec_dai->quirks = quirks; - sec_dai->idma_playback.dma_addr = i2s_cfg->idma_addr; + sec_dai->idma_playback.dma_addr = idma_addr; sec_dai->pri_dai = pri_dai; pri_dai->sec_dai = sec_dai; } - if (i2s_pdata->cfg_gpio && i2s_pdata->cfg_gpio(pdev)) { - dev_err(&pdev->dev, "Unable to configure gpio\n"); - ret = -EINVAL; - goto err; + if (np) { + if (samsung_i2s_parse_dt_gpio(pri_dai)) { + dev_err(&pdev->dev, "Unable to configure gpio\n"); + ret = -EINVAL; + goto err; + } + } else { + if (i2s_pdata->cfg_gpio && i2s_pdata->cfg_gpio(pdev)) { + dev_err(&pdev->dev, "Unable to configure gpio\n"); + ret = -EINVAL; + goto err; + } } snd_soc_register_dai(&pri_dai->pdev->dev, &pri_dai->i2s_dai_drv); @@ -1135,10 +1234,14 @@ static int samsung_i2s_remove(struct platform_device *pdev) { struct i2s_dai *i2s, *other; struct resource *res; + struct s3c_audio_pdata *i2s_pdata = pdev->dev.platform_data; i2s = dev_get_drvdata(&pdev->dev); other = i2s->pri_dai ? : i2s->sec_dai; + if (!i2s_pdata->cfg_gpio && pdev->dev.of_node) + samsung_i2s_dt_gpio_free(i2s->pri_dai); + if (other) { other->pri_dai = NULL; other->sec_dai = NULL; @@ -1170,6 +1273,21 @@ static struct platform_device_id samsung_i2s_driver_ids[] = { }; MODULE_DEVICE_TABLE(platform, samsung-i2s-driver-ids); +#ifdef CONFIG_OF +static struct samsung_i2s_dai_data samsung_i2s_dai_data_array[] = { + [TYPE_PRI] = { TYPE_PRI }, + [TYPE_SEC] = { TYPE_SEC }, +}; + +static const struct of_device_id exynos_i2s_match[] = { + { .compatible = "samsung,i2s-v5", + .data = &samsung_i2s_dai_data_array[TYPE_PRI], + }, + {}, +}; +MODULE_DEVICE_TABLE(of, exynos_i2s_match); +#endif + static struct platform_driver samsung_i2s_driver = { .probe = samsung_i2s_probe, .remove = samsung_i2s_remove, @@ -1177,6 +1295,7 @@ static struct platform_driver samsung_i2s_driver = { .driver = { .name = "samsung-i2s", .owner = THIS_MODULE, + .of_match_table = of_match_ptr(exynos_i2s_match), }, }; -- cgit v0.10.2 From e7ba5f1d0f6292e1b99c63cc4bb74c70232e9065 Mon Sep 17 00:00:00 2001 From: Padmavathi Venna Date: Fri, 18 Jan 2013 17:17:02 +0530 Subject: ARM: SAMSUNG: Make dma request compatible to generic dma bindings. This patch make the dma dev request operation compatible for both DT and non-DT cases. It takes the all the arguments required for dma_request_slave_channel and dma_request_channel. If the driver is initiated via DT or non-DT the corresponding call will be made. Signed-off-by: Padmavathi Venna Signed-off-by: Mark Brown diff --git a/arch/arm/plat-samsung/dma-ops.c b/arch/arm/plat-samsung/dma-ops.c index d088afa..71d58dd 100644 --- a/arch/arm/plat-samsung/dma-ops.c +++ b/arch/arm/plat-samsung/dma-ops.c @@ -19,7 +19,8 @@ #include static unsigned samsung_dmadev_request(enum dma_ch dma_ch, - struct samsung_dma_req *param) + struct samsung_dma_req *param, + struct device *dev, char *ch_name) { dma_cap_mask_t mask; void *filter_param; @@ -33,7 +34,12 @@ static unsigned samsung_dmadev_request(enum dma_ch dma_ch, */ filter_param = (dma_ch == DMACH_DT_PROP) ? (void *)param->dt_dmach_prop : (void *)dma_ch; - return (unsigned)dma_request_channel(mask, pl330_filter, filter_param); + + if (dev->of_node) + return (unsigned)dma_request_slave_channel(dev, ch_name); + else + return (unsigned)dma_request_channel(mask, pl330_filter, + filter_param); } static int samsung_dmadev_release(unsigned ch, void *param) diff --git a/arch/arm/plat-samsung/include/plat/dma-ops.h b/arch/arm/plat-samsung/include/plat/dma-ops.h index f5144cd..1141782 100644 --- a/arch/arm/plat-samsung/include/plat/dma-ops.h +++ b/arch/arm/plat-samsung/include/plat/dma-ops.h @@ -39,7 +39,8 @@ struct samsung_dma_config { }; struct samsung_dma_ops { - unsigned (*request)(enum dma_ch ch, struct samsung_dma_req *param); + unsigned (*request)(enum dma_ch ch, struct samsung_dma_req *param, + struct device *dev, char *ch_name); int (*release)(unsigned ch, void *param); int (*config)(unsigned ch, struct samsung_dma_config *param); int (*prepare)(unsigned ch, struct samsung_dma_prep *param); diff --git a/arch/arm/plat-samsung/s3c-dma-ops.c b/arch/arm/plat-samsung/s3c-dma-ops.c index f99448c..0cc40ae 100644 --- a/arch/arm/plat-samsung/s3c-dma-ops.c +++ b/arch/arm/plat-samsung/s3c-dma-ops.c @@ -36,7 +36,8 @@ static void s3c_dma_cb(struct s3c2410_dma_chan *channel, void *param, } static unsigned s3c_dma_request(enum dma_ch dma_ch, - struct samsung_dma_req *param) + struct samsung_dma_req *param, + struct device *dev, char *ch_name) { struct cb_data *data; -- cgit v0.10.2 From b5be04d35dbb2e00ab27a97bfd26e17019e857ef Mon Sep 17 00:00:00 2001 From: Padmavathi Venna Date: Fri, 18 Jan 2013 17:17:03 +0530 Subject: spi: s3c64xx: Modify SPI driver to use generic DMA DT support This patch modifies the SPI driver to use generic dma dt bindings support. This passes all the required arguments to dma dev request functon which in turn calls the dma_request_slave_channel or dma__ request_channel based on DT or non-DT respectively. Signed-off-by: Padmavathi Venna Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c index ad93231..51a8c42 100644 --- a/drivers/spi/spi-s3c64xx.c +++ b/drivers/spi/spi-s3c64xx.c @@ -134,7 +134,6 @@ struct s3c64xx_spi_dma_data { unsigned ch; enum dma_transfer_direction direction; enum dma_ch dmach; - struct property *dma_prop; }; /** @@ -319,16 +318,15 @@ static void prepare_dma(struct s3c64xx_spi_dma_data *dma, static int acquire_dma(struct s3c64xx_spi_driver_data *sdd) { struct samsung_dma_req req; + struct device *dev = &sdd->pdev->dev; sdd->ops = samsung_dma_get_ops(); req.cap = DMA_SLAVE; req.client = &s3c64xx_spi_dma_client; - req.dt_dmach_prop = sdd->rx_dma.dma_prop; - sdd->rx_dma.ch = sdd->ops->request(sdd->rx_dma.dmach, &req); - req.dt_dmach_prop = sdd->tx_dma.dma_prop; - sdd->tx_dma.ch = sdd->ops->request(sdd->tx_dma.dmach, &req); + sdd->rx_dma.ch = sdd->ops->request(sdd->rx_dma.dmach, &req, dev, "rx"); + sdd->tx_dma.ch = sdd->ops->request(sdd->tx_dma.dmach, &req, dev, "tx"); return 1; } @@ -1054,49 +1052,6 @@ static void s3c64xx_spi_hwinit(struct s3c64xx_spi_driver_data *sdd, int channel) flush_fifo(sdd); } -static int s3c64xx_spi_get_dmares( - struct s3c64xx_spi_driver_data *sdd, bool tx) -{ - struct platform_device *pdev = sdd->pdev; - struct s3c64xx_spi_dma_data *dma_data; - struct property *prop; - struct resource *res; - char prop_name[15], *chan_str; - - if (tx) { - dma_data = &sdd->tx_dma; - dma_data->direction = DMA_MEM_TO_DEV; - chan_str = "tx"; - } else { - dma_data = &sdd->rx_dma; - dma_data->direction = DMA_DEV_TO_MEM; - chan_str = "rx"; - } - - if (!sdd->pdev->dev.of_node) { - res = platform_get_resource(pdev, IORESOURCE_DMA, tx ? 0 : 1); - if (!res) { - dev_err(&pdev->dev, "Unable to get SPI-%s dma " - "resource\n", chan_str); - return -ENXIO; - } - dma_data->dmach = res->start; - return 0; - } - - sprintf(prop_name, "%s-dma-channel", chan_str); - prop = of_find_property(pdev->dev.of_node, prop_name, NULL); - if (!prop) { - dev_err(&pdev->dev, "%s dma channel property not specified\n", - chan_str); - return -ENXIO; - } - - dma_data->dmach = DMACH_DT_PROP; - dma_data->dma_prop = prop; - return 0; -} - #ifdef CONFIG_OF static int s3c64xx_spi_parse_dt_gpio(struct s3c64xx_spi_driver_data *sdd) { @@ -1198,6 +1153,7 @@ static inline struct s3c64xx_spi_port_config *s3c64xx_spi_get_port_config( static int __init s3c64xx_spi_probe(struct platform_device *pdev) { struct resource *mem_res; + struct resource *res; struct s3c64xx_spi_driver_data *sdd; struct s3c64xx_spi_info *sci = pdev->dev.platform_data; struct spi_master *master; @@ -1256,13 +1212,26 @@ static int __init s3c64xx_spi_probe(struct platform_device *pdev) sdd->cur_bpw = 8; - ret = s3c64xx_spi_get_dmares(sdd, true); - if (ret) - goto err0; + if (!sdd->pdev->dev.of_node) { + res = platform_get_resource(pdev, IORESOURCE_DMA, 0); + if (!res) { + dev_err(&pdev->dev, "Unable to get SPI tx dma " + "resource\n"); + return -ENXIO; + } + sdd->tx_dma.dmach = res->start; + + res = platform_get_resource(pdev, IORESOURCE_DMA, 1); + if (!res) { + dev_err(&pdev->dev, "Unable to get SPI rx dma " + "resource\n"); + return -ENXIO; + } + sdd->rx_dma.dmach = res->start; + } - ret = s3c64xx_spi_get_dmares(sdd, false); - if (ret) - goto err0; + sdd->tx_dma.direction = DMA_MEM_TO_DEV; + sdd->rx_dma.direction = DMA_DEV_TO_MEM; master->dev.of_node = pdev->dev.of_node; master->bus_num = sdd->port_id; -- cgit v0.10.2 From 4c4c746399a6fdd34c4f0c60e4041c9d49f3b940 Mon Sep 17 00:00:00 2001 From: Padmavathi Venna Date: Fri, 18 Jan 2013 17:17:04 +0530 Subject: ARM: dts: Add nodes for i2s controllers for Samsung Exynos5 platforms Add device nodes for the three instances of i2s controllers in Exynos5 platforms. Enable instance i2s 0 for exynos5250 board and disable all other i2s instances. Signed-off-by: Padmavathi Venna Signed-off-by: Mark Brown diff --git a/arch/arm/boot/dts/exynos5250-smdk5250.dts b/arch/arm/boot/dts/exynos5250-smdk5250.dts index 942d576..78fee35 100644 --- a/arch/arm/boot/dts/exynos5250-smdk5250.dts +++ b/arch/arm/boot/dts/exynos5250-smdk5250.dts @@ -204,4 +204,18 @@ samsung,mfc-r = <0x43000000 0x800000>; samsung,mfc-l = <0x51000000 0x800000>; }; + + i2s@03830000 { + gpios = <&gpz 0 2 0 0>, <&gpz 1 2 0 0>, <&gpz 2 2 0 0>, + <&gpz 3 2 0 0>, <&gpz 4 2 0 0>, <&gpz 5 2 0 0>, + <&gpz 6 2 0 0>; + }; + + i2s@12D60000 { + status = "disabled"; + }; + + i2s@12D70000 { + status = "disabled"; + }; }; diff --git a/arch/arm/boot/dts/exynos5250.dtsi b/arch/arm/boot/dts/exynos5250.dtsi index 3acf594..fe05b60 100644 --- a/arch/arm/boot/dts/exynos5250.dtsi +++ b/arch/arm/boot/dts/exynos5250.dtsi @@ -269,6 +269,35 @@ #size-cells = <0>; }; + i2s@03830000 { + compatible = "samsung,i2s-v5"; + reg = <0x03830000 0x100>; + dmas = <&pdma0 10 + &pdma0 9 + &pdma0 8>; + dma-names = "tx", "rx", "tx-sec"; + samsung,supports-6ch; + samsung,supports-rstclr; + samsung,supports-secdai; + samsung,idma-addr = <0x03000000>; + }; + + i2s@12D60000 { + compatible = "samsung,i2s-v5"; + reg = <0x12D60000 0x100>; + dmas = <&pdma1 12 + &pdma1 11>; + dma-names = "tx", "rx"; + }; + + i2s@12D70000 { + compatible = "samsung,i2s-v5"; + reg = <0x12D70000 0x100>; + dmas = <&pdma0 12 + &pdma0 11>; + dma-names = "tx", "rx"; + }; + amba { #address-cells = <1>; #size-cells = <1>; -- cgit v0.10.2 From 99b97fdccbde9860366ec00bd61f2622055f1b86 Mon Sep 17 00:00:00 2001 From: Padmavathi Venna Date: Fri, 18 Jan 2013 17:17:05 +0530 Subject: ARM: EXYNOS: Enable platform support for I2S controllers Add AUXDATA entries for i2s controller driver so as to set the device name for clock lookups Signed-off-by: Padmavathi Venna Signed-off-by: Mark Brown diff --git a/arch/arm/mach-exynos/mach-exynos5-dt.c b/arch/arm/mach-exynos/mach-exynos5-dt.c index e99d3d8..ea9e302 100644 --- a/arch/arm/mach-exynos/mach-exynos5-dt.c +++ b/arch/arm/mach-exynos/mach-exynos5-dt.c @@ -104,6 +104,12 @@ static const struct of_dev_auxdata exynos5250_auxdata_lookup[] __initconst = { OF_DEV_AUXDATA("samsung,mfc-v6", 0x11000000, "s5p-mfc-v6", NULL), OF_DEV_AUXDATA("samsung,exynos5250-tmu", 0x10060000, "exynos-tmu", NULL), + OF_DEV_AUXDATA("samsung,i2s-v5", 0x03830000, + "samsung-i2s.0", NULL), + OF_DEV_AUXDATA("samsung,i2s-v5", 0x12D60000, + "samsung-i2s.1", NULL), + OF_DEV_AUXDATA("samsung,i2s-v5", 0x12D70000, + "samsung-i2s.2", NULL), {}, }; -- cgit v0.10.2 From 28a480583361b8e67b0a7f4898180725b71cceec Mon Sep 17 00:00:00 2001 From: Padmavathi Venna Date: Fri, 18 Jan 2013 17:17:06 +0530 Subject: ASoC: SMDK: WM8994: Add device tree support for machine file Add the basic device tree based lookup. Signed-off-by: Padmavathi Venna Signed-off-by: Mark Brown diff --git a/Documentation/devicetree/bindings/sound/samsung,smdk-wm8994.txt b/Documentation/devicetree/bindings/sound/samsung,smdk-wm8994.txt new file mode 100644 index 0000000..4686646f --- /dev/null +++ b/Documentation/devicetree/bindings/sound/samsung,smdk-wm8994.txt @@ -0,0 +1,14 @@ +Samsung SMDK audio complex + +Required properties: +- compatible : "samsung,smdk-wm8994" +- samsung,i2s-controller: The phandle of the Samsung I2S0 controller +- samsung,audio-codec: The phandle of the WM8994 audio codec +Example: + +sound { + compatible = "samsung,smdk-wm8994"; + + samsung,i2s-controller = <&i2s0>; + samsung,audio-codec = <&wm8994>; +}; diff --git a/arch/arm/boot/dts/exynos5250-smdk5250.dts b/arch/arm/boot/dts/exynos5250-smdk5250.dts index 78fee35..127b8cd 100644 --- a/arch/arm/boot/dts/exynos5250-smdk5250.dts +++ b/arch/arm/boot/dts/exynos5250-smdk5250.dts @@ -49,6 +49,11 @@ compatible = "samsung,s524ad0xd1"; reg = <0x51>; }; + + wm8994: wm8994@1a { + compatible = "wlf,wm8994"; + reg = <0x1a>; + }; }; i2c@121D0000 { @@ -205,17 +210,24 @@ samsung,mfc-l = <0x51000000 0x800000>; }; - i2s@03830000 { + i2s0: i2s@03830000 { gpios = <&gpz 0 2 0 0>, <&gpz 1 2 0 0>, <&gpz 2 2 0 0>, <&gpz 3 2 0 0>, <&gpz 4 2 0 0>, <&gpz 5 2 0 0>, <&gpz 6 2 0 0>; }; - i2s@12D60000 { + i2s1: i2s@12D60000 { status = "disabled"; }; - i2s@12D70000 { + i2s2: i2s@12D70000 { status = "disabled"; }; + + sound { + compatible = "samsung,smdk-wm8994"; + + samsung,i2s-controller = <&i2s0>; + samsung,audio-codec = <&wm8994>; + }; }; diff --git a/arch/arm/boot/dts/exynos5250.dtsi b/arch/arm/boot/dts/exynos5250.dtsi index fe05b60..a320b4a 100644 --- a/arch/arm/boot/dts/exynos5250.dtsi +++ b/arch/arm/boot/dts/exynos5250.dtsi @@ -269,7 +269,7 @@ #size-cells = <0>; }; - i2s@03830000 { + i2s0: i2s@03830000 { compatible = "samsung,i2s-v5"; reg = <0x03830000 0x100>; dmas = <&pdma0 10 @@ -282,7 +282,7 @@ samsung,idma-addr = <0x03000000>; }; - i2s@12D60000 { + i2s1: i2s@12D60000 { compatible = "samsung,i2s-v5"; reg = <0x12D60000 0x100>; dmas = <&pdma1 12 @@ -290,7 +290,7 @@ dma-names = "tx", "rx"; }; - i2s@12D70000 { + i2s2: i2s@12D70000 { compatible = "samsung,i2s-v5"; reg = <0x12D70000 0x100>; dmas = <&pdma0 12 diff --git a/sound/soc/samsung/smdk_wm8994.c b/sound/soc/samsung/smdk_wm8994.c index cc2f407..581ea4a 100644 --- a/sound/soc/samsung/smdk_wm8994.c +++ b/sound/soc/samsung/smdk_wm8994.c @@ -10,6 +10,7 @@ #include "../codecs/wm8994.h" #include #include +#include /* * Default CFG switch settings to use this driver: @@ -153,9 +154,25 @@ static struct snd_soc_card smdk = { static int smdk_audio_probe(struct platform_device *pdev) { int ret; + struct device_node *np = pdev->dev.of_node; struct snd_soc_card *card = &smdk; card->dev = &pdev->dev; + + if (np) { + smdk_dai[0].cpu_dai_name = NULL; + smdk_dai[0].cpu_of_node = of_parse_phandle(np, + "samsung,i2s-controller", 0); + if (!smdk_dai[0].cpu_of_node) { + dev_err(&pdev->dev, + "Property 'samsung,i2s-controller' missing or invalid\n"); + ret = -EINVAL; + } + + smdk_dai[0].platform_name = NULL; + smdk_dai[0].platform_of_node = smdk_dai[0].cpu_of_node; + } + ret = snd_soc_register_card(card); if (ret) @@ -173,10 +190,19 @@ static int smdk_audio_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_OF +static const struct of_device_id samsung_wm8994_of_match[] = { + { .compatible = "samsung,smdk-wm8994", }, + {}, +}; +MODULE_DEVICE_TABLE(of, samsung_wm8994_of_match); +#endif /* CONFIG_OF */ + static struct platform_driver smdk_audio_driver = { .driver = { .name = "smdk-audio", .owner = THIS_MODULE, + .of_match_table = of_match_ptr(samsung_wm8994_of_match), }, .probe = smdk_audio_probe, .remove = smdk_audio_remove, -- cgit v0.10.2 From a4a8a9d3aee357d8f34060c2fee711d2a9df3709 Mon Sep 17 00:00:00 2001 From: Padmavathi Venna Date: Fri, 18 Jan 2013 17:17:07 +0530 Subject: ARM: dts: Modify SPI nodes according generic DMA DT bindings This patch removes custom way of adding spi dma channels and adds according to new generic DMA DT bindings. Signed-off-by: Padmavathi Venna Signed-off-by: Mark Brown diff --git a/arch/arm/boot/dts/exynos5250.dtsi b/arch/arm/boot/dts/exynos5250.dtsi index a320b4a..f50b4e8 100644 --- a/arch/arm/boot/dts/exynos5250.dtsi +++ b/arch/arm/boot/dts/exynos5250.dtsi @@ -211,8 +211,9 @@ compatible = "samsung,exynos4210-spi"; reg = <0x12d20000 0x100>; interrupts = <0 66 0>; - tx-dma-channel = <&pdma0 5>; /* preliminary */ - rx-dma-channel = <&pdma0 4>; /* preliminary */ + dmas = <&pdma0 5 + &pdma0 4>; + dma-names = "tx", "rx"; #address-cells = <1>; #size-cells = <0>; }; @@ -221,8 +222,9 @@ compatible = "samsung,exynos4210-spi"; reg = <0x12d30000 0x100>; interrupts = <0 67 0>; - tx-dma-channel = <&pdma1 5>; /* preliminary */ - rx-dma-channel = <&pdma1 4>; /* preliminary */ + dmas = <&pdma1 5 + &pdma1 4>; + dma-names = "tx", "rx"; #address-cells = <1>; #size-cells = <0>; }; @@ -231,8 +233,9 @@ compatible = "samsung,exynos4210-spi"; reg = <0x12d40000 0x100>; interrupts = <0 68 0>; - tx-dma-channel = <&pdma0 7>; /* preliminary */ - rx-dma-channel = <&pdma0 6>; /* preliminary */ + dmas = <&pdma0 7 + &pdma0 6>; + dma-names = "tx", "rx"; #address-cells = <1>; #size-cells = <0>; }; -- cgit v0.10.2 From 313367e7bfa9de924245ad65c83c32c073b2fdfc Mon Sep 17 00:00:00 2001 From: Padmavathi Venna Date: Fri, 18 Jan 2013 17:17:08 +0530 Subject: ASoC: Samsung: Update Kconfig for I2S,SPDIF and PCM audio Update Kconfig file to enable I2S,PCM audio for wm8994 and spdif on all samsung platforms. Signed-off-by: Sangsu Park Signed-off-by: Sangbeom Kim Signed-off-by: Padmavathi Venna Signed-off-by: Mark Brown diff --git a/sound/soc/samsung/Kconfig b/sound/soc/samsung/Kconfig index 3c7c3a5..90e7e66 100644 --- a/sound/soc/samsung/Kconfig +++ b/sound/soc/samsung/Kconfig @@ -63,7 +63,7 @@ config SND_SOC_SAMSUNG_SMDK_WM8580 config SND_SOC_SAMSUNG_SMDK_WM8994 tristate "SoC I2S Audio support for WM8994 on SMDK" - depends on SND_SOC_SAMSUNG && (MACH_SMDKV310 || MACH_SMDKC210 || MACH_SMDK4212) + depends on SND_SOC_SAMSUNG depends on I2C=y && GENERIC_HARDIRQS select MFD_WM8994 select SND_SOC_WM8994 @@ -162,7 +162,7 @@ config SND_SOC_GONI_AQUILA_WM8994 config SND_SOC_SAMSUNG_SMDK_SPDIF tristate "SoC S/PDIF Audio support for SMDK" - depends on SND_SOC_SAMSUNG && (MACH_SMDKC100 || MACH_SMDKC110 || MACH_SMDKV210 || MACH_SMDKV310 || MACH_SMDK4212) + depends on SND_SOC_SAMSUNG select SND_SAMSUNG_SPDIF help Say Y if you want to add support for SoC S/PDIF audio on the SMDK. @@ -177,7 +177,7 @@ config SND_SOC_SMDK_WM8580_PCM config SND_SOC_SMDK_WM8994_PCM tristate "SoC PCM Audio support for WM8994 on SMDK" - depends on SND_SOC_SAMSUNG && (MACH_SMDKC210 || MACH_SMDKV310 || MACH_SMDK4212) + depends on SND_SOC_SAMSUNG depends on I2C=y && GENERIC_HARDIRQS select MFD_WM8994 select SND_SOC_WM8994 -- cgit v0.10.2 From 7b2ee291fbd3dbe8079c67fec6382a8ed6c275f7 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 29 Jan 2013 09:18:55 +0100 Subject: ALSA: hda - Update documentation Signed-off-by: Takashi Iwai diff --git a/Documentation/sound/alsa/HD-Audio.txt b/Documentation/sound/alsa/HD-Audio.txt index 7813c06..d4faa63 100644 --- a/Documentation/sound/alsa/HD-Audio.txt +++ b/Documentation/sound/alsa/HD-Audio.txt @@ -176,14 +176,14 @@ support the automatic probing (yet as of 2.6.28). And, BIOS is often, yes, pretty often broken. It sets up wrong values and screws up the driver. -The preset model is provided basically to overcome such a situation. -When the matching preset model is found in the white-list, the driver -assumes the static configuration of that preset and builds the mixer -elements and PCM streams based on the static information. Thus, if -you have a newer machine with a slightly different PCI SSID from the -existing one, you may have a good chance to re-use the same model. -You can pass the `model` option to specify the preset model instead of -PCI SSID look-up. +The preset model (or recently called as "fix-up") is provided +basically to overcome such a situation. When the matching preset +model is found in the white-list, the driver assumes the static +configuration of that preset with the correct pin setup, etc. +Thus, if you have a newer machine with a slightly different PCI SSID +(or codec SSID) from the existing one, you may have a good chance to +re-use the same model. You can pass the `model` option to specify the +preset model instead of PCI (and codec-) SSID look-up. What `model` option values are available depends on the codec chip. Check your codec chip from the codec proc file (see "Codec Proc-File" @@ -199,17 +199,12 @@ non-working HD-audio hardware is to check HD-audio codec and several different `model` option values. If you have any luck, some of them might suit with your device well. -Some codecs such as ALC880 have a special model option `model=test`. -This configures the driver to provide as many mixer controls as -possible for every single pin feature except for the unsolicited -events (and maybe some other specials). Adjust each mixer element and -try the I/O in the way of trial-and-error until figuring out the whole -I/O pin mappings. +There are a few special model option values: +- when 'nofixup' is passed, the device-specific fixups in the codec + parser are skipped. +- when `generic` is passed, the codec-specific parser is skipped and + only the generic parser is used. -Note that `model=generic` has a special meaning. It means to use the -generic parser regardless of the codec. Usually the codec-specific -parser is much better than the generic parser (as now). Thus this -option is more about the debugging purpose. Speaker and Headphone Output ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -387,9 +382,8 @@ init_verbs:: (separated with a space). hints:: Shows / stores hint strings for codec parsers for any use. - Its format is `key = value`. For example, passing `hp_detect = yes` - to IDT/STAC codec parser will result in the disablement of the - headphone detection. + Its format is `key = value`. For example, passing `jack_detect = no` + will disable the jack detection of the machine completely. init_pin_configs:: Shows the initial pin default config values set by BIOS. driver_pin_configs:: @@ -421,6 +415,61 @@ re-configure based on that state, run like below: ------------------------------------------------------------------------ +Hint Strings +~~~~~~~~~~~~ +The codec parser have several switches and adjustment knobs for +matching better with the actual codec or device behavior. Many of +them can be adjusted dynamically via "hints" strings as mentioned in +the section above. For example, by passing `jack_detect = no` string +via sysfs or a patch file, you can disable the jack detection, thus +the codec parser will skip the features like auto-mute or mic +auto-switch. As a boolean value, either `yes`, `no`, `true`, `false`, +`1` or `0` can be passed. + +The generic parser supports the following hints: + +- jack_detect (bool): specify whether the jack detection is available + at all on this machine; default true +- inv_jack_detect (bool): indicates that the jack detection logic is + inverted +- trigger_sense (bool): indicates that the jack detection needs the + explicit call of AC_VERB_SET_PIN_SENSE verb +- inv_eapd (bool): indicates that the EAPD is implemented in the + inverted logic +- pcm_format_first (bool): sets the PCM format before the stream tag + and channel ID +- sticky_stream (bool): keep the PCM format, stream tag and ID as long + as possible; default true +- spdif_status_reset (bool): reset the SPDIF status bits at each time + the SPDIF stream is set up +- pin_amp_workaround (bool): the output pin may have multiple amp + values +- single_adc_amp (bool): ADCs can have only single input amps +- auto_mute (bool): enable/disable the headphone auto-mute feature; + default true +- auto_mic (bool): enable/disable the mic auto-switch feature; default + true +- line_in_auto_switch (bool): enable/disable the line-in auto-switch + feature; default false +- need_dac_fix (bool): limits the DACs depending on the channel count +- primary_hp (bool): probe headphone jacks as the primary outputs; + default true +- multi_cap_vol (bool): provide multiple capture volumes +- inv_dmic_split (bool): provide split internal mic volume/switch for + phase-inverted digital mics +- indep_hp (bool): provide the independent headphone PCM stream and + the corresponding mixer control, if available +- add_stereo_mix_input (bool): add the stereo mix (analog-loopback + mix) to the input mux if available +- add_out_jack_modes (bool): add "xxx Jack Mode" enum controls to each + output jack for allowing to change the headphone amp capability +- add_in_jack_modes (bool): add "xxx Jack Mode" enum controls to each + input jack for allowing to change the mic bias vref +- power_down_unused (bool): power down the unused widgets +- mixer_nid (int): specifies the widget NID of the analog-loopback + mixer + + Early Patching ~~~~~~~~~~~~~~ When CONFIG_SND_HDA_PATCH_LOADER=y is set, you can pass a "patch" as a @@ -445,7 +494,7 @@ A patch file is a plain text file which looks like below: 0x20 0x400 0xff [hint] - hp_detect = yes + jack_detect = no ------------------------------------------------------------------------ The file needs to have a line `[codec]`. The next line should contain @@ -531,6 +580,13 @@ cable is unplugged. Thus, if you hear noises, suspect first the power-saving. See /sys/module/snd_hda_intel/parameters/power_save to check the current value. If it's non-zero, the feature is turned on. +The recent kernel supports the runtime PM for the HD-audio controller +chip, too. It means that the HD-audio controller is also powered up / +down dynamically. The feature is enabled only for certain controller +chips like Intel LynxPoint. You can enable/disable this feature +forcibly by setting `power_save_controller` option, which is also +available at /sys/module/snd_hda_intel/parameters directory. + Tracepoints ~~~~~~~~~~~ @@ -587,8 +643,9 @@ The latest development codes for HD-audio are found on sound git tree: - git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound.git The master branch or for-next branches can be used as the main -development branches in general while the HD-audio specific patches -are committed in topic/hda branch. +development branches in general while the development for the current +and next kernels are found in for-linus and for-next branches, +respectively. If you are using the latest Linus tree, it'd be better to pull the above GIT tree onto it. If you are using the older kernels, an easy @@ -699,7 +756,11 @@ won't be always updated. For example, the volume values are usually cached in the driver, and thus changing the widget amp value directly via hda-verb won't change the mixer value. -The hda-verb program is found in the ftp directory: +The hda-verb program is included now in alsa-tools: + +- git://git.alsa-project.org/alsa-tools.git + +Also, the old stand-alone package is found in the ftp directory: - ftp://ftp.suse.com/pub/people/tiwai/misc/ @@ -777,3 +838,18 @@ A git repository is available: See README file in the tarball for more details about hda-emu program. + + +hda-jack-retask +~~~~~~~~~~~~~~~ +hda-jack-retask is a user-friendly GUI program to manipulate the +HD-audio pin control for jack retasking. If you have a problem about +the jack assignment, try this program and check whether you can get +useful results. Once when you figure out the proper pin assignment, +it can be fixed either in the driver code statically or via passing a +firmware patch file (see "Early Patching" section). + +The program is included in alsa-tools now: + +- git://git.alsa-project.org/alsa-tools.git + -- cgit v0.10.2 From 019d80db5727707faa2108fcd4fbbfac9defb3a6 Mon Sep 17 00:00:00 2001 From: Antonio Ospite Date: Tue, 29 Jan 2013 12:56:26 +0100 Subject: ALSA: Force a cast to silence a warning from "sparse" Some audio drivers are calling snd_dma_continuous_data(GFP_KERNEL) which makes "sparse" give a warning: $ make C=2 M=sound/usb modules ... sound/usb/6fire/pcm.c:625:25: warning: cast from restricted gfp_t sound/usb/caiaq/audio.c:845:41: warning: cast from restricted gfp_t sound/usb/usx2y/usbusx2yaudio.c:997:54: warning: cast from restricted gfp_t sound/usb/usx2y/usbusx2yaudio.c:1001:54: warning: cast from restricted gfp_t sound/usb/usx2y/usx2yhwdeppcm.c:774:54: warning: cast from restricted gfp_t sound/usb/usx2y/usx2yhwdeppcm.c:778:54: warning: cast from restricted gfp_t Add __force to the cast to silence the warning. Signed-off-by: Antonio Ospite Signed-off-by: Takashi Iwai diff --git a/include/sound/memalloc.h b/include/sound/memalloc.h index 844af65..cf15b82 100644 --- a/include/sound/memalloc.h +++ b/include/sound/memalloc.h @@ -37,7 +37,7 @@ struct snd_dma_device { #ifndef snd_dma_pci_data #define snd_dma_pci_data(pci) (&(pci)->dev) #define snd_dma_isa_data() NULL -#define snd_dma_continuous_data(x) ((struct device *)(unsigned long)(x)) +#define snd_dma_continuous_data(x) ((struct device *)(__force unsigned long)(x)) #endif -- cgit v0.10.2 From 04044b819b21826f11f32e11aba54def635d8457 Mon Sep 17 00:00:00 2001 From: Antonio Ospite Date: Tue, 29 Jan 2013 12:56:27 +0100 Subject: ALSA: Documentation: fix some thinkos Signed-off-by: Antonio Ospite Signed-off-by: Takashi Iwai diff --git a/Documentation/DocBook/writing-an-alsa-driver.tmpl b/Documentation/DocBook/writing-an-alsa-driver.tmpl index fb32aea..da2f443 100644 --- a/Documentation/DocBook/writing-an-alsa-driver.tmpl +++ b/Documentation/DocBook/writing-an-alsa-driver.tmpl @@ -871,9 +871,8 @@ This function itself doesn't allocate the data space. The data must be allocated manually beforehand, and its pointer is passed - as the argument. This pointer is used as the - (chip identifier in the above example) - for the instance. + as the argument. This pointer (chip in the + above example) is used as the identifier for the instance. @@ -2304,7 +2303,7 @@ struct _snd_pcm_runtime { SNDRV_PCM_INFO_XXX. Here, at least, you have to specify whether the mmap is supported and which interleaved format is supported. - When the is supported, add the + When the hardware supports mmap, add the SNDRV_PCM_INFO_MMAP flag here. When the hardware supports the interleaved or the non-interleaved formats, SNDRV_PCM_INFO_INTERLEAVED or -- cgit v0.10.2 From d4dab5ab5bd0445420aa090185409bf3ae6ccd37 Mon Sep 17 00:00:00 2001 From: Antonio Ospite Date: Tue, 29 Jan 2013 12:56:28 +0100 Subject: ALSA: Documentation: fix some typos s/PAUSE_PUSE/PAUSE_PUSH/ s/happense/happens/ Signed-off-by: Antonio Ospite Signed-off-by: Takashi Iwai diff --git a/Documentation/DocBook/writing-an-alsa-driver.tmpl b/Documentation/DocBook/writing-an-alsa-driver.tmpl index da2f443..c0781bb 100644 --- a/Documentation/DocBook/writing-an-alsa-driver.tmpl +++ b/Documentation/DocBook/writing-an-alsa-driver.tmpl @@ -2897,7 +2897,7 @@ struct _snd_pcm_runtime { When the pcm supports the pause operation (given in the info - field of the hardware table), the PAUSE_PUSE + field of the hardware table), the PAUSE_PUSH and PAUSE_RELEASE commands must be handled here, too. The former is the command to pause the pcm, and the latter to restart the pcm again. @@ -3084,7 +3084,7 @@ struct _snd_pcm_runtime {
High frequency timer interrupts - This happense when the hardware doesn't generate interrupts + This happens when the hardware doesn't generate interrupts at the period boundary but issues timer interrupts at a fixed timer rate (e.g. es1968 or ymfpci drivers). In this case, you need to check the current hardware -- cgit v0.10.2 From febd1cc43882733d0030295246cabfe1ded924c2 Mon Sep 17 00:00:00 2001 From: Antonio Ospite Date: Tue, 29 Jan 2013 12:56:29 +0100 Subject: ALSA: caiaq: fix use of MODULE_SUPPORTED_DEVICES() It looks like MODULE_SUPPORTED_DEVICES() is not implemented yet, but still, having the entries in the list consistently separated by commas and with balanced parenthesis won't hurt. Signed-off-by: Antonio Ospite Acked-by: Daniel Mack Signed-off-by: Takashi Iwai diff --git a/sound/usb/caiaq/device.c b/sound/usb/caiaq/device.c index c828f81..e4d6dbb 100644 --- a/sound/usb/caiaq/device.c +++ b/sound/usb/caiaq/device.c @@ -48,10 +48,10 @@ MODULE_SUPPORTED_DEVICE("{{Native Instruments, RigKontrol2}," "{Native Instruments, Audio 8 DJ}," "{Native Instruments, Traktor Audio 2}," "{Native Instruments, Session I/O}," - "{Native Instruments, GuitarRig mobile}" - "{Native Instruments, Traktor Kontrol X1}" - "{Native Instruments, Traktor Kontrol S4}" - "{Native Instruments, Maschine Controller}"); + "{Native Instruments, GuitarRig mobile}," + "{Native Instruments, Traktor Kontrol X1}," + "{Native Instruments, Traktor Kontrol S4}," + "{Native Instruments, Maschine Controller}}"); static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-max */ static char* id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* Id for this card */ -- cgit v0.10.2 From aa53f98674a5c21a3098018be2c8ac0984273d8f Mon Sep 17 00:00:00 2001 From: Antonio Ospite Date: Tue, 29 Jan 2013 12:56:30 +0100 Subject: ALSA: usb: cosmetics, remove a leading space Signed-off-by: Antonio Ospite Signed-off-by: Takashi Iwai diff --git a/sound/usb/card.c b/sound/usb/card.c index ccf95cf..803953a 100644 --- a/sound/usb/card.c +++ b/sound/usb/card.c @@ -646,7 +646,7 @@ static int usb_audio_suspend(struct usb_interface *intf, pm_message_t message) as->substream[0].need_setup_ep = as->substream[1].need_setup_ep = true; } - } + } } else { /* * otherwise we keep the rest of the system in the dark -- cgit v0.10.2 From 06378da45d58fc3703ce5243c7b278aa8cfadb61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Th=C3=A9baudeau?= Date: Tue, 29 Jan 2013 21:31:48 +0100 Subject: ASoC: tlv320aic3x: Remove mono support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Playing a mono stream on the TLV320AIC3x results in too fast playback rate. Remove mono support so that mono streams can be played correctly on this codec. Tested with imx-ssi (i.MX25) and TLV320AIC3104. Signed-off-by: Benoît Thébaudeau Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c index 5708a97..4989143 100644 --- a/sound/soc/codecs/tlv320aic3x.c +++ b/sound/soc/codecs/tlv320aic3x.c @@ -1210,13 +1210,13 @@ static struct snd_soc_dai_driver aic3x_dai = { .name = "tlv320aic3x-hifi", .playback = { .stream_name = "Playback", - .channels_min = 1, + .channels_min = 2, .channels_max = 2, .rates = AIC3X_RATES, .formats = AIC3X_FORMATS,}, .capture = { .stream_name = "Capture", - .channels_min = 1, + .channels_min = 2, .channels_max = 2, .rates = AIC3X_RATES, .formats = AIC3X_FORMATS,}, -- cgit v0.10.2 From eef28e10821fb671ba797a41e7cf44e3d244e32e Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 29 Jan 2013 21:03:13 -0800 Subject: ASoC: SND_SOC_DAIFMT_GATED become 0 as default settings Current soc-dai.h defines SND_SOC_DAIFMT_GATED as (2 << 4), but gated clock should be default settings (= 0). This patch fixup SND_SOC_DAIFMT_GATED as (0 << 4). Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown diff --git a/include/sound/soc-dai.h b/include/sound/soc-dai.h index 3953cea..4dbd3e7 100644 --- a/include/sound/soc-dai.h +++ b/include/sound/soc-dai.h @@ -45,7 +45,7 @@ struct snd_compr_stream; * sending or receiving PCM data in a frame. This can be used to save power. */ #define SND_SOC_DAIFMT_CONT (1 << 4) /* continuous clock */ -#define SND_SOC_DAIFMT_GATED (2 << 4) /* clock is gated */ +#define SND_SOC_DAIFMT_GATED (0 << 4) /* clock is gated */ /* * DAI hardware signal inversions. -- cgit v0.10.2 From 8c2d6a9f9cfa59acfa63ee88e70d58f0ba3eaf21 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 29 Jan 2013 21:03:36 -0800 Subject: ASoC: clock gating is decided by bool on snd_soc_of_parse_daifmt() ASoC clock gate settings are continuous/gated only. This patch decides it as bool, then, gated clock will be default. Special thanks to Stephen Cc: Stephen Warren Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 9d07dc0..b3d75d2 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -4229,9 +4229,6 @@ unsigned int snd_soc_of_parse_daifmt(struct device_node *np, { "pdm", SND_SOC_DAIFMT_PDM}, { "msb", SND_SOC_DAIFMT_MSB }, { "lsb", SND_SOC_DAIFMT_LSB }, - }, of_clock_table[] = { - { "continuous", SND_SOC_DAIFMT_CONT }, - { "gated", SND_SOC_DAIFMT_GATED }, }; if (!prefix) @@ -4253,19 +4250,14 @@ unsigned int snd_soc_of_parse_daifmt(struct device_node *np, } /* - * check "[prefix]clock-gating = xxx" + * check "[prefix]continuous-clock" * SND_SOC_DAIFMT_CLOCK_MASK area */ - snprintf(prop, sizeof(prop), "%sclock-gating", prefix); - ret = of_property_read_string(np, prop, &str); - if (ret == 0) { - for (i = 0; i < ARRAY_SIZE(of_clock_table); i++) { - if (strcmp(str, of_clock_table[i].name) == 0) { - format |= of_clock_table[i].val; - break; - } - } - } + snprintf(prop, sizeof(prop), "%scontinuous-clock", prefix); + if (of_get_property(np, prop, NULL)) + format |= SND_SOC_DAIFMT_CONT; + else + format |= SND_SOC_DAIFMT_GATED; /* * check "[prefix]bitclock-inversion" -- cgit v0.10.2 From 31522764c6b57e41b79220156efc5d208f2f841a Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 30 Jan 2013 20:11:01 +0800 Subject: ASoC: wm_adsp: Fix support for firmware switching on DSP2 and higher Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index f3a9b55..5487a94 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -196,7 +196,7 @@ static int wm_adsp_fw_put(struct snd_kcontrol *kcontrol, if (adsp[e->shift_l].running) return -EBUSY; - adsp->fw = ucontrol->value.integer.value[0]; + adsp[e->shift_l].fw = ucontrol->value.integer.value[0]; return 0; } -- cgit v0.10.2 From 8e9bb423a2f70eb5f15ba5cc4423b0b6f75133e2 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 23 Jan 2013 18:38:54 +0800 Subject: ASoC: wm2000: Lock state machine updates Need to ensure we don't get confused by simultaneous updates. Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm2000.c b/sound/soc/codecs/wm2000.c index 12bcae6..eb96b87 100644 --- a/sound/soc/codecs/wm2000.c +++ b/sound/soc/codecs/wm2000.c @@ -76,6 +76,8 @@ struct wm2000_priv { int anc_download_size; char *anc_download; + + struct mutex lock; }; static int wm2000_write(struct i2c_client *i2c, unsigned int reg, @@ -599,13 +601,20 @@ static int wm2000_anc_mode_put(struct snd_kcontrol *kcontrol, struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); struct wm2000_priv *wm2000 = dev_get_drvdata(codec->dev); int anc_active = ucontrol->value.enumerated.item[0]; + int ret; if (anc_active > 1) return -EINVAL; + mutex_lock(&wm2000->lock); + wm2000->anc_active = anc_active; - return wm2000_anc_set_mode(wm2000); + ret = wm2000_anc_set_mode(wm2000); + + mutex_unlock(&wm2000->lock); + + return ret; } static int wm2000_speaker_get(struct snd_kcontrol *kcontrol, @@ -625,13 +634,20 @@ static int wm2000_speaker_put(struct snd_kcontrol *kcontrol, struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); struct wm2000_priv *wm2000 = dev_get_drvdata(codec->dev); int val = ucontrol->value.enumerated.item[0]; + int ret; if (val > 1) return -EINVAL; + mutex_lock(&wm2000->lock); + wm2000->spk_ena = val; - return wm2000_anc_set_mode(wm2000); + ret = wm2000_anc_set_mode(wm2000); + + mutex_unlock(&wm2000->lock); + + return ret; } static const struct snd_kcontrol_new wm2000_controls[] = { @@ -648,6 +664,9 @@ static int wm2000_anc_power_event(struct snd_soc_dapm_widget *w, { struct snd_soc_codec *codec = w->codec; struct wm2000_priv *wm2000 = dev_get_drvdata(codec->dev); + int ret; + + mutex_lock(&wm2000->lock); if (SND_SOC_DAPM_EVENT_ON(event)) wm2000->anc_eng_ena = 1; @@ -655,7 +674,11 @@ static int wm2000_anc_power_event(struct snd_soc_dapm_widget *w, if (SND_SOC_DAPM_EVENT_OFF(event)) wm2000->anc_eng_ena = 0; - return wm2000_anc_set_mode(wm2000); + ret = wm2000_anc_set_mode(wm2000); + + mutex_unlock(&wm2000->lock); + + return ret; } static const struct snd_soc_dapm_widget wm2000_dapm_widgets[] = { @@ -782,6 +805,8 @@ static int wm2000_i2c_probe(struct i2c_client *i2c, return -ENOMEM; } + mutex_init(&wm2000->lock); + dev_set_drvdata(&i2c->dev, wm2000); wm2000->regmap = devm_regmap_init_i2c(i2c, &wm2000_regmap); -- cgit v0.10.2 From 3f3af6eeff3e048e27eb864519e779aedc194386 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 30 Jan 2013 21:35:44 +0800 Subject: ASoC: wm2000: Expose ANC gain adjustment No TLV information since it's not actually a direct gain control. Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm2000.c b/sound/soc/codecs/wm2000.c index 627c454..cb7fa3c 100644 --- a/sound/soc/codecs/wm2000.c +++ b/sound/soc/codecs/wm2000.c @@ -650,6 +650,7 @@ static int wm2000_speaker_put(struct snd_kcontrol *kcontrol, } static const struct snd_kcontrol_new wm2000_controls[] = { + SOC_SINGLE("ANC Volume", WM2000_REG_ANC_GAIN_CTRL, 0, 255, 0), SOC_SINGLE_BOOL_EXT("WM2000 ANC Switch", 0, wm2000_anc_mode_get, wm2000_anc_mode_put), @@ -755,6 +756,8 @@ static int wm2000_probe(struct snd_soc_codec *codec) { struct wm2000_priv *wm2000 = dev_get_drvdata(codec->dev); + snd_soc_codec_set_cache_io(codec, 16, 8, SND_SOC_REGMAP); + /* This will trigger a transition to standby mode by default */ wm2000_anc_set_mode(wm2000); -- cgit v0.10.2 From 58f5a7151efe2a3039b097883e6ba9fb24e42536 Mon Sep 17 00:00:00 2001 From: Alexey Galakhov Date: Wed, 30 Jan 2013 15:35:22 +0600 Subject: ASoC: samsung: Fix compilation error on S3C2440 The commit a08485d8 ("ASoC: Samsung: Do not register samsung audio dma device as pdev") introduced compilation error. Combination of CONFIG_CPU_S3C2440 and CONFIG_SND_S3C24XX_I2S caused undefined symbols 's3c_i2sv2_register_dai' and 's3c2412_i2s_dai' in sound/soc/samsung/s3c24xx-i2s.c. This patch fixes the problem and makes S3C2440 I2S usable again. It does not affect S3C2412 (aka I2S-v2). Signed-off-by: Alexey Galakhov Signed-off-by: Mark Brown diff --git a/sound/soc/samsung/s3c24xx-i2s.c b/sound/soc/samsung/s3c24xx-i2s.c index ee10e87..13f6dd1 100644 --- a/sound/soc/samsung/s3c24xx-i2s.c +++ b/sound/soc/samsung/s3c24xx-i2s.c @@ -469,7 +469,7 @@ static int s3c24xx_iis_dev_probe(struct platform_device *pdev) { int ret = 0; - ret = s3c_i2sv2_register_dai(&pdev->dev, -1, &s3c2412_i2s_dai); + ret = snd_soc_register_dai(&pdev->dev, &s3c24xx_i2s_dai); if (ret) { pr_err("failed to register the dai\n"); return ret; -- cgit v0.10.2 From 5b1d3c3472f1941ab1a78575fe9ada718a7c0c25 Mon Sep 17 00:00:00 2001 From: "R. Chandrasekar" Date: Wed, 30 Jan 2013 17:41:04 +0530 Subject: ASoC: Samsung: I2S: Add support for runtime S2R This patch adds runtime suspend to resume support for I2S. I2S clk is disabled at suspend and enabled at resume. Signed-off-by: R. Chandrasekar Signed-off-by: Padmavathi Venna Signed-off-by: Mark Brown diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c index 2fc42f9..d7231e3 100644 --- a/sound/soc/samsung/i2s.c +++ b/sound/soc/samsung/i2s.c @@ -1071,6 +1071,26 @@ static inline int samsung_i2s_get_driver_data(struct platform_device *pdev) return platform_get_device_id(pdev)->driver_data; } +#ifdef CONFIG_PM_RUNTIME +static int i2s_runtime_suspend(struct device *dev) +{ + struct i2s_dai *i2s = dev_get_drvdata(dev); + + clk_disable_unprepare(i2s->clk); + + return 0; +} + +static int i2s_runtime_resume(struct device *dev) +{ + struct i2s_dai *i2s = dev_get_drvdata(dev); + + clk_prepare_enable(i2s->clk); + + return 0; +} +#endif /* CONFIG_PM_RUNTIME */ + static int samsung_i2s_probe(struct platform_device *pdev) { struct i2s_dai *pri_dai, *sec_dai = NULL; @@ -1288,6 +1308,11 @@ static const struct of_device_id exynos_i2s_match[] = { MODULE_DEVICE_TABLE(of, exynos_i2s_match); #endif +static const struct dev_pm_ops samsung_i2s_pm = { + SET_RUNTIME_PM_OPS(i2s_runtime_suspend, + i2s_runtime_resume, NULL) +}; + static struct platform_driver samsung_i2s_driver = { .probe = samsung_i2s_probe, .remove = samsung_i2s_remove, @@ -1296,6 +1321,7 @@ static struct platform_driver samsung_i2s_driver = { .name = "samsung-i2s", .owner = THIS_MODULE, .of_match_table = of_match_ptr(exynos_i2s_match), + .pm = &samsung_i2s_pm, }, }; -- cgit v0.10.2 From 9fc4cd828107d0196b1ac0f5dec1f3b747542470 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Thu, 31 Jan 2013 12:34:00 +0000 Subject: ASoC: Ux500: Fix build error pertaining to missing include file Some compilers complain when building sound for MOP500: sound/soc/ux500/mop500.c:27:27: fatal error: mop500_ab8500.h: No such file or directory That file is actually in the local directory rather than include. Signed-off-by: Lee Jones Signed-off-by: Mark Brown diff --git a/sound/soc/ux500/mop500.c b/sound/soc/ux500/mop500.c index ae69907..204b899 100644 --- a/sound/soc/ux500/mop500.c +++ b/sound/soc/ux500/mop500.c @@ -24,7 +24,7 @@ #include "ux500_pcm.h" #include "ux500_msp_dai.h" -#include +#include "mop500_ab8500.h" /* Define the whole MOP500 soundcard, linking platform to the codec-drivers */ struct snd_soc_dai_link mop500_dai_links[] = { -- cgit v0.10.2 From 7da58046482fceb17c4a0d4afefd9507ec56de7f Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Thu, 31 Jan 2013 21:14:33 +0100 Subject: ALSA: usb-audio: fix Roland A-PRO support The quirk for the Roland/Cakewalk A-PRO keyboards accidentally used the wrong interface number, which prevented the driver from attaching to the device. Signed-off-by: Clemens Ladisch Cc: 2.6.37+ diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h index 64d25a7..820580a 100644 --- a/sound/usb/quirks-table.h +++ b/sound/usb/quirks-table.h @@ -1750,7 +1750,7 @@ YAMAHA_DEVICE(0x7010, "UB99"), .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { /* .vendor_name = "Roland", */ /* .product_name = "A-PRO", */ - .ifnum = 1, + .ifnum = 0, .type = QUIRK_MIDI_FIXED_ENDPOINT, .data = & (const struct snd_usb_midi_endpoint_info) { .out_cables = 0x0003, -- cgit v0.10.2 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 diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 951fc0d..617ac1f 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index a85b3aa..3d8df7c 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 v0.10.2 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 diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 807a2aa..1b3b4ee 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 v0.10.2 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 diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 807a2aa..e85959f 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 v0.10.2 From edac894389f9c9de2a1368c78809c824b343f3a5 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 4 Feb 2013 10:28:15 +0100 Subject: ALSA: aloop: Fix Oops while PM resume snd-aloop driver has no proper PM implementation, thus the PM resume may trigger Oops due to leftover timer instance. This patch adds the missing suspend/resume implementation. Reported-and-tested-by: El boulangero Cc: Signed-off-by: Takashi Iwai diff --git a/sound/drivers/aloop.c b/sound/drivers/aloop.c index 3d82232..64d5347 100644 --- a/sound/drivers/aloop.c +++ b/sound/drivers/aloop.c @@ -286,12 +286,14 @@ static int loopback_trigger(struct snd_pcm_substream *substream, int cmd) loopback_active_notify(dpcm); break; case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_SUSPEND: spin_lock(&cable->lock); cable->pause |= stream; loopback_timer_stop(dpcm); spin_unlock(&cable->lock); break; case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + case SNDRV_PCM_TRIGGER_RESUME: spin_lock(&cable->lock); dpcm->last_jiffies = jiffies; cable->pause &= ~stream; @@ -563,7 +565,8 @@ static snd_pcm_uframes_t loopback_pointer(struct snd_pcm_substream *substream) static struct snd_pcm_hardware loopback_pcm_hardware = { .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_PAUSE), + SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_RESUME), .formats = (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE | SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE | SNDRV_PCM_FMTBIT_FLOAT_LE | SNDRV_PCM_FMTBIT_FLOAT_BE), -- cgit v0.10.2 From 4c97e8fedf1a7ef52e23dc06ec7a15be54ed18a4 Mon Sep 17 00:00:00 2001 From: Chris Rattray Date: Fri, 1 Feb 2013 15:40:41 +0000 Subject: ASoC: wm2200: Add Rx ANC input select control Signed-off-by: Chris Rattray Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm2200.c b/sound/soc/codecs/wm2200.c index fc05553..fee1a18 100644 --- a/sound/soc/codecs/wm2200.c +++ b/sound/soc/codecs/wm2200.c @@ -1112,6 +1112,16 @@ static int wm2200_mixer_values[] = { static WM2200_MUX_CTL_DECL(name##_aux5); \ static WM2200_MUX_CTL_DECL(name##_aux6); +static const char *wm2200_rxanc_input_sel_texts[] = { + "None", "IN1", "IN2", "IN3", +}; + +static const struct soc_enum wm2200_rxanc_input_sel = + SOC_ENUM_SINGLE(WM2200_RXANC_SRC, + WM2200_IN_RXANC_SEL_SHIFT, + ARRAY_SIZE(wm2200_rxanc_input_sel_texts), + wm2200_rxanc_input_sel_texts); + static const struct snd_kcontrol_new wm2200_snd_controls[] = { SOC_SINGLE("IN1 High Performance Switch", WM2200_IN1L_CONTROL, WM2200_IN1_OSR_SHIFT, 1, 0), @@ -1171,6 +1181,7 @@ SOC_DOUBLE_R_TLV("OUT2 Digital Volume", WM2200_DAC_DIGITAL_VOLUME_2L, digital_tlv), SOC_DOUBLE("OUT2 Switch", WM2200_PDM_1, WM2200_SPK1L_MUTE_SHIFT, WM2200_SPK1R_MUTE_SHIFT, 1, 1), +SOC_ENUM("RxANC Src", wm2200_rxanc_input_sel), }; WM2200_MIXER_ENUMS(OUT1L, WM2200_OUT1LMIX_INPUT_1_SOURCE); -- cgit v0.10.2 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 diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig index ba1dbd8..11b4b77 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 483850f..9d9040b 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 v0.10.2 From 01f58153aefc158fd690b337d29ad140e963959d Mon Sep 17 00:00:00 2001 From: Ryo Tsutsui Date: Sun, 3 Feb 2013 17:18:00 +0900 Subject: ASoC: arizona: Fixed a bug in FLL fractional calculation Previously arizona_calc_fll() was checking if the target frequency is exactly divisible by reference frequency, but should have been product of the ratio and the reference frequency. Also scale down the Lamba and Theta coefficients be under 16-bits in order to match the registers. Signed-off-by: Ryo Tsutsui Signed-off-by: Charles Keepax Signed-off-by: Mark Brown Cc: stable@vger.kernel.org diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c index ef62c43..2899cb9 100644 --- a/sound/soc/codecs/arizona.c +++ b/sound/soc/codecs/arizona.c @@ -910,7 +910,7 @@ static int arizona_calc_fll(struct arizona_fll *fll, cfg->n = target / (ratio * Fref); - if (target % Fref) { + if (target % (ratio * Fref)) { gcd_fll = gcd(target, ratio * Fref); arizona_fll_dbg(fll, "GCD=%u\n", gcd_fll); @@ -922,6 +922,15 @@ static int arizona_calc_fll(struct arizona_fll *fll, cfg->lambda = 0; } + /* Round down to 16bit range with cost of accuracy lost. + * Denominator must be bigger than numerator so we only + * take care of it. + */ + while (cfg->lambda >= (1 << 16)) { + cfg->theta >>= 1; + cfg->lambda >>= 1; + } + arizona_fll_dbg(fll, "N=%x THETA=%x LAMBDA=%x\n", cfg->n, cfg->theta, cfg->lambda); arizona_fll_dbg(fll, "FRATIO=%x(%d) OUTDIV=%x REFCLK_DIV=%x\n", -- cgit v0.10.2 From d8976cfd8257cd9539f19cd7fe512be468ed8118 Mon Sep 17 00:00:00 2001 From: Bo Shen Date: Thu, 31 Jan 2013 11:53:37 +0800 Subject: ASoC: atmel_pcm: make it buildable as module When build as module, it reports following error, using this patch fix it sound/soc/atmel/atmel-pcm-pdc.c:387: error: redefinition of 'atmel_pcm_pdc_platform_register' sound/soc/atmel/atmel-pcm.h:95: note: previous definition of 'atmel_pcm_pdc_platform_register' was here sound/soc/atmel/atmel-pcm-pdc.c:393: error: redefinition of 'atmel_pcm_pdc_platform_unregister' sound/soc/atmel/atmel-pcm.h:99: note: previous definition of 'atmel_pcm_pdc_platform_unregister' was here Signed-off-by: Bo Shen Signed-off-by: Mark Brown diff --git a/sound/soc/atmel/atmel-pcm.h b/sound/soc/atmel/atmel-pcm.h index bb45d20..12ae814 100644 --- a/sound/soc/atmel/atmel-pcm.h +++ b/sound/soc/atmel/atmel-pcm.h @@ -88,7 +88,8 @@ void atmel_pcm_free(struct snd_pcm *pcm); int atmel_pcm_mmap(struct snd_pcm_substream *substream, struct vm_area_struct *vma); -#ifdef CONFIG_SND_ATMEL_SOC_PDC +#if defined(CONFIG_SND_ATMEL_SOC_PDC) || \ + defined(CONFIG_SND_ATMEL_SOC_PDC_MODULE) int atmel_pcm_pdc_platform_register(struct device *dev); void atmel_pcm_pdc_platform_unregister(struct device *dev); #else @@ -101,7 +102,8 @@ static inline void atmel_pcm_pdc_platform_unregister(struct device *dev) } #endif -#ifdef CONFIG_SND_ATMEL_SOC_DMA +#if defined(CONFIG_SND_ATMEL_SOC_DMA) || \ + defined(CONFIG_SND_ATMEL_SOC_DMA_MODULE) int atmel_pcm_dma_platform_register(struct device *dev); void atmel_pcm_dma_platform_unregister(struct device *dev); #else -- cgit v0.10.2 From e08b273c38a7c049eefd72b9bbb2dcecb28f3956 Mon Sep 17 00:00:00 2001 From: Bo Shen Date: Thu, 31 Jan 2013 11:53:38 +0800 Subject: ASoC: atmel_ssc_dai: remove error set private data ssc private data has been set in ssc driver, this cause the error private data set to ssc, remove it Signed-off-by: Bo Shen Signed-off-by: Mark Brown diff --git a/sound/soc/atmel/atmel_ssc_dai.c b/sound/soc/atmel/atmel_ssc_dai.c index 2755750..5cb8498 100644 --- a/sound/soc/atmel/atmel_ssc_dai.c +++ b/sound/soc/atmel/atmel_ssc_dai.c @@ -677,15 +677,6 @@ static int atmel_ssc_resume(struct snd_soc_dai *cpu_dai) # define atmel_ssc_resume NULL #endif /* CONFIG_PM */ -static int atmel_ssc_probe(struct snd_soc_dai *dai) -{ - struct atmel_ssc_info *ssc_p = &ssc_info[dai->id]; - - snd_soc_dai_set_drvdata(dai, ssc_p); - - return 0; -} - #define ATMEL_SSC_RATES (SNDRV_PCM_RATE_8000_96000) #define ATMEL_SSC_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE |\ @@ -701,7 +692,6 @@ static const struct snd_soc_dai_ops atmel_ssc_dai_ops = { }; static struct snd_soc_dai_driver atmel_ssc_dai = { - .probe = atmel_ssc_probe, .suspend = atmel_ssc_suspend, .resume = atmel_ssc_resume, .playback = { -- cgit v0.10.2 From 69706028b94f10a2dc0a28af65e84ec6fd38054b Mon Sep 17 00:00:00 2001 From: Bo Shen Date: Thu, 31 Jan 2013 11:53:39 +0800 Subject: ASoC: atmel_ssc_dai: correct sequence when unload correct the sequence when unload this module Signed-off-by: Bo Shen Signed-off-by: Mark Brown diff --git a/sound/soc/atmel/atmel_ssc_dai.c b/sound/soc/atmel/atmel_ssc_dai.c index 5cb8498..e13580d 100644 --- a/sound/soc/atmel/atmel_ssc_dai.c +++ b/sound/soc/atmel/atmel_ssc_dai.c @@ -778,8 +778,8 @@ void atmel_ssc_put_audio(int ssc_id) { struct ssc_device *ssc = ssc_info[ssc_id].ssc; - ssc_free(ssc); asoc_ssc_exit(&ssc->pdev->dev); + ssc_free(ssc); } EXPORT_SYMBOL_GPL(atmel_ssc_put_audio); -- cgit v0.10.2 From 151edfc757babd94d8b6c64d84a9ccf8f3c53320 Mon Sep 17 00:00:00 2001 From: Bo Shen Date: Thu, 31 Jan 2013 11:53:40 +0800 Subject: ASoC: sam9g20_wm8731: disable clock and correct sequence when unload disable clock and correct sequence when unload Signed-off-by: Bo Shen Signed-off-by: Mark Brown diff --git a/sound/soc/atmel/sam9g20_wm8731.c b/sound/soc/atmel/sam9g20_wm8731.c index da97629..2d6fbd0 100644 --- a/sound/soc/atmel/sam9g20_wm8731.c +++ b/sound/soc/atmel/sam9g20_wm8731.c @@ -305,10 +305,10 @@ static int at91sam9g20ek_audio_remove(struct platform_device *pdev) { struct snd_soc_card *card = platform_get_drvdata(pdev); - atmel_ssc_put_audio(0); - snd_soc_unregister_card(card); - clk_put(mclk); + clk_disable(mclk); mclk = NULL; + snd_soc_unregister_card(card); + atmel_ssc_put_audio(0); return 0; } -- cgit v0.10.2 From e2e8bfdf61573c98162d1112b971d8d00f00fcf8 Mon Sep 17 00:00:00 2001 From: Hebbar Gururaja Date: Thu, 31 Jan 2013 18:23:04 +0530 Subject: ASoC: tlv320aic3x: Convert mic bias to a supply widget Convert MicBias widgets to supply widget. On tlv320aic3x, Mic bias power on/off shares the same register bits with output mic bias voltage. So, when power on mic bias, we need reclaim it to voltage value. Provide a new platform data so that the micbias voltage can be sent according to board requirement. Now since tlv320aic3x codec driver is DT aware, update dt files and functions to handle this new "micbias-vg" platform data. Because of sharing of bits, when enabling the micbias, voltage also needs to be updated. So use SND_SOC_DAPM_POST_PMU & SND_SOC_DAPM_PRE_PMD macro to create an event to handle this. Since micbias is converted to supply widget, updated machine drivers as well. This change is runtime tested on da850-evm with audio loopback (arecord|aplay) for confirmation. Signed-off-by: Hebbar Gururaja Signed-off-by: Mark Brown diff --git a/Documentation/devicetree/bindings/sound/tlv320aic3x.txt b/Documentation/devicetree/bindings/sound/tlv320aic3x.txt index e7b98f4..f47c3f5 100644 --- a/Documentation/devicetree/bindings/sound/tlv320aic3x.txt +++ b/Documentation/devicetree/bindings/sound/tlv320aic3x.txt @@ -11,6 +11,12 @@ Optional properties: - gpio-reset - gpio pin number used for codec reset - ai3x-gpio-func - - AIC3X_GPIO1 & AIC3X_GPIO2 Functionality +- ai3x-micbias-vg - MicBias Voltage required. + 1 - MICBIAS output is powered to 2.0V, + 2 - MICBIAS output is powered to 2.5V, + 3 - MICBIAS output is connected to AVDD, + If this node is not mentioned or if the value is incorrect, then MicBias + is powered down. Example: diff --git a/include/sound/tlv320aic3x.h b/include/sound/tlv320aic3x.h index ffd9bc7..9407fd0 100644 --- a/include/sound/tlv320aic3x.h +++ b/include/sound/tlv320aic3x.h @@ -46,6 +46,13 @@ enum { AIC3X_GPIO2_FUNC_BUTTON_PRESS_IRQ = 15 }; +enum aic3x_micbias_voltage { + AIC3X_MICBIAS_OFF = 0, + AIC3X_MICBIAS_2_0V = 1, + AIC3X_MICBIAS_2_5V = 2, + AIC3X_MICBIAS_AVDDV = 3, +}; + struct aic3x_setup_data { unsigned int gpio_func[2]; }; @@ -53,6 +60,9 @@ struct aic3x_setup_data { struct aic3x_pdata { int gpio_reset; /* < 0 if not used */ struct aic3x_setup_data *setup; + + /* Selects the micbias voltage */ + enum aic3x_micbias_voltage micbias_vg; }; #endif diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c index 5708a97..ba82ba2 100644 --- a/sound/soc/codecs/tlv320aic3x.c +++ b/sound/soc/codecs/tlv320aic3x.c @@ -85,6 +85,9 @@ struct aic3x_priv { #define AIC3X_MODEL_33 1 #define AIC3X_MODEL_3007 2 u16 model; + + /* Selects the micbias voltage */ + enum aic3x_micbias_voltage micbias_vg; }; /* @@ -195,6 +198,37 @@ static int snd_soc_dapm_put_volsw_aic3x(struct snd_kcontrol *kcontrol, return ret; } +/* + * mic bias power on/off share the same register bits with + * output voltage of mic bias. when power on mic bias, we + * need reclaim it to voltage value. + * 0x0 = Powered off + * 0x1 = MICBIAS output is powered to 2.0V, + * 0x2 = MICBIAS output is powered to 2.5V + * 0x3 = MICBIAS output is connected to AVDD + */ +static int mic_bias_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = w->codec; + struct aic3x_priv *aic3x = snd_soc_codec_get_drvdata(codec); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + /* change mic bias voltage to user defined */ + snd_soc_update_bits(codec, MICBIAS_CTRL, + MICBIAS_LEVEL_MASK, + aic3x->micbias_vg << MICBIAS_LEVEL_SHIFT); + break; + + case SND_SOC_DAPM_PRE_PMD: + snd_soc_update_bits(codec, MICBIAS_CTRL, + MICBIAS_LEVEL_MASK, 0); + break; + } + return 0; +} + static const char *aic3x_left_dac_mux[] = { "DAC_L1", "DAC_L3", "DAC_L2" }; static const char *aic3x_right_dac_mux[] = { "DAC_R1", "DAC_R3", "DAC_R2" }; static const char *aic3x_left_hpcom_mux[] = @@ -596,12 +630,9 @@ static const struct snd_soc_dapm_widget aic3x_dapm_widgets[] = { AIC3X_ASD_INTF_CTRLA, 0, 3, 3, 0), /* Mic Bias */ - SND_SOC_DAPM_REG(snd_soc_dapm_micbias, "Mic Bias 2V", - MICBIAS_CTRL, 6, 3, 1, 0), - SND_SOC_DAPM_REG(snd_soc_dapm_micbias, "Mic Bias 2.5V", - MICBIAS_CTRL, 6, 3, 2, 0), - SND_SOC_DAPM_REG(snd_soc_dapm_micbias, "Mic Bias AVDD", - MICBIAS_CTRL, 6, 3, 3, 0), + SND_SOC_DAPM_SUPPLY("Mic Bias", MICBIAS_CTRL, 6, 0, + mic_bias_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), /* Output mixers */ SND_SOC_DAPM_MIXER("Left Line Mixer", SND_SOC_NOPM, 0, 0, @@ -1386,6 +1417,24 @@ static int aic3x_probe(struct snd_soc_codec *codec) if (aic3x->model == AIC3X_MODEL_3007) snd_soc_add_codec_controls(codec, &aic3x_classd_amp_gain_ctrl, 1); + /* set mic bias voltage */ + switch (aic3x->micbias_vg) { + case AIC3X_MICBIAS_2_0V: + case AIC3X_MICBIAS_2_5V: + case AIC3X_MICBIAS_AVDDV: + snd_soc_update_bits(codec, MICBIAS_CTRL, + MICBIAS_LEVEL_MASK, + (aic3x->micbias_vg) << MICBIAS_LEVEL_SHIFT); + break; + case AIC3X_MICBIAS_OFF: + /* + * noting to do. target won't enter here. This is just to avoid + * compile time warning "warning: enumeration value + * 'AIC3X_MICBIAS_OFF' not handled in switch" + */ + break; + } + aic3x_add_widgets(codec); list_add(&aic3x->list, &reset_list); @@ -1461,6 +1510,7 @@ static int aic3x_i2c_probe(struct i2c_client *i2c, struct aic3x_setup_data *ai3x_setup; struct device_node *np = i2c->dev.of_node; int ret; + u32 value; aic3x = devm_kzalloc(&i2c->dev, sizeof(struct aic3x_priv), GFP_KERNEL); if (aic3x == NULL) { @@ -1474,6 +1524,7 @@ static int aic3x_i2c_probe(struct i2c_client *i2c, if (pdata) { aic3x->gpio_reset = pdata->gpio_reset; aic3x->setup = pdata->setup; + aic3x->micbias_vg = pdata->micbias_vg; } else if (np) { ai3x_setup = devm_kzalloc(&i2c->dev, sizeof(*ai3x_setup), GFP_KERNEL); @@ -1493,6 +1544,26 @@ static int aic3x_i2c_probe(struct i2c_client *i2c, aic3x->setup = ai3x_setup; } + if (!of_property_read_u32(np, "ai3x-micbias-vg", &value)) { + switch (value) { + case 1 : + aic3x->micbias_vg = AIC3X_MICBIAS_2_0V; + break; + case 2 : + aic3x->micbias_vg = AIC3X_MICBIAS_2_5V; + break; + case 3 : + aic3x->micbias_vg = AIC3X_MICBIAS_AVDDV; + break; + default : + aic3x->micbias_vg = AIC3X_MICBIAS_OFF; + dev_err(&i2c->dev, "Unsuitable MicBias voltage " + "found in DT\n"); + } + } else { + aic3x->micbias_vg = AIC3X_MICBIAS_OFF; + } + } else { aic3x->gpio_reset = -1; } diff --git a/sound/soc/codecs/tlv320aic3x.h b/sound/soc/codecs/tlv320aic3x.h index 6db3c41..e521ac3 100644 --- a/sound/soc/codecs/tlv320aic3x.h +++ b/sound/soc/codecs/tlv320aic3x.h @@ -238,6 +238,10 @@ /* Default input volume */ #define DEFAULT_GAIN 0x20 +/* MICBIAS Control Register */ +#define MICBIAS_LEVEL_SHIFT (6) +#define MICBIAS_LEVEL_MASK (3 << 6) + /* headset detection / button API */ /* The AIC3x supports detection of stereo headsets (GND + left + right signal) diff --git a/sound/soc/davinci/davinci-evm.c b/sound/soc/davinci/davinci-evm.c index d55e647..484b22c 100644 --- a/sound/soc/davinci/davinci-evm.c +++ b/sound/soc/davinci/davinci-evm.c @@ -116,9 +116,9 @@ static const struct snd_soc_dapm_route audio_map[] = { {"Line Out", NULL, "RLOUT"}, /* Mic connected to (MIC3L | MIC3R) */ - {"MIC3L", NULL, "Mic Bias 2V"}, - {"MIC3R", NULL, "Mic Bias 2V"}, - {"Mic Bias 2V", NULL, "Mic Jack"}, + {"MIC3L", NULL, "Mic Bias"}, + {"MIC3R", NULL, "Mic Bias"}, + {"Mic Bias", NULL, "Mic Jack"}, /* Line In connected to (LINE1L | LINE2L), (LINE1R | LINE2R) */ {"LINE1L", NULL, "Line In"}, diff --git a/sound/soc/omap/n810.c b/sound/soc/omap/n810.c index 230b8c1..ee7cd53 100644 --- a/sound/soc/omap/n810.c +++ b/sound/soc/omap/n810.c @@ -230,8 +230,8 @@ static const struct snd_soc_dapm_route audio_map[] = { {"Ext Spk", NULL, "LLOUT"}, {"Ext Spk", NULL, "RLOUT"}, - {"DMic Rate 64", NULL, "Mic Bias 2V"}, - {"Mic Bias 2V", NULL, "DMic"}, + {"DMic Rate 64", NULL, "Mic Bias"}, + {"Mic Bias", NULL, "DMic"}, }; static const char *spk_function[] = {"Off", "On"}; diff --git a/sound/soc/omap/rx51.c b/sound/soc/omap/rx51.c index d921ddb..3cd5257 100644 --- a/sound/soc/omap/rx51.c +++ b/sound/soc/omap/rx51.c @@ -248,16 +248,16 @@ static const struct snd_soc_dapm_route audio_map[] = { {"FM Transmitter", NULL, "LLOUT"}, {"FM Transmitter", NULL, "RLOUT"}, - {"DMic Rate 64", NULL, "Mic Bias 2V"}, - {"Mic Bias 2V", NULL, "DMic"}, + {"DMic Rate 64", NULL, "Mic Bias"}, + {"Mic Bias", NULL, "DMic"}, }; static const struct snd_soc_dapm_route audio_mapb[] = { {"b LINE2R", NULL, "MONO_LOUT"}, {"Earphone", NULL, "b HPLOUT"}, - {"LINE1L", NULL, "b Mic Bias 2.5V"}, - {"b Mic Bias 2.5V", NULL, "HS Mic"} + {"LINE1L", NULL, "b Mic Bias"}, + {"b Mic Bias", NULL, "HS Mic"} }; static const char *spk_function[] = {"Off", "On"}; -- cgit v0.10.2 From 0d2b6422529a26ac4dee06196524ba9da70cf735 Mon Sep 17 00:00:00 2001 From: Chris Rattray Date: Fri, 1 Feb 2013 15:51:41 +0000 Subject: ASoC: wm2200: correct IN2L and IN3L digital mute Signed-off-by: Chris Rattray Signed-off-by: Mark Brown Cc: stable@vger.kernel.org diff --git a/sound/soc/codecs/wm2200.c b/sound/soc/codecs/wm2200.c index d8c65f5..d5371e0 100644 --- a/sound/soc/codecs/wm2200.c +++ b/sound/soc/codecs/wm2200.c @@ -1126,9 +1126,9 @@ SOC_DOUBLE_R_TLV("IN3 Volume", WM2200_IN3L_CONTROL, WM2200_IN3R_CONTROL, SOC_DOUBLE_R("IN1 Digital Switch", WM2200_ADC_DIGITAL_VOLUME_1L, WM2200_ADC_DIGITAL_VOLUME_1R, WM2200_IN1L_MUTE_SHIFT, 1, 1), -SOC_DOUBLE_R("IN2 Digital Switch", WM2200_ADC_DIGITAL_VOLUME_1L, +SOC_DOUBLE_R("IN2 Digital Switch", WM2200_ADC_DIGITAL_VOLUME_2L, WM2200_ADC_DIGITAL_VOLUME_2R, WM2200_IN2L_MUTE_SHIFT, 1, 1), -SOC_DOUBLE_R("IN3 Digital Switch", WM2200_ADC_DIGITAL_VOLUME_1L, +SOC_DOUBLE_R("IN3 Digital Switch", WM2200_ADC_DIGITAL_VOLUME_3L, WM2200_ADC_DIGITAL_VOLUME_3R, WM2200_IN3L_MUTE_SHIFT, 1, 1), SOC_DOUBLE_R_TLV("IN1 Digital Volume", WM2200_ADC_DIGITAL_VOLUME_1L, -- cgit v0.10.2 From cf17c83c4ac2de13a7b158c1c27fffb30ce109c3 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 30 Jan 2013 14:37:23 +0800 Subject: ASoC: wm_adsp: Use asynchronous I/O to write firmware and coefficients Allow the regmap API to use asynchronous I/O where supported to minimise the delay between transfers, reducing firmware download times. Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index 5487a94..be45e2b 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -153,6 +154,43 @@ #define ADSP2_RAM_RDY_SHIFT 0 #define ADSP2_RAM_RDY_WIDTH 1 +struct wm_adsp_buf { + struct list_head list; + void *buf; +}; + +static struct wm_adsp_buf *wm_adsp_buf_alloc(const void *src, size_t len, + struct list_head *list) +{ + struct wm_adsp_buf *buf = kzalloc(sizeof(*buf), GFP_KERNEL); + + if (buf == NULL) + return NULL; + + buf->buf = kmemdup(src, len, GFP_KERNEL | GFP_DMA); + if (!buf->buf) { + kfree(buf); + return NULL; + } + + if (list) + list_add_tail(&buf->list, list); + + return buf; +} + +static void wm_adsp_buf_free(struct list_head *list) +{ + while (!list_empty(list)) { + struct wm_adsp_buf *buf = list_first_entry(list, + struct wm_adsp_buf, + list); + list_del(&buf->list); + kfree(buf->buf); + kfree(buf); + } +} + #define WM_ADSP_NUM_FW 4 static const char *wm_adsp_fw_text[WM_ADSP_NUM_FW] = { @@ -254,6 +292,7 @@ static unsigned int wm_adsp_region_to_reg(struct wm_adsp_region const *region, static int wm_adsp_load(struct wm_adsp *dsp) { + LIST_HEAD(buf_list); const struct firmware *firmware; struct regmap *regmap = dsp->regmap; unsigned int pos = 0; @@ -265,7 +304,7 @@ static int wm_adsp_load(struct wm_adsp *dsp) const struct wm_adsp_region *mem; const char *region_name; char *file, *text; - void *buf; + struct wm_adsp_buf *buf; unsigned int reg; int regions = 0; int ret, offset, type, sizes; @@ -420,18 +459,16 @@ static int wm_adsp_load(struct wm_adsp *dsp) } if (reg) { - buf = kmemdup(region->data, le32_to_cpu(region->len), - GFP_KERNEL | GFP_DMA); + buf = wm_adsp_buf_alloc(region->data, + le32_to_cpu(region->len), + &buf_list); if (!buf) { adsp_err(dsp, "Out of memory\n"); return -ENOMEM; } - ret = regmap_raw_write(regmap, reg, buf, - le32_to_cpu(region->len)); - - kfree(buf); - + ret = regmap_raw_write_async(regmap, reg, buf->buf, + le32_to_cpu(region->len)); if (ret != 0) { adsp_err(dsp, "%s.%d: Failed to write %d bytes at %d in %s: %d\n", @@ -445,12 +482,20 @@ static int wm_adsp_load(struct wm_adsp *dsp) pos += le32_to_cpu(region->len) + sizeof(*region); regions++; } - + + ret = regmap_async_complete(regmap); + if (ret != 0) { + adsp_err(dsp, "Failed to complete async write: %d\n", ret); + goto out_fw; + } + if (pos > firmware->size) adsp_warn(dsp, "%s.%d: %zu bytes at end of file\n", file, regions, pos - firmware->size); out_fw: + regmap_async_complete(regmap); + wm_adsp_buf_free(&buf_list); release_firmware(firmware); out: kfree(file); @@ -655,6 +700,7 @@ out: static int wm_adsp_load_coeff(struct wm_adsp *dsp) { + LIST_HEAD(buf_list); struct regmap *regmap = dsp->regmap; struct wmfw_coeff_hdr *hdr; struct wmfw_coeff_item *blk; @@ -664,7 +710,7 @@ static int wm_adsp_load_coeff(struct wm_adsp *dsp) const char *region_name; int ret, pos, blocks, type, offset, reg; char *file; - void *buf; + struct wm_adsp_buf *buf; file = kzalloc(PAGE_SIZE, GFP_KERNEL); if (file == NULL) @@ -776,8 +822,9 @@ static int wm_adsp_load_coeff(struct wm_adsp *dsp) } if (reg) { - buf = kmemdup(blk->data, le32_to_cpu(blk->len), - GFP_KERNEL | GFP_DMA); + buf = wm_adsp_buf_alloc(blk->data, + le32_to_cpu(blk->len), + &buf_list); if (!buf) { adsp_err(dsp, "Out of memory\n"); return -ENOMEM; @@ -786,27 +833,30 @@ static int wm_adsp_load_coeff(struct wm_adsp *dsp) adsp_dbg(dsp, "%s.%d: Writing %d bytes at %x\n", file, blocks, le32_to_cpu(blk->len), reg); - ret = regmap_raw_write(regmap, reg, blk->data, - le32_to_cpu(blk->len)); + ret = regmap_raw_write_async(regmap, reg, buf->buf, + le32_to_cpu(blk->len)); if (ret != 0) { adsp_err(dsp, "%s.%d: Failed to write to %x in %s\n", file, blocks, reg, region_name); } - - kfree(buf); } pos += le32_to_cpu(blk->len) + sizeof(*blk); blocks++; } + ret = regmap_async_complete(regmap); + if (ret != 0) + adsp_err(dsp, "Failed to complete async write: %d\n", ret); + if (pos > firmware->size) adsp_warn(dsp, "%s.%d: %zu bytes at end of file\n", file, blocks, pos - firmware->size); out_fw: release_firmware(firmware); + wm_adsp_buf_free(&buf_list); out: kfree(file); return 0; -- cgit v0.10.2 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 diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 9641c0e..e934c49 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 v0.10.2 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 diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index e934c49..ca7d962 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 v0.10.2 From a690a2a1eb32e533e2b2afb1daeef3c4011d47e9 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 5 Feb 2013 12:24:22 +0100 Subject: ALSA: Fix wrong description about hw constraints The definitions of hw constraint functions are wrongly placed, and the description about the function is also wrong. hw_rule_channels_by_format actually refines the channels depending on the format, not vice versa. Reported-by: Peter Ujfalusi Signed-off-by: Takashi Iwai diff --git a/Documentation/DocBook/writing-an-alsa-driver.tmpl b/Documentation/DocBook/writing-an-alsa-driver.tmpl index c0781bb..c564faa 100644 --- a/Documentation/DocBook/writing-an-alsa-driver.tmpl +++ b/Documentation/DocBook/writing-an-alsa-driver.tmpl @@ -3250,18 +3250,19 @@ struct _snd_pcm_runtime { Example of Hardware Constraints for Channels min < 2) { - fmt.bits[0] &= SNDRV_PCM_FMTBIT_S16_LE; - return snd_mask_refine(f, &fmt); + snd_interval_any(&ch); + if (f->bits[0] == SNDRV_PCM_FMTBIT_S16_LE) { + ch.min = ch.max = 1; + ch.integer = 1; + return snd_interval_refine(c, &ch); } return 0; } @@ -3285,27 +3286,27 @@ struct _snd_pcm_runtime { - The rule function is called when an application sets the number of - channels. But an application can set the format before the number of - channels. Thus you also need to define the inverse rule: + The rule function is called when an application sets the PCM + format, and it refines the number of channels accordingly. + But an application may set the number of channels before + setting the format. Thus you also need to define the inverse rule: - Example of Hardware Constraints for Channels + Example of Hardware Constraints for Formats bits[0] == SNDRV_PCM_FMTBIT_S16_LE) { - ch.min = ch.max = 1; - ch.integer = 1; - return snd_interval_refine(c, &ch); + snd_mask_any(&fmt); /* Init the struct */ + if (c->min < 2) { + fmt.bits[0] &= SNDRV_PCM_FMTBIT_S16_LE; + return snd_mask_refine(f, &fmt); } return 0; } -- cgit v0.10.2 From 16c5ab1d3a6d1b11ed2966fa33a3a4fecd13a2bc Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 5 Feb 2013 14:23:53 +0100 Subject: ALSA: Replace 0 with NULL in writing-an-alsa-driver.tmpl Spotted while correcting the sentences. Signed-off-by: Takashi Iwai diff --git a/Documentation/DocBook/writing-an-alsa-driver.tmpl b/Documentation/DocBook/writing-an-alsa-driver.tmpl index c564faa..bd6fee2 100644 --- a/Documentation/DocBook/writing-an-alsa-driver.tmpl +++ b/Documentation/DocBook/writing-an-alsa-driver.tmpl @@ -3278,8 +3278,8 @@ struct _snd_pcm_runtime { runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, - hw_rule_channels_by_format, 0, SNDRV_PCM_HW_PARAM_FORMAT, - -1); + hw_rule_channels_by_format, NULL, + SNDRV_PCM_HW_PARAM_FORMAT, -1); ]]> @@ -3321,8 +3321,8 @@ struct _snd_pcm_runtime { runtime, 0, SNDRV_PCM_HW_PARAM_FORMAT, - hw_rule_format_by_channels, 0, SNDRV_PCM_HW_PARAM_CHANNELS, - -1); + hw_rule_format_by_channels, NULL, + SNDRV_PCM_HW_PARAM_CHANNELS, -1); ]]> -- cgit v0.10.2 From a92b53179d7d2d004f0379e70d41c56c4f570c5c Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 5 Feb 2013 13:43:39 +0100 Subject: ASoC: omap-pcm: No need to set constraint at open time The same constraint is going to be set in the snd_dmaengine_pcm_open() function, so there is no need to set it here as well. Signed-off-by: Peter Ujfalusi Reviewed-by: Jarkko Nikula Signed-off-by: Mark Brown diff --git a/sound/soc/omap/omap-pcm.c b/sound/soc/omap/omap-pcm.c index 47bdbd4..c722c2e 100644 --- a/sound/soc/omap/omap-pcm.c +++ b/sound/soc/omap/omap-pcm.c @@ -174,23 +174,15 @@ static snd_pcm_uframes_t omap_pcm_pointer(struct snd_pcm_substream *substream) static int omap_pcm_open(struct snd_pcm_substream *substream) { - struct snd_pcm_runtime *runtime = substream->runtime; struct snd_soc_pcm_runtime *rtd = substream->private_data; struct omap_pcm_dma_data *dma_data; - int ret; snd_soc_set_runtime_hwparams(substream, &omap_pcm_hardware); - /* Ensure that buffer size is a multiple of period size */ - ret = snd_pcm_hw_constraint_integer(runtime, - SNDRV_PCM_HW_PARAM_PERIODS); - if (ret < 0) - return ret; - dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); - ret = snd_dmaengine_pcm_open(substream, omap_dma_filter_fn, - &dma_data->dma_req); - return ret; + + return snd_dmaengine_pcm_open(substream, omap_dma_filter_fn, + &dma_data->dma_req); } static int omap_pcm_close(struct snd_pcm_substream *substream) -- cgit v0.10.2 From 1f88eb0f0660f8b58a1fe9011f3d3a350c7dd194 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Tue, 5 Feb 2013 10:41:47 +0000 Subject: ASoC: soc-compress: Add support for not memory mapped DSPs The ASoC compressed API did not implement the copy callback in its compressed ops which is required for DSPs that are not memory mapped. This patch creates a local copy of the compress ops for each runtime and modifies them with a copy callback as appropriate. Signed-off-by: Charles Keepax Acked-by: Vinod Koul Signed-off-by: Mark Brown diff --git a/sound/soc/soc-compress.c b/sound/soc/soc-compress.c index 3ea7956..c81aeec 100644 --- a/sound/soc/soc-compress.c +++ b/sound/soc/soc-compress.c @@ -302,6 +302,22 @@ static int soc_compr_pointer(struct snd_compr_stream *cstream, return 0; } +static int soc_compr_copy(struct snd_compr_stream *cstream, + const char __user *buf, size_t count) +{ + struct snd_soc_pcm_runtime *rtd = cstream->private_data; + struct snd_soc_platform *platform = rtd->platform; + int ret = 0; + + mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); + + if (platform->driver->compr_ops && platform->driver->compr_ops->copy) + ret = platform->driver->compr_ops->copy(cstream, buf, count); + + mutex_unlock(&rtd->pcm_mutex); + return ret; +} + /* ASoC Compress operations */ static struct snd_compr_ops soc_compr_ops = { .open = soc_compr_open, @@ -319,6 +335,7 @@ static struct snd_compr_ops soc_compr_ops = { int soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num) { struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_platform *platform = rtd->platform; struct snd_soc_dai *codec_dai = rtd->codec_dai; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; struct snd_compr *compr; @@ -335,14 +352,25 @@ int soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num) return -ENOMEM; } - compr->ops = &soc_compr_ops; + compr->ops = devm_kzalloc(rtd->card->dev, sizeof(soc_compr_ops), + GFP_KERNEL); + if (compr->ops == NULL) { + dev_err(rtd->card->dev, "Cannot allocate compressed ops\n"); + ret = -ENOMEM; + goto compr_err; + } + memcpy(compr->ops, &soc_compr_ops, sizeof(soc_compr_ops)); + + /* Add copy callback for not memory mapped DSPs */ + if (platform->driver->compr_ops && platform->driver->compr_ops->copy) + compr->ops->copy = soc_compr_copy; + mutex_init(&compr->lock); ret = snd_compress_new(rtd->card->snd_card, num, direction, compr); if (ret < 0) { pr_err("compress asoc: can't create compress for codec %s\n", codec->name); - kfree(compr); - return ret; + goto compr_err; } /* DAPM dai link stream work */ @@ -354,4 +382,8 @@ int soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num) printk(KERN_INFO "compress asoc: %s <-> %s mapping ok\n", codec_dai->name, cpu_dai->name); return ret; + +compr_err: + kfree(compr); + return ret; } -- cgit v0.10.2 From 1a786243235b8a8f4762ee57f185dadd97794fa4 Mon Sep 17 00:00:00 2001 From: Chris Rattray Date: Tue, 5 Feb 2013 14:40:44 +0000 Subject: ASoC: wm2200: Provide platform data for MICBIAS configuration Signed-off-by: Chris Rattray Signed-off-by: Mark Brown diff --git a/include/sound/wm2200.h b/include/sound/wm2200.h index 79bf55b..bc7ab1a 100644 --- a/include/sound/wm2200.h +++ b/include/sound/wm2200.h @@ -12,6 +12,7 @@ #define __LINUX_SND_WM2200_H #define WM2200_GPIO_SET 0x10000 +#define WM2200_MAX_MICBIAS 2 enum wm2200_in_mode { WM2200_IN_SE = 0, @@ -25,6 +26,24 @@ enum wm2200_dmic_sup { WM2200_DMIC_SUP_MICBIAS2 = 2, }; +enum wm2200_mbias_lvl { + WM2200_MBIAS_LVL_1V5 = 1, + WM2200_MBIAS_LVL_1V8 = 2, + WM2200_MBIAS_LVL_1V9 = 3, + WM2200_MBIAS_LVL_2V0 = 4, + WM2200_MBIAS_LVL_2V2 = 5, + WM2200_MBIAS_LVL_2V4 = 6, + WM2200_MBIAS_LVL_2V5 = 7, + WM2200_MBIAS_LVL_2V6 = 8, +}; + +struct wm2200_micbias { + enum wm2200_mbias_lvl mb_lvl; /** Regulated voltage */ + unsigned int discharge:1; /** Actively discharge */ + unsigned int fast_start:1; /** Enable aggressive startup ramp rate */ + unsigned int bypass:1; /** Use bypass mode */ +}; + struct wm2200_pdata { int reset; /** GPIO controlling /RESET, if any */ int ldo_ena; /** GPIO controlling LODENA, if any */ @@ -35,7 +54,8 @@ struct wm2200_pdata { enum wm2200_in_mode in_mode[3]; enum wm2200_dmic_sup dmic_sup[3]; - int micbias_cfg[2]; /** Register value to configure MICBIAS */ + /** MICBIAS configurations */ + struct wm2200_micbias micbias[WM2200_MAX_MICBIAS]; }; #endif diff --git a/sound/soc/codecs/wm2200.c b/sound/soc/codecs/wm2200.c index fee1a18..31d29c8 100644 --- a/sound/soc/codecs/wm2200.c +++ b/sound/soc/codecs/wm2200.c @@ -2212,6 +2212,7 @@ static int wm2200_i2c_probe(struct i2c_client *i2c, struct wm2200_priv *wm2200; unsigned int reg; int ret, i; + int val; wm2200 = devm_kzalloc(&i2c->dev, sizeof(struct wm2200_priv), GFP_KERNEL); @@ -2362,6 +2363,36 @@ static int wm2200_i2c_probe(struct i2c_client *i2c, regmap_write(wm2200->regmap, WM2200_AUDIO_IF_1_16 + i, i); } + for (i = 0; i < WM2200_MAX_MICBIAS; i++) { + if (!wm2200->pdata.micbias[i].mb_lvl && + !wm2200->pdata.micbias[i].bypass) + continue; + + /* Apply default for bypass mode */ + if (!wm2200->pdata.micbias[i].mb_lvl) + wm2200->pdata.micbias[i].mb_lvl + = WM2200_MBIAS_LVL_1V5; + + val = (wm2200->pdata.micbias[i].mb_lvl -1) + << WM2200_MICB1_LVL_SHIFT; + + if (wm2200->pdata.micbias[i].discharge) + val |= WM2200_MICB1_DISCH; + + if (wm2200->pdata.micbias[i].fast_start) + val |= WM2200_MICB1_RATE; + + if (wm2200->pdata.micbias[i].bypass) + val |= WM2200_MICB1_MODE; + + regmap_update_bits(wm2200->regmap, + WM2200_MIC_BIAS_CTRL_1 + i, + WM2200_MICB1_LVL_MASK | + WM2200_MICB1_DISCH | + WM2200_MICB1_MODE | + WM2200_MICB1_RATE, val); + } + for (i = 0; i < ARRAY_SIZE(wm2200->pdata.in_mode); i++) { regmap_update_bits(wm2200->regmap, wm2200_mic_ctrl_reg[i], WM2200_IN1_MODE_MASK | -- cgit v0.10.2 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 diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 617ac1f..83d5335 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 v0.10.2 From b57a895fa2188d4e1cefa030d0fc9d126e453a95 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 7 Feb 2013 11:28:48 +0300 Subject: ALSA: ice1712: fix boundary check in snd_wm8766_write() The wm->regs[] array has WM8766_REG_COUNT (16) elements not WM8766_REG_RESET (31). Signed-off-by: Dan Carpenter Signed-off-by: Takashi Iwai diff --git a/sound/pci/ice1712/wm8766.c b/sound/pci/ice1712/wm8766.c index 8072ade..e473f8a 100644 --- a/sound/pci/ice1712/wm8766.c +++ b/sound/pci/ice1712/wm8766.c @@ -31,7 +31,7 @@ static void snd_wm8766_write(struct snd_wm8766 *wm, u16 addr, u16 data) { - if (addr < WM8766_REG_RESET) + if (addr < WM8766_REG_COUNT) wm->regs[addr] = data; wm->ops.write(wm, addr, data); } -- cgit v0.10.2 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 diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index c4ba306..c2cd3d6 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 065fcc7..d5348dd 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index c2cd3d6..6af5ade 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 d5348dd..009b57b 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 ca7d962..c35338a 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 v0.10.2 From 9092a6ea1627d739ce2a98c2b4a7eadb23e07021 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 6 Feb 2013 17:58:57 +0000 Subject: ASoC: arizona: Fix debug logging level for FLLs and AIFs Use _dbg for debug messages. Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c index 2899cb9..3b8e8c7 100644 --- a/sound/soc/codecs/arizona.c +++ b/sound/soc/codecs/arizona.c @@ -56,14 +56,14 @@ #define arizona_fll_warn(_fll, fmt, ...) \ dev_warn(_fll->arizona->dev, "FLL%d: " fmt, _fll->id, ##__VA_ARGS__) #define arizona_fll_dbg(_fll, fmt, ...) \ - dev_err(_fll->arizona->dev, "FLL%d: " fmt, _fll->id, ##__VA_ARGS__) + dev_dbg(_fll->arizona->dev, "FLL%d: " fmt, _fll->id, ##__VA_ARGS__) #define arizona_aif_err(_dai, fmt, ...) \ dev_err(_dai->dev, "AIF%d: " fmt, _dai->id, ##__VA_ARGS__) #define arizona_aif_warn(_dai, fmt, ...) \ dev_warn(_dai->dev, "AIF%d: " fmt, _dai->id, ##__VA_ARGS__) #define arizona_aif_dbg(_dai, fmt, ...) \ - dev_err(_dai->dev, "AIF%d: " fmt, _dai->id, ##__VA_ARGS__) + dev_dbg(_dai->dev, "AIF%d: " fmt, _dai->id, ##__VA_ARGS__) const char *arizona_mixer_texts[ARIZONA_NUM_MIXER_INPUTS] = { "None", -- cgit v0.10.2 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 diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index 9d9040b..639a282 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 v0.10.2 From 7e3bb169ff20972b7c238a9fda108b94e7cb9df0 Mon Sep 17 00:00:00 2001 From: Jerry Wong Date: Wed, 6 Feb 2013 11:02:33 -0800 Subject: ASoC: Replace max98090 Device Driver This patch removes the existing max98090 driver prior to installing a more complete one. Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 3a84782..0e368d4 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -44,7 +44,6 @@ config SND_SOC_ALL_CODECS select SND_SOC_LM4857 if I2C select SND_SOC_LM49453 if I2C select SND_SOC_MAX98088 if I2C - select SND_SOC_MAX98090 if I2C select SND_SOC_MAX98095 if I2C select SND_SOC_MAX9850 if I2C select SND_SOC_MAX9768 if I2C @@ -268,9 +267,6 @@ config SND_SOC_LM49453 config SND_SOC_MAX98088 tristate -config SND_SOC_MAX98090 - tristate - config SND_SOC_MAX98095 tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index f6e8e36..aa56312 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -34,7 +34,6 @@ snd-soc-lm4857-objs := lm4857.o snd-soc-lm49453-objs := lm49453.o snd-soc-max9768-objs := max9768.o snd-soc-max98088-objs := max98088.o -snd-soc-max98090-objs := max98090.o snd-soc-max98095-objs := max98095.o snd-soc-max9850-objs := max9850.o snd-soc-mc13783-objs := mc13783.o @@ -158,7 +157,6 @@ obj-$(CONFIG_SND_SOC_LM4857) += snd-soc-lm4857.o obj-$(CONFIG_SND_SOC_LM49453) += snd-soc-lm49453.o obj-$(CONFIG_SND_SOC_MAX9768) += snd-soc-max9768.o obj-$(CONFIG_SND_SOC_MAX98088) += snd-soc-max98088.o -obj-$(CONFIG_SND_SOC_MAX98090) += snd-soc-max98090.o obj-$(CONFIG_SND_SOC_MAX98095) += snd-soc-max98095.o obj-$(CONFIG_SND_SOC_MAX9850) += snd-soc-max9850.o obj-$(CONFIG_SND_SOC_MC13783) += snd-soc-mc13783.o diff --git a/sound/soc/codecs/max98090.c b/sound/soc/codecs/max98090.c deleted file mode 100644 index c9772ca..0000000 --- a/sound/soc/codecs/max98090.c +++ /dev/null @@ -1,577 +0,0 @@ -/* - * max98090.c -- MAX98090 ALSA SoC Audio driver - * based on Rev0p8 datasheet - * - * Copyright (C) 2012 Renesas Solutions Corp. - * Kuninori Morimoto - * - * Based on - * - * max98095.c - * Copyright 2011 Maxim Integrated Products - * - * https://github.com/hardkernel/linux/commit/\ - * 3417d7166b17113b3b33b0a337c74d1c7cc313df#sound/soc/codecs/max98090.c - * Copyright 2011 Maxim Integrated Products - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include - -/* - * - * MAX98090 Registers Definition - * - */ - -/* RESET / STATUS / INTERRUPT REGISTERS */ -#define MAX98090_0x00_SW_RESET 0x00 -#define MAX98090_0x01_INT_STS 0x01 -#define MAX98090_0x02_JACK_STS 0x02 -#define MAX98090_0x03_INT_MASK 0x03 - -/* QUICK SETUP REGISTERS */ -#define MAX98090_0x04_SYS_CLK 0x04 -#define MAX98090_0x05_SAMPLE_RATE 0x05 -#define MAX98090_0x06_DAI_IF 0x06 -#define MAX98090_0x07_DAC_PATH 0x07 -#define MAX98090_0x08_MIC_TO_ADC 0x08 -#define MAX98090_0x09_LINE_TO_ADC 0x09 -#define MAX98090_0x0A_ANALOG_MIC_LOOP 0x0A -#define MAX98090_0x0B_ANALOG_LINE_LOOP 0x0B - -/* ANALOG INPUT CONFIGURATION REGISTERS */ -#define MAX98090_0x0D_INPUT_CONFIG 0x0D -#define MAX98090_0x0E_LINE_IN_LVL 0x0E -#define MAX98090_0x0F_LINI_IN_CFG 0x0F -#define MAX98090_0x10_MIC1_IN_LVL 0x10 -#define MAX98090_0x11_MIC2_IN_LVL 0x11 - -/* MICROPHONE CONFIGURATION REGISTERS */ -#define MAX98090_0x12_MIC_BIAS_VOL 0x12 -#define MAX98090_0x13_DIGITAL_MIC_CFG 0x13 -#define MAX98090_0x14_DIGITAL_MIC_MODE 0x14 - -/* ADC PATH AND CONFIGURATION REGISTERS */ -#define MAX98090_0x15_L_ADC_MIX 0x15 -#define MAX98090_0x16_R_ADC_MIX 0x16 -#define MAX98090_0x17_L_ADC_LVL 0x17 -#define MAX98090_0x18_R_ADC_LVL 0x18 -#define MAX98090_0x19_ADC_BIQUAD_LVL 0x19 -#define MAX98090_0x1A_ADC_SIDETONE 0x1A - -/* CLOCK CONFIGURATION REGISTERS */ -#define MAX98090_0x1B_SYS_CLK 0x1B -#define MAX98090_0x1C_CLK_MODE 0x1C -#define MAX98090_0x1D_ANY_CLK1 0x1D -#define MAX98090_0x1E_ANY_CLK2 0x1E -#define MAX98090_0x1F_ANY_CLK3 0x1F -#define MAX98090_0x20_ANY_CLK4 0x20 -#define MAX98090_0x21_MASTER_MODE 0x21 - -/* INTERFACE CONTROL REGISTERS */ -#define MAX98090_0x22_DAI_IF_FMT 0x22 -#define MAX98090_0x23_DAI_TDM_FMT1 0x23 -#define MAX98090_0x24_DAI_TDM_FMT2 0x24 -#define MAX98090_0x25_DAI_IO_CFG 0x25 -#define MAX98090_0x26_FILTER_CFG 0x26 -#define MAX98090_0x27_DAI_PLAYBACK_LVL 0x27 -#define MAX98090_0x28_EQ_PLAYBACK_LVL 0x28 - -/* HEADPHONE CONTROL REGISTERS */ -#define MAX98090_0x29_L_HP_MIX 0x29 -#define MAX98090_0x2A_R_HP_MIX 0x2A -#define MAX98090_0x2B_HP_CTR 0x2B -#define MAX98090_0x2C_L_HP_VOL 0x2C -#define MAX98090_0x2D_R_HP_VOL 0x2D - -/* SPEAKER CONFIGURATION REGISTERS */ -#define MAX98090_0x2E_L_SPK_MIX 0x2E -#define MAX98090_0x2F_R_SPK_MIX 0x2F -#define MAX98090_0x30_SPK_CTR 0x30 -#define MAX98090_0x31_L_SPK_VOL 0x31 -#define MAX98090_0x32_R_SPK_VOL 0x32 - -/* ALC CONFIGURATION REGISTERS */ -#define MAX98090_0x33_ALC_TIMING 0x33 -#define MAX98090_0x34_ALC_COMPRESSOR 0x34 -#define MAX98090_0x35_ALC_EXPANDER 0x35 -#define MAX98090_0x36_ALC_GAIN 0x36 - -/* RECEIVER AND LINE_OUTPUT REGISTERS */ -#define MAX98090_0x37_RCV_LOUT_L_MIX 0x37 -#define MAX98090_0x38_RCV_LOUT_L_CNTL 0x38 -#define MAX98090_0x39_RCV_LOUT_L_VOL 0x39 -#define MAX98090_0x3A_LOUT_R_MIX 0x3A -#define MAX98090_0x3B_LOUT_R_CNTL 0x3B -#define MAX98090_0x3C_LOUT_R_VOL 0x3C - -/* JACK DETECT AND ENABLE REGISTERS */ -#define MAX98090_0x3D_JACK_DETECT 0x3D -#define MAX98090_0x3E_IN_ENABLE 0x3E -#define MAX98090_0x3F_OUT_ENABLE 0x3F -#define MAX98090_0x40_LVL_CTR 0x40 -#define MAX98090_0x41_DSP_FILTER_ENABLE 0x41 - -/* BIAS AND POWER MODE CONFIGURATION REGISTERS */ -#define MAX98090_0x42_BIAS_CTR 0x42 -#define MAX98090_0x43_DAC_CTR 0x43 -#define MAX98090_0x44_ADC_CTR 0x44 -#define MAX98090_0x45_DEV_SHUTDOWN 0x45 - -/* REVISION ID REGISTER */ -#define MAX98090_0xFF_REV_ID 0xFF - -#define MAX98090_REG_MAX_CACHED 0x45 -#define MAX98090_REG_END 0xFF - -/* - * - * MAX98090 Registers Bit Fields - * - */ - -/* MAX98090_0x06_DAI_IF */ -#define MAX98090_DAI_IF_MASK 0x3F -#define MAX98090_RJ_M (1 << 5) -#define MAX98090_RJ_S (1 << 4) -#define MAX98090_LJ_M (1 << 3) -#define MAX98090_LJ_S (1 << 2) -#define MAX98090_I2S_M (1 << 1) -#define MAX98090_I2S_S (1 << 0) - -/* MAX98090_0x45_DEV_SHUTDOWN */ -#define MAX98090_SHDNRUN (1 << 7) - -/* codec private data */ -struct max98090_priv { - struct regmap *regmap; -}; - -static const struct reg_default max98090_reg_defaults[] = { - /* RESET / STATUS / INTERRUPT REGISTERS */ - {MAX98090_0x00_SW_RESET, 0x00}, - {MAX98090_0x01_INT_STS, 0x00}, - {MAX98090_0x02_JACK_STS, 0x00}, - {MAX98090_0x03_INT_MASK, 0x04}, - - /* QUICK SETUP REGISTERS */ - {MAX98090_0x04_SYS_CLK, 0x00}, - {MAX98090_0x05_SAMPLE_RATE, 0x00}, - {MAX98090_0x06_DAI_IF, 0x00}, - {MAX98090_0x07_DAC_PATH, 0x00}, - {MAX98090_0x08_MIC_TO_ADC, 0x00}, - {MAX98090_0x09_LINE_TO_ADC, 0x00}, - {MAX98090_0x0A_ANALOG_MIC_LOOP, 0x00}, - {MAX98090_0x0B_ANALOG_LINE_LOOP, 0x00}, - - /* ANALOG INPUT CONFIGURATION REGISTERS */ - {MAX98090_0x0D_INPUT_CONFIG, 0x00}, - {MAX98090_0x0E_LINE_IN_LVL, 0x1B}, - {MAX98090_0x0F_LINI_IN_CFG, 0x00}, - {MAX98090_0x10_MIC1_IN_LVL, 0x11}, - {MAX98090_0x11_MIC2_IN_LVL, 0x11}, - - /* MICROPHONE CONFIGURATION REGISTERS */ - {MAX98090_0x12_MIC_BIAS_VOL, 0x00}, - {MAX98090_0x13_DIGITAL_MIC_CFG, 0x00}, - {MAX98090_0x14_DIGITAL_MIC_MODE, 0x00}, - - /* ADC PATH AND CONFIGURATION REGISTERS */ - {MAX98090_0x15_L_ADC_MIX, 0x00}, - {MAX98090_0x16_R_ADC_MIX, 0x00}, - {MAX98090_0x17_L_ADC_LVL, 0x03}, - {MAX98090_0x18_R_ADC_LVL, 0x03}, - {MAX98090_0x19_ADC_BIQUAD_LVL, 0x00}, - {MAX98090_0x1A_ADC_SIDETONE, 0x00}, - - /* CLOCK CONFIGURATION REGISTERS */ - {MAX98090_0x1B_SYS_CLK, 0x00}, - {MAX98090_0x1C_CLK_MODE, 0x00}, - {MAX98090_0x1D_ANY_CLK1, 0x00}, - {MAX98090_0x1E_ANY_CLK2, 0x00}, - {MAX98090_0x1F_ANY_CLK3, 0x00}, - {MAX98090_0x20_ANY_CLK4, 0x00}, - {MAX98090_0x21_MASTER_MODE, 0x00}, - - /* INTERFACE CONTROL REGISTERS */ - {MAX98090_0x22_DAI_IF_FMT, 0x00}, - {MAX98090_0x23_DAI_TDM_FMT1, 0x00}, - {MAX98090_0x24_DAI_TDM_FMT2, 0x00}, - {MAX98090_0x25_DAI_IO_CFG, 0x00}, - {MAX98090_0x26_FILTER_CFG, 0x80}, - {MAX98090_0x27_DAI_PLAYBACK_LVL, 0x00}, - {MAX98090_0x28_EQ_PLAYBACK_LVL, 0x00}, - - /* HEADPHONE CONTROL REGISTERS */ - {MAX98090_0x29_L_HP_MIX, 0x00}, - {MAX98090_0x2A_R_HP_MIX, 0x00}, - {MAX98090_0x2B_HP_CTR, 0x00}, - {MAX98090_0x2C_L_HP_VOL, 0x1A}, - {MAX98090_0x2D_R_HP_VOL, 0x1A}, - - /* SPEAKER CONFIGURATION REGISTERS */ - {MAX98090_0x2E_L_SPK_MIX, 0x00}, - {MAX98090_0x2F_R_SPK_MIX, 0x00}, - {MAX98090_0x30_SPK_CTR, 0x00}, - {MAX98090_0x31_L_SPK_VOL, 0x2C}, - {MAX98090_0x32_R_SPK_VOL, 0x2C}, - - /* ALC CONFIGURATION REGISTERS */ - {MAX98090_0x33_ALC_TIMING, 0x00}, - {MAX98090_0x34_ALC_COMPRESSOR, 0x00}, - {MAX98090_0x35_ALC_EXPANDER, 0x00}, - {MAX98090_0x36_ALC_GAIN, 0x00}, - - /* RECEIVER AND LINE_OUTPUT REGISTERS */ - {MAX98090_0x37_RCV_LOUT_L_MIX, 0x00}, - {MAX98090_0x38_RCV_LOUT_L_CNTL, 0x00}, - {MAX98090_0x39_RCV_LOUT_L_VOL, 0x15}, - {MAX98090_0x3A_LOUT_R_MIX, 0x00}, - {MAX98090_0x3B_LOUT_R_CNTL, 0x00}, - {MAX98090_0x3C_LOUT_R_VOL, 0x15}, - - /* JACK DETECT AND ENABLE REGISTERS */ - {MAX98090_0x3D_JACK_DETECT, 0x00}, - {MAX98090_0x3E_IN_ENABLE, 0x00}, - {MAX98090_0x3F_OUT_ENABLE, 0x00}, - {MAX98090_0x40_LVL_CTR, 0x00}, - {MAX98090_0x41_DSP_FILTER_ENABLE, 0x00}, - - /* BIAS AND POWER MODE CONFIGURATION REGISTERS */ - {MAX98090_0x42_BIAS_CTR, 0x00}, - {MAX98090_0x43_DAC_CTR, 0x00}, - {MAX98090_0x44_ADC_CTR, 0x06}, - {MAX98090_0x45_DEV_SHUTDOWN, 0x00}, -}; - -static const unsigned int max98090_hp_tlv[] = { - TLV_DB_RANGE_HEAD(5), - 0x0, 0x6, TLV_DB_SCALE_ITEM(-6700, 400, 0), - 0x7, 0xE, TLV_DB_SCALE_ITEM(-4000, 300, 0), - 0xF, 0x15, TLV_DB_SCALE_ITEM(-1700, 200, 0), - 0x16, 0x1B, TLV_DB_SCALE_ITEM(-400, 100, 0), - 0x1C, 0x1F, TLV_DB_SCALE_ITEM(150, 50, 0), -}; - -static struct snd_kcontrol_new max98090_snd_controls[] = { - SOC_DOUBLE_R_TLV("Headphone Volume", MAX98090_0x2C_L_HP_VOL, - MAX98090_0x2D_R_HP_VOL, 0, 31, 0, max98090_hp_tlv), -}; - -/* Left HeadPhone Mixer Switch */ -static struct snd_kcontrol_new max98090_left_hp_mixer_controls[] = { - SOC_DAPM_SINGLE("DACR Switch", MAX98090_0x29_L_HP_MIX, 1, 1, 0), - SOC_DAPM_SINGLE("DACL Switch", MAX98090_0x29_L_HP_MIX, 0, 1, 0), -}; - -/* Right HeadPhone Mixer Switch */ -static struct snd_kcontrol_new max98090_right_hp_mixer_controls[] = { - SOC_DAPM_SINGLE("DACR Switch", MAX98090_0x2A_R_HP_MIX, 1, 1, 0), - SOC_DAPM_SINGLE("DACL Switch", MAX98090_0x2A_R_HP_MIX, 0, 1, 0), -}; - -static struct snd_soc_dapm_widget max98090_dapm_widgets[] = { - /* Output */ - SND_SOC_DAPM_OUTPUT("HPL"), - SND_SOC_DAPM_OUTPUT("HPR"), - - /* PGA */ - SND_SOC_DAPM_PGA("HPL Out", MAX98090_0x3F_OUT_ENABLE, 7, 0, NULL, 0), - SND_SOC_DAPM_PGA("HPR Out", MAX98090_0x3F_OUT_ENABLE, 6, 0, NULL, 0), - - /* Mixer */ - SND_SOC_DAPM_MIXER("HPL Mixer", SND_SOC_NOPM, 0, 0, - max98090_left_hp_mixer_controls, - ARRAY_SIZE(max98090_left_hp_mixer_controls)), - - SND_SOC_DAPM_MIXER("HPR Mixer", SND_SOC_NOPM, 0, 0, - max98090_right_hp_mixer_controls, - ARRAY_SIZE(max98090_right_hp_mixer_controls)), - - /* DAC */ - SND_SOC_DAPM_DAC("DACL", "Hifi Playback", MAX98090_0x3F_OUT_ENABLE, 0, 0), - SND_SOC_DAPM_DAC("DACR", "Hifi Playback", MAX98090_0x3F_OUT_ENABLE, 1, 0), -}; - -static struct snd_soc_dapm_route max98090_audio_map[] = { - /* Output */ - {"HPL", NULL, "HPL Out"}, - {"HPR", NULL, "HPR Out"}, - - /* PGA */ - {"HPL Out", NULL, "HPL Mixer"}, - {"HPR Out", NULL, "HPR Mixer"}, - - /* Mixer*/ - {"HPL Mixer", "DACR Switch", "DACR"}, - {"HPL Mixer", "DACL Switch", "DACL"}, - - {"HPR Mixer", "DACR Switch", "DACR"}, - {"HPR Mixer", "DACL Switch", "DACL"}, -}; - -static bool max98090_volatile(struct device *dev, unsigned int reg) -{ - if ((reg == MAX98090_0x01_INT_STS) || - (reg == MAX98090_0x02_JACK_STS) || - (reg > MAX98090_REG_MAX_CACHED)) - return true; - - return false; -} - -static int max98090_dai_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params, - struct snd_soc_dai *dai) -{ - struct snd_soc_codec *codec = dai->codec; - unsigned int val; - - switch (params_rate(params)) { - case 96000: - val = 1 << 5; - break; - case 32000: - val = 1 << 4; - break; - case 48000: - val = 1 << 3; - break; - case 44100: - val = 1 << 2; - break; - case 16000: - val = 1 << 1; - break; - case 8000: - val = 1 << 0; - break; - default: - dev_err(codec->dev, "unsupported rate\n"); - return -EINVAL; - } - snd_soc_update_bits(codec, MAX98090_0x05_SAMPLE_RATE, 0x03F, val); - - return 0; -} - -static int max98090_dai_set_sysclk(struct snd_soc_dai *dai, - int clk_id, unsigned int freq, int dir) -{ - struct snd_soc_codec *codec = dai->codec; - unsigned int val; - - snd_soc_update_bits(codec, MAX98090_0x45_DEV_SHUTDOWN, - MAX98090_SHDNRUN, 0); - - switch (freq) { - case 26000000: - val = 1 << 7; - break; - case 19200000: - val = 1 << 6; - break; - case 13000000: - val = 1 << 5; - break; - case 12288000: - val = 1 << 4; - break; - case 12000000: - val = 1 << 3; - break; - case 11289600: - val = 1 << 2; - break; - default: - dev_err(codec->dev, "Invalid master clock frequency\n"); - return -EINVAL; - } - snd_soc_update_bits(codec, MAX98090_0x04_SYS_CLK, 0xFD, val); - - snd_soc_update_bits(codec, MAX98090_0x45_DEV_SHUTDOWN, - MAX98090_SHDNRUN, MAX98090_SHDNRUN); - - dev_dbg(dai->dev, "sysclk is %uHz\n", freq); - - return 0; -} - -static int max98090_dai_set_fmt(struct snd_soc_dai *dai, - unsigned int fmt) -{ - struct snd_soc_codec *codec = dai->codec; - int is_master; - u8 val; - - /* master/slave mode */ - switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: - is_master = 1; - break; - case SND_SOC_DAIFMT_CBS_CFS: - is_master = 0; - break; - default: - dev_err(codec->dev, "unsupported clock\n"); - return -EINVAL; - } - - /* format */ - switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_I2S: - val = (is_master) ? MAX98090_I2S_M : MAX98090_I2S_S; - break; - case SND_SOC_DAIFMT_RIGHT_J: - val = (is_master) ? MAX98090_RJ_M : MAX98090_RJ_S; - break; - case SND_SOC_DAIFMT_LEFT_J: - val = (is_master) ? MAX98090_LJ_M : MAX98090_LJ_S; - break; - default: - dev_err(codec->dev, "unsupported format\n"); - return -EINVAL; - } - snd_soc_update_bits(codec, MAX98090_0x06_DAI_IF, - MAX98090_DAI_IF_MASK, val); - - return 0; -} - -#define MAX98090_RATES SNDRV_PCM_RATE_8000_96000 -#define MAX98090_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE) - -static struct snd_soc_dai_ops max98090_dai_ops = { - .set_sysclk = max98090_dai_set_sysclk, - .set_fmt = max98090_dai_set_fmt, - .hw_params = max98090_dai_hw_params, -}; - -static struct snd_soc_dai_driver max98090_dai = { - .name = "max98090-Hifi", - .playback = { - .stream_name = "Playback", - .channels_min = 1, - .channels_max = 2, - .rates = MAX98090_RATES, - .formats = MAX98090_FORMATS, - }, - .ops = &max98090_dai_ops, -}; - -static int max98090_probe(struct snd_soc_codec *codec) -{ - struct max98090_priv *priv = snd_soc_codec_get_drvdata(codec); - struct device *dev = codec->dev; - int ret; - - codec->control_data = priv->regmap; - ret = snd_soc_codec_set_cache_io(codec, 8, 8, SND_SOC_REGMAP); - if (ret < 0) { - dev_err(dev, "Failed to set cache I/O: %d\n", ret); - return ret; - } - - /* Device active */ - snd_soc_update_bits(codec, MAX98090_0x45_DEV_SHUTDOWN, - MAX98090_SHDNRUN, MAX98090_SHDNRUN); - - return 0; -} - -static int max98090_remove(struct snd_soc_codec *codec) -{ - return 0; -} - -static struct snd_soc_codec_driver soc_codec_dev_max98090 = { - .probe = max98090_probe, - .remove = max98090_remove, - .controls = max98090_snd_controls, - .num_controls = ARRAY_SIZE(max98090_snd_controls), - .dapm_widgets = max98090_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(max98090_dapm_widgets), - .dapm_routes = max98090_audio_map, - .num_dapm_routes = ARRAY_SIZE(max98090_audio_map), -}; - -static const struct regmap_config max98090_regmap = { - .reg_bits = 8, - .val_bits = 8, - .max_register = MAX98090_REG_END, - .volatile_reg = max98090_volatile, - .cache_type = REGCACHE_RBTREE, - .reg_defaults = max98090_reg_defaults, - .num_reg_defaults = ARRAY_SIZE(max98090_reg_defaults), -}; - -static int max98090_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) -{ - struct max98090_priv *priv; - struct device *dev = &i2c->dev; - unsigned int val; - int ret; - - priv = devm_kzalloc(dev, sizeof(struct max98090_priv), - GFP_KERNEL); - if (!priv) - return -ENOMEM; - - priv->regmap = devm_regmap_init_i2c(i2c, &max98090_regmap); - if (IS_ERR(priv->regmap)) { - ret = PTR_ERR(priv->regmap); - dev_err(dev, "Failed to init regmap: %d\n", ret); - return ret; - } - - i2c_set_clientdata(i2c, priv); - - ret = regmap_read(priv->regmap, MAX98090_0xFF_REV_ID, &val); - if (ret < 0) { - dev_err(dev, "Failed to read device revision: %d\n", ret); - return ret; - } - dev_info(dev, "revision 0x%02x\n", val); - - ret = snd_soc_register_codec(dev, - &soc_codec_dev_max98090, - &max98090_dai, 1); - - return ret; -} - -static int max98090_i2c_remove(struct i2c_client *client) -{ - snd_soc_unregister_codec(&client->dev); - return 0; -} - -static const struct i2c_device_id max98090_i2c_id[] = { - { "max98090", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, max98090_i2c_id); - -static struct i2c_driver max98090_i2c_driver = { - .driver = { - .name = "max98090", - .owner = THIS_MODULE, - }, - .probe = max98090_i2c_probe, - .remove = max98090_i2c_remove, - .id_table = max98090_i2c_id, -}; -module_i2c_driver(max98090_i2c_driver); - -MODULE_DESCRIPTION("ALSA SoC MAX98090 driver"); -MODULE_AUTHOR("Peter Hsiang, Kuninori Morimoto"); -MODULE_LICENSE("GPL"); -- cgit v0.10.2 From 685e42154dcf3f6c0a52c115bd15e3d28ad8621b Mon Sep 17 00:00:00 2001 From: Jerry Wong Date: Wed, 6 Feb 2013 11:06:37 -0800 Subject: ASoC: Replace max98090 Device Driver This patch completes the replacement of the existing max98090 driver, by installing a more complete driver. Signed-off-by: Jerry Wong Tested-by: Matthew Mowdy Reviewed-by: Ralph Birt Signed-off-by: Mark Brown diff --git a/include/sound/max98090.h b/include/sound/max98090.h new file mode 100755 index 0000000..95efb13 --- /dev/null +++ b/include/sound/max98090.h @@ -0,0 +1,29 @@ +/* + * Platform data for MAX98090 + * + * Copyright 2011-2012 Maxim Integrated Products + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#ifndef __SOUND_MAX98090_PDATA_H__ +#define __SOUND_MAX98090_PDATA_H__ + +/* codec platform data */ +struct max98090_pdata { + + /* Analog/digital microphone configuration: + * 0 = analog microphone input (normal setting) + * 1 = digital microphone input + */ + unsigned int digmic_left_mode:1; + unsigned int digmic_right_mode:1; + unsigned int digmic_3_mode:1; + unsigned int digmic_4_mode:1; +}; + +#endif diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 0e368d4..3a84782 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -44,6 +44,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_LM4857 if I2C select SND_SOC_LM49453 if I2C select SND_SOC_MAX98088 if I2C + select SND_SOC_MAX98090 if I2C select SND_SOC_MAX98095 if I2C select SND_SOC_MAX9850 if I2C select SND_SOC_MAX9768 if I2C @@ -267,6 +268,9 @@ config SND_SOC_LM49453 config SND_SOC_MAX98088 tristate +config SND_SOC_MAX98090 + tristate + config SND_SOC_MAX98095 tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index aa56312..f6e8e36 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -34,6 +34,7 @@ snd-soc-lm4857-objs := lm4857.o snd-soc-lm49453-objs := lm49453.o snd-soc-max9768-objs := max9768.o snd-soc-max98088-objs := max98088.o +snd-soc-max98090-objs := max98090.o snd-soc-max98095-objs := max98095.o snd-soc-max9850-objs := max9850.o snd-soc-mc13783-objs := mc13783.o @@ -157,6 +158,7 @@ obj-$(CONFIG_SND_SOC_LM4857) += snd-soc-lm4857.o obj-$(CONFIG_SND_SOC_LM49453) += snd-soc-lm49453.o obj-$(CONFIG_SND_SOC_MAX9768) += snd-soc-max9768.o obj-$(CONFIG_SND_SOC_MAX98088) += snd-soc-max98088.o +obj-$(CONFIG_SND_SOC_MAX98090) += snd-soc-max98090.o obj-$(CONFIG_SND_SOC_MAX98095) += snd-soc-max98095.o obj-$(CONFIG_SND_SOC_MAX9850) += snd-soc-max9850.o obj-$(CONFIG_SND_SOC_MC13783) += snd-soc-mc13783.o diff --git a/sound/soc/codecs/max98090.c b/sound/soc/codecs/max98090.c new file mode 100755 index 0000000..fc17604 --- /dev/null +++ b/sound/soc/codecs/max98090.c @@ -0,0 +1,2398 @@ +/* + * max98090.c -- MAX98090 ALSA SoC Audio driver + * + * Copyright 2011-2012 Maxim Integrated Products + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "max98090.h" + +#include + +#define DEBUG +#define EXTMIC_METHOD +#define EXTMIC_METHOD_TEST + +/* Allows for sparsely populated register maps */ +static struct reg_default max98090_reg[] = { + { 0x00, 0x00 }, /* 00 Software Reset */ + { 0x03, 0x04 }, /* 03 Interrupt Masks */ + { 0x04, 0x00 }, /* 04 System Clock Quick */ + { 0x05, 0x00 }, /* 05 Sample Rate Quick */ + { 0x06, 0x00 }, /* 06 DAI Interface Quick */ + { 0x07, 0x00 }, /* 07 DAC Path Quick */ + { 0x08, 0x00 }, /* 08 Mic/Direct to ADC Quick */ + { 0x09, 0x00 }, /* 09 Line to ADC Quick */ + { 0x0A, 0x00 }, /* 0A Analog Mic Loop Quick */ + { 0x0B, 0x00 }, /* 0B Analog Line Loop Quick */ + { 0x0C, 0x00 }, /* 0C Reserved */ + { 0x0D, 0x00 }, /* 0D Input Config */ + { 0x0E, 0x1B }, /* 0E Line Input Level */ + { 0x0F, 0x00 }, /* 0F Line Config */ + + { 0x10, 0x14 }, /* 10 Mic1 Input Level */ + { 0x11, 0x14 }, /* 11 Mic2 Input Level */ + { 0x12, 0x00 }, /* 12 Mic Bias Voltage */ + { 0x13, 0x00 }, /* 13 Digital Mic Config */ + { 0x14, 0x00 }, /* 14 Digital Mic Mode */ + { 0x15, 0x00 }, /* 15 Left ADC Mixer */ + { 0x16, 0x00 }, /* 16 Right ADC Mixer */ + { 0x17, 0x03 }, /* 17 Left ADC Level */ + { 0x18, 0x03 }, /* 18 Right ADC Level */ + { 0x19, 0x00 }, /* 19 ADC Biquad Level */ + { 0x1A, 0x00 }, /* 1A ADC Sidetone */ + { 0x1B, 0x00 }, /* 1B System Clock */ + { 0x1C, 0x00 }, /* 1C Clock Mode */ + { 0x1D, 0x00 }, /* 1D Any Clock 1 */ + { 0x1E, 0x00 }, /* 1E Any Clock 2 */ + { 0x1F, 0x00 }, /* 1F Any Clock 3 */ + + { 0x20, 0x00 }, /* 20 Any Clock 4 */ + { 0x21, 0x00 }, /* 21 Master Mode */ + { 0x22, 0x00 }, /* 22 Interface Format */ + { 0x23, 0x00 }, /* 23 TDM Format 1*/ + { 0x24, 0x00 }, /* 24 TDM Format 2*/ + { 0x25, 0x00 }, /* 25 I/O Configuration */ + { 0x26, 0x80 }, /* 26 Filter Config */ + { 0x27, 0x00 }, /* 27 DAI Playback Level */ + { 0x28, 0x00 }, /* 28 EQ Playback Level */ + { 0x29, 0x00 }, /* 29 Left HP Mixer */ + { 0x2A, 0x00 }, /* 2A Right HP Mixer */ + { 0x2B, 0x00 }, /* 2B HP Control */ + { 0x2C, 0x1A }, /* 2C Left HP Volume */ + { 0x2D, 0x1A }, /* 2D Right HP Volume */ + { 0x2E, 0x00 }, /* 2E Left Spk Mixer */ + { 0x2F, 0x00 }, /* 2F Right Spk Mixer */ + + { 0x30, 0x00 }, /* 30 Spk Control */ + { 0x31, 0x2C }, /* 31 Left Spk Volume */ + { 0x32, 0x2C }, /* 32 Right Spk Volume */ + { 0x33, 0x00 }, /* 33 ALC Timing */ + { 0x34, 0x00 }, /* 34 ALC Compressor */ + { 0x35, 0x00 }, /* 35 ALC Expander */ + { 0x36, 0x00 }, /* 36 ALC Gain */ + { 0x37, 0x00 }, /* 37 Rcv/Line OutL Mixer */ + { 0x38, 0x00 }, /* 38 Rcv/Line OutL Control */ + { 0x39, 0x15 }, /* 39 Rcv/Line OutL Volume */ + { 0x3A, 0x00 }, /* 3A Line OutR Mixer */ + { 0x3B, 0x00 }, /* 3B Line OutR Control */ + { 0x3C, 0x15 }, /* 3C Line OutR Volume */ + { 0x3D, 0x00 }, /* 3D Jack Detect */ + { 0x3E, 0x00 }, /* 3E Input Enable */ + { 0x3F, 0x00 }, /* 3F Output Enable */ + + { 0x40, 0x00 }, /* 40 Level Control */ + { 0x41, 0x00 }, /* 41 DSP Filter Enable */ + { 0x42, 0x00 }, /* 42 Bias Control */ + { 0x43, 0x00 }, /* 43 DAC Control */ + { 0x44, 0x06 }, /* 44 ADC Control */ + { 0x45, 0x00 }, /* 45 Device Shutdown */ + { 0x46, 0x00 }, /* 46 Equalizer Band 1 Coefficient B0 */ + { 0x47, 0x00 }, /* 47 Equalizer Band 1 Coefficient B0 */ + { 0x48, 0x00 }, /* 48 Equalizer Band 1 Coefficient B0 */ + { 0x49, 0x00 }, /* 49 Equalizer Band 1 Coefficient B1 */ + { 0x4A, 0x00 }, /* 4A Equalizer Band 1 Coefficient B1 */ + { 0x4B, 0x00 }, /* 4B Equalizer Band 1 Coefficient B1 */ + { 0x4C, 0x00 }, /* 4C Equalizer Band 1 Coefficient B2 */ + { 0x4D, 0x00 }, /* 4D Equalizer Band 1 Coefficient B2 */ + { 0x4E, 0x00 }, /* 4E Equalizer Band 1 Coefficient B2 */ + { 0x4F, 0x00 }, /* 4F Equalizer Band 1 Coefficient A1 */ + + { 0x50, 0x00 }, /* 50 Equalizer Band 1 Coefficient A1 */ + { 0x51, 0x00 }, /* 51 Equalizer Band 1 Coefficient A1 */ + { 0x52, 0x00 }, /* 52 Equalizer Band 1 Coefficient A2 */ + { 0x53, 0x00 }, /* 53 Equalizer Band 1 Coefficient A2 */ + { 0x54, 0x00 }, /* 54 Equalizer Band 1 Coefficient A2 */ + { 0x55, 0x00 }, /* 55 Equalizer Band 2 Coefficient B0 */ + { 0x56, 0x00 }, /* 56 Equalizer Band 2 Coefficient B0 */ + { 0x57, 0x00 }, /* 57 Equalizer Band 2 Coefficient B0 */ + { 0x58, 0x00 }, /* 58 Equalizer Band 2 Coefficient B1 */ + { 0x59, 0x00 }, /* 59 Equalizer Band 2 Coefficient B1 */ + { 0x5A, 0x00 }, /* 5A Equalizer Band 2 Coefficient B1 */ + { 0x5B, 0x00 }, /* 5B Equalizer Band 2 Coefficient B2 */ + { 0x5C, 0x00 }, /* 5C Equalizer Band 2 Coefficient B2 */ + { 0x5D, 0x00 }, /* 5D Equalizer Band 2 Coefficient B2 */ + { 0x5E, 0x00 }, /* 5E Equalizer Band 2 Coefficient A1 */ + { 0x5F, 0x00 }, /* 5F Equalizer Band 2 Coefficient A1 */ + + { 0x60, 0x00 }, /* 60 Equalizer Band 2 Coefficient A1 */ + { 0x61, 0x00 }, /* 61 Equalizer Band 2 Coefficient A2 */ + { 0x62, 0x00 }, /* 62 Equalizer Band 2 Coefficient A2 */ + { 0x63, 0x00 }, /* 63 Equalizer Band 2 Coefficient A2 */ + { 0x64, 0x00 }, /* 64 Equalizer Band 3 Coefficient B0 */ + { 0x65, 0x00 }, /* 65 Equalizer Band 3 Coefficient B0 */ + { 0x66, 0x00 }, /* 66 Equalizer Band 3 Coefficient B0 */ + { 0x67, 0x00 }, /* 67 Equalizer Band 3 Coefficient B1 */ + { 0x68, 0x00 }, /* 68 Equalizer Band 3 Coefficient B1 */ + { 0x69, 0x00 }, /* 69 Equalizer Band 3 Coefficient B1 */ + { 0x6A, 0x00 }, /* 6A Equalizer Band 3 Coefficient B2 */ + { 0x6B, 0x00 }, /* 6B Equalizer Band 3 Coefficient B2 */ + { 0x6C, 0x00 }, /* 6C Equalizer Band 3 Coefficient B2 */ + { 0x6D, 0x00 }, /* 6D Equalizer Band 3 Coefficient A1 */ + { 0x6E, 0x00 }, /* 6E Equalizer Band 3 Coefficient A1 */ + { 0x6F, 0x00 }, /* 6F Equalizer Band 3 Coefficient A1 */ + + { 0x70, 0x00 }, /* 70 Equalizer Band 3 Coefficient A2 */ + { 0x71, 0x00 }, /* 71 Equalizer Band 3 Coefficient A2 */ + { 0x72, 0x00 }, /* 72 Equalizer Band 3 Coefficient A2 */ + { 0x73, 0x00 }, /* 73 Equalizer Band 4 Coefficient B0 */ + { 0x74, 0x00 }, /* 74 Equalizer Band 4 Coefficient B0 */ + { 0x75, 0x00 }, /* 75 Equalizer Band 4 Coefficient B0 */ + { 0x76, 0x00 }, /* 76 Equalizer Band 4 Coefficient B1 */ + { 0x77, 0x00 }, /* 77 Equalizer Band 4 Coefficient B1 */ + { 0x78, 0x00 }, /* 78 Equalizer Band 4 Coefficient B1 */ + { 0x79, 0x00 }, /* 79 Equalizer Band 4 Coefficient B2 */ + { 0x7A, 0x00 }, /* 7A Equalizer Band 4 Coefficient B2 */ + { 0x7B, 0x00 }, /* 7B Equalizer Band 4 Coefficient B2 */ + { 0x7C, 0x00 }, /* 7C Equalizer Band 4 Coefficient A1 */ + { 0x7D, 0x00 }, /* 7D Equalizer Band 4 Coefficient A1 */ + { 0x7E, 0x00 }, /* 7E Equalizer Band 4 Coefficient A1 */ + { 0x7F, 0x00 }, /* 7F Equalizer Band 4 Coefficient A2 */ + + { 0x80, 0x00 }, /* 80 Equalizer Band 4 Coefficient A2 */ + { 0x81, 0x00 }, /* 81 Equalizer Band 4 Coefficient A2 */ + { 0x82, 0x00 }, /* 82 Equalizer Band 5 Coefficient B0 */ + { 0x83, 0x00 }, /* 83 Equalizer Band 5 Coefficient B0 */ + { 0x84, 0x00 }, /* 84 Equalizer Band 5 Coefficient B0 */ + { 0x85, 0x00 }, /* 85 Equalizer Band 5 Coefficient B1 */ + { 0x86, 0x00 }, /* 86 Equalizer Band 5 Coefficient B1 */ + { 0x87, 0x00 }, /* 87 Equalizer Band 5 Coefficient B1 */ + { 0x88, 0x00 }, /* 88 Equalizer Band 5 Coefficient B2 */ + { 0x89, 0x00 }, /* 89 Equalizer Band 5 Coefficient B2 */ + { 0x8A, 0x00 }, /* 8A Equalizer Band 5 Coefficient B2 */ + { 0x8B, 0x00 }, /* 8B Equalizer Band 5 Coefficient A1 */ + { 0x8C, 0x00 }, /* 8C Equalizer Band 5 Coefficient A1 */ + { 0x8D, 0x00 }, /* 8D Equalizer Band 5 Coefficient A1 */ + { 0x8E, 0x00 }, /* 8E Equalizer Band 5 Coefficient A2 */ + { 0x8F, 0x00 }, /* 8F Equalizer Band 5 Coefficient A2 */ + + { 0x90, 0x00 }, /* 90 Equalizer Band 5 Coefficient A2 */ + { 0x91, 0x00 }, /* 91 Equalizer Band 6 Coefficient B0 */ + { 0x92, 0x00 }, /* 92 Equalizer Band 6 Coefficient B0 */ + { 0x93, 0x00 }, /* 93 Equalizer Band 6 Coefficient B0 */ + { 0x94, 0x00 }, /* 94 Equalizer Band 6 Coefficient B1 */ + { 0x95, 0x00 }, /* 95 Equalizer Band 6 Coefficient B1 */ + { 0x96, 0x00 }, /* 96 Equalizer Band 6 Coefficient B1 */ + { 0x97, 0x00 }, /* 97 Equalizer Band 6 Coefficient B2 */ + { 0x98, 0x00 }, /* 98 Equalizer Band 6 Coefficient B2 */ + { 0x99, 0x00 }, /* 99 Equalizer Band 6 Coefficient B2 */ + { 0x9A, 0x00 }, /* 9A Equalizer Band 6 Coefficient A1 */ + { 0x9B, 0x00 }, /* 9B Equalizer Band 6 Coefficient A1 */ + { 0x9C, 0x00 }, /* 9C Equalizer Band 6 Coefficient A1 */ + { 0x9D, 0x00 }, /* 9D Equalizer Band 6 Coefficient A2 */ + { 0x9E, 0x00 }, /* 9E Equalizer Band 6 Coefficient A2 */ + { 0x9F, 0x00 }, /* 9F Equalizer Band 6 Coefficient A2 */ + + { 0xA0, 0x00 }, /* A0 Equalizer Band 7 Coefficient B0 */ + { 0xA1, 0x00 }, /* A1 Equalizer Band 7 Coefficient B0 */ + { 0xA2, 0x00 }, /* A2 Equalizer Band 7 Coefficient B0 */ + { 0xA3, 0x00 }, /* A3 Equalizer Band 7 Coefficient B1 */ + { 0xA4, 0x00 }, /* A4 Equalizer Band 7 Coefficient B1 */ + { 0xA5, 0x00 }, /* A5 Equalizer Band 7 Coefficient B1 */ + { 0xA6, 0x00 }, /* A6 Equalizer Band 7 Coefficient B2 */ + { 0xA7, 0x00 }, /* A7 Equalizer Band 7 Coefficient B2 */ + { 0xA8, 0x00 }, /* A8 Equalizer Band 7 Coefficient B2 */ + { 0xA9, 0x00 }, /* A9 Equalizer Band 7 Coefficient A1 */ + { 0xAA, 0x00 }, /* AA Equalizer Band 7 Coefficient A1 */ + { 0xAB, 0x00 }, /* AB Equalizer Band 7 Coefficient A1 */ + { 0xAC, 0x00 }, /* AC Equalizer Band 7 Coefficient A2 */ + { 0xAD, 0x00 }, /* AD Equalizer Band 7 Coefficient A2 */ + { 0xAE, 0x00 }, /* AE Equalizer Band 7 Coefficient A2 */ + { 0xAF, 0x00 }, /* AF ADC Biquad Coefficient B0 */ + + { 0xB0, 0x00 }, /* B0 ADC Biquad Coefficient B0 */ + { 0xB1, 0x00 }, /* B1 ADC Biquad Coefficient B0 */ + { 0xB2, 0x00 }, /* B2 ADC Biquad Coefficient B1 */ + { 0xB3, 0x00 }, /* B3 ADC Biquad Coefficient B1 */ + { 0xB4, 0x00 }, /* B4 ADC Biquad Coefficient B1 */ + { 0xB5, 0x00 }, /* B5 ADC Biquad Coefficient B2 */ + { 0xB6, 0x00 }, /* B6 ADC Biquad Coefficient B2 */ + { 0xB7, 0x00 }, /* B7 ADC Biquad Coefficient B2 */ + { 0xB8, 0x00 }, /* B8 ADC Biquad Coefficient A1 */ + { 0xB9, 0x00 }, /* B9 ADC Biquad Coefficient A1 */ + { 0xBA, 0x00 }, /* BA ADC Biquad Coefficient A1 */ + { 0xBB, 0x00 }, /* BB ADC Biquad Coefficient A2 */ + { 0xBC, 0x00 }, /* BC ADC Biquad Coefficient A2 */ + { 0xBD, 0x00 }, /* BD ADC Biquad Coefficient A2 */ + { 0xBE, 0x00 }, /* BE Digital Mic 3 Volume */ + { 0xBF, 0x00 }, /* BF Digital Mic 4 Volume */ + + { 0xC0, 0x00 }, /* C0 Digital Mic 34 Biquad Pre Atten */ + { 0xC1, 0x00 }, /* C1 Record TDM Slot */ + { 0xC2, 0x00 }, /* C2 Sample Rate */ + { 0xC3, 0x00 }, /* C3 Digital Mic 34 Biquad Coefficient C3 */ + { 0xC4, 0x00 }, /* C4 Digital Mic 34 Biquad Coefficient C4 */ + { 0xC5, 0x00 }, /* C5 Digital Mic 34 Biquad Coefficient C5 */ + { 0xC6, 0x00 }, /* C6 Digital Mic 34 Biquad Coefficient C6 */ + { 0xC7, 0x00 }, /* C7 Digital Mic 34 Biquad Coefficient C7 */ + { 0xC8, 0x00 }, /* C8 Digital Mic 34 Biquad Coefficient C8 */ + { 0xC9, 0x00 }, /* C9 Digital Mic 34 Biquad Coefficient C9 */ + { 0xCA, 0x00 }, /* CA Digital Mic 34 Biquad Coefficient CA */ + { 0xCB, 0x00 }, /* CB Digital Mic 34 Biquad Coefficient CB */ + { 0xCC, 0x00 }, /* CC Digital Mic 34 Biquad Coefficient CC */ + { 0xCD, 0x00 }, /* CD Digital Mic 34 Biquad Coefficient CD */ + { 0xCE, 0x00 }, /* CE Digital Mic 34 Biquad Coefficient CE */ + { 0xCF, 0x00 }, /* CF Digital Mic 34 Biquad Coefficient CF */ + + { 0xD0, 0x00 }, /* D0 Digital Mic 34 Biquad Coefficient D0 */ + { 0xD1, 0x00 }, /* D1 Digital Mic 34 Biquad Coefficient D1 */ +}; + +static bool max98090_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case M98090_REG_DEVICE_STATUS: + case M98090_REG_JACK_STATUS: + case M98090_REG_REVISION_ID: + return true; + default: + return false; + } +} + +static bool max98090_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case M98090_REG_DEVICE_STATUS: + case M98090_REG_JACK_STATUS: + case M98090_REG_INTERRUPT_S: + case M98090_REG_RESERVED: + case M98090_REG_LINE_INPUT_CONFIG: + case M98090_REG_LINE_INPUT_LEVEL: + case M98090_REG_INPUT_MODE: + case M98090_REG_MIC1_INPUT_LEVEL: + case M98090_REG_MIC2_INPUT_LEVEL: + case M98090_REG_MIC_BIAS_VOLTAGE: + case M98090_REG_DIGITAL_MIC_ENABLE: + case M98090_REG_DIGITAL_MIC_CONFIG: + case M98090_REG_LEFT_ADC_MIXER: + case M98090_REG_RIGHT_ADC_MIXER: + case M98090_REG_LEFT_ADC_LEVEL: + case M98090_REG_RIGHT_ADC_LEVEL: + case M98090_REG_ADC_BIQUAD_LEVEL: + case M98090_REG_ADC_SIDETONE: + case M98090_REG_SYSTEM_CLOCK: + case M98090_REG_CLOCK_MODE: + case M98090_REG_CLOCK_RATIO_NI_MSB: + case M98090_REG_CLOCK_RATIO_NI_LSB: + case M98090_REG_CLOCK_RATIO_MI_MSB: + case M98090_REG_CLOCK_RATIO_MI_LSB: + case M98090_REG_MASTER_MODE: + case M98090_REG_INTERFACE_FORMAT: + case M98090_REG_TDM_CONTROL: + case M98090_REG_TDM_FORMAT: + case M98090_REG_IO_CONFIGURATION: + case M98090_REG_FILTER_CONFIG: + case M98090_REG_DAI_PLAYBACK_LEVEL: + case M98090_REG_DAI_PLAYBACK_LEVEL_EQ: + case M98090_REG_LEFT_HP_MIXER: + case M98090_REG_RIGHT_HP_MIXER: + case M98090_REG_HP_CONTROL: + case M98090_REG_LEFT_HP_VOLUME: + case M98090_REG_RIGHT_HP_VOLUME: + case M98090_REG_LEFT_SPK_MIXER: + case M98090_REG_RIGHT_SPK_MIXER: + case M98090_REG_SPK_CONTROL: + case M98090_REG_LEFT_SPK_VOLUME: + case M98090_REG_RIGHT_SPK_VOLUME: + case M98090_REG_DRC_TIMING: + case M98090_REG_DRC_COMPRESSOR: + case M98090_REG_DRC_EXPANDER: + case M98090_REG_DRC_GAIN: + case M98090_REG_RCV_LOUTL_MIXER: + case M98090_REG_RCV_LOUTL_CONTROL: + case M98090_REG_RCV_LOUTL_VOLUME: + case M98090_REG_LOUTR_MIXER: + case M98090_REG_LOUTR_CONTROL: + case M98090_REG_LOUTR_VOLUME: + case M98090_REG_JACK_DETECT: + case M98090_REG_INPUT_ENABLE: + case M98090_REG_OUTPUT_ENABLE: + case M98090_REG_LEVEL_CONTROL: + case M98090_REG_DSP_FILTER_ENABLE: + case M98090_REG_BIAS_CONTROL: + case M98090_REG_DAC_CONTROL: + case M98090_REG_ADC_CONTROL: + case M98090_REG_DEVICE_SHUTDOWN: + case M98090_REG_EQUALIZER_BASE ... M98090_REG_EQUALIZER_BASE + 0x68: + case M98090_REG_RECORD_BIQUAD_BASE ... M98090_REG_RECORD_BIQUAD_BASE + 0x0E: + case M98090_REG_DMIC3_VOLUME: + case M98090_REG_DMIC4_VOLUME: + case M98090_REG_DMIC34_BQ_PREATTEN: + case M98090_REG_RECORD_TDM_SLOT: + case M98090_REG_SAMPLE_RATE: + case M98090_REG_DMIC34_BIQUAD_BASE ... M98090_REG_DMIC34_BIQUAD_BASE + 0x0E: + return true; + default: + return false; + } +} + +static int max98090_reset(struct max98090_priv *max98090) +{ + int ret; + + /* Reset the codec by writing to this write-only reset register */ + ret = regmap_write(max98090->regmap, M98090_REG_SOFTWARE_RESET, + M98090_SWRESET_MASK); + if (ret < 0) { + dev_err(max98090->codec->dev, + "Failed to reset codec: %d\n", ret); + return ret; + } + + msleep(20); + return ret; +} + +static const unsigned int max98090_micboost_tlv[] = { + TLV_DB_RANGE_HEAD(2), + 0, 1, TLV_DB_SCALE_ITEM(0, 2000, 0), + 2, 2, TLV_DB_SCALE_ITEM(3000, 0, 0), +}; + +static const DECLARE_TLV_DB_SCALE(max98090_mic_tlv, 0, 100, 0); + +static const DECLARE_TLV_DB_SCALE(max98090_line_single_ended_tlv, + -600, 600, 0); + +static const unsigned int max98090_line_tlv[] = { + TLV_DB_RANGE_HEAD(2), + 0, 3, TLV_DB_SCALE_ITEM(-600, 300, 0), + 4, 5, TLV_DB_SCALE_ITEM(1400, 600, 0), +}; + +static const DECLARE_TLV_DB_SCALE(max98090_avg_tlv, 0, 600, 0); +static const DECLARE_TLV_DB_SCALE(max98090_av_tlv, -1200, 100, 0); + +static const DECLARE_TLV_DB_SCALE(max98090_dvg_tlv, 0, 600, 0); +static const DECLARE_TLV_DB_SCALE(max98090_dv_tlv, -1500, 100, 0); + +static const DECLARE_TLV_DB_SCALE(max98090_sidetone_tlv, -6050, 200, 0); + +static const DECLARE_TLV_DB_SCALE(max98090_alc_tlv, -1500, 100, 0); +static const DECLARE_TLV_DB_SCALE(max98090_alcmakeup_tlv, 0, 100, 0); +static const DECLARE_TLV_DB_SCALE(max98090_alccomp_tlv, -3100, 100, 0); +static const DECLARE_TLV_DB_SCALE(max98090_drcexp_tlv, -6600, 100, 0); + +static const unsigned int max98090_mixout_tlv[] = { + TLV_DB_RANGE_HEAD(2), + 0, 1, TLV_DB_SCALE_ITEM(-1200, 250, 0), + 2, 3, TLV_DB_SCALE_ITEM(-600, 600, 0), +}; + +static const unsigned int max98090_hp_tlv[] = { + TLV_DB_RANGE_HEAD(5), + 0, 6, TLV_DB_SCALE_ITEM(-6700, 400, 0), + 7, 14, TLV_DB_SCALE_ITEM(-4000, 300, 0), + 15, 21, TLV_DB_SCALE_ITEM(-1700, 200, 0), + 22, 27, TLV_DB_SCALE_ITEM(-400, 100, 0), + 28, 31, TLV_DB_SCALE_ITEM(150, 50, 0), +}; + +static const unsigned int max98090_spk_tlv[] = { + TLV_DB_RANGE_HEAD(5), + 0, 4, TLV_DB_SCALE_ITEM(-4800, 400, 0), + 5, 10, TLV_DB_SCALE_ITEM(-2900, 300, 0), + 11, 14, TLV_DB_SCALE_ITEM(-1200, 200, 0), + 15, 29, TLV_DB_SCALE_ITEM(-500, 100, 0), + 30, 39, TLV_DB_SCALE_ITEM(950, 50, 0), +}; + +static const unsigned int max98090_rcv_lout_tlv[] = { + TLV_DB_RANGE_HEAD(5), + 0, 6, TLV_DB_SCALE_ITEM(-6200, 400, 0), + 7, 14, TLV_DB_SCALE_ITEM(-3500, 300, 0), + 15, 21, TLV_DB_SCALE_ITEM(-1200, 200, 0), + 22, 27, TLV_DB_SCALE_ITEM(100, 100, 0), + 28, 31, TLV_DB_SCALE_ITEM(650, 50, 0), +}; + +static int max98090_get_enab_tlv(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + unsigned int mask = (1 << fls(mc->max)) - 1; + unsigned int val = snd_soc_read(codec, mc->reg); + unsigned int *select; + + switch (mc->reg) { + case M98090_REG_MIC1_INPUT_LEVEL: + select = &(max98090->pa1en); + break; + case M98090_REG_MIC2_INPUT_LEVEL: + select = &(max98090->pa2en); + break; + case M98090_REG_ADC_SIDETONE: + select = &(max98090->sidetone); + break; + default: + return -EINVAL; + } + + val = (val >> mc->shift) & mask; + + if (val >= 1) { + /* If on, return the volume */ + val = val - 1; + *select = val; + } else { + /* If off, return last stored value */ + val = *select; + } + + ucontrol->value.integer.value[0] = val; + return 0; +} + +static int max98090_put_enab_tlv(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + unsigned int mask = (1 << fls(mc->max)) - 1; + unsigned int sel = ucontrol->value.integer.value[0]; + unsigned int val = snd_soc_read(codec, mc->reg); + unsigned int *select; + + switch (mc->reg) { + case M98090_REG_MIC1_INPUT_LEVEL: + select = &(max98090->pa1en); + break; + case M98090_REG_MIC2_INPUT_LEVEL: + select = &(max98090->pa2en); + break; + case M98090_REG_ADC_SIDETONE: + select = &(max98090->sidetone); + break; + default: + return -EINVAL; + } + + val = (val >> mc->shift) & mask; + + *select = sel; + + /* Setting a volume is only valid if it is already On */ + if (val >= 1) { + sel = sel + 1; + } else { + /* Write what was already there */ + sel = val; + } + + snd_soc_update_bits(codec, mc->reg, + mask << mc->shift, + sel << mc->shift); + + return 0; +} + +static const char * max98090_perf_pwr_text[] = + { "High Performance", "Low Power" }; +static const char * max98090_pwr_perf_text[] = + { "Low Power", "High Performance" }; + +static const struct soc_enum max98090_vcmbandgap_enum = + SOC_ENUM_SINGLE(M98090_REG_BIAS_CONTROL, M98090_VCM_MODE_SHIFT, + ARRAY_SIZE(max98090_pwr_perf_text), max98090_pwr_perf_text); + +static const char * max98090_osr128_text[] = { "64*fs", "128*fs" }; + +static const struct soc_enum max98090_osr128_enum = + SOC_ENUM_SINGLE(M98090_REG_ADC_CONTROL, M98090_OSR128_SHIFT, + ARRAY_SIZE(max98090_osr128_text), max98090_osr128_text); + +static const char *max98090_mode_text[] = { "Voice", "Music" }; + +static const struct soc_enum max98090_mode_enum = + SOC_ENUM_SINGLE(M98090_REG_FILTER_CONFIG, M98090_MODE_SHIFT, + ARRAY_SIZE(max98090_mode_text), max98090_mode_text); + +static const struct soc_enum max98090_filter_dmic34mode_enum = + SOC_ENUM_SINGLE(M98090_REG_FILTER_CONFIG, + M98090_FLT_DMIC34MODE_SHIFT, + ARRAY_SIZE(max98090_mode_text), max98090_mode_text); + +static const char * max98090_drcatk_text[] = + { "0.5ms", "1ms", "5ms", "10ms", "25ms", "50ms", "100ms", "200ms" }; + +static const struct soc_enum max98090_drcatk_enum = + SOC_ENUM_SINGLE(M98090_REG_DRC_TIMING, M98090_DRCATK_SHIFT, + ARRAY_SIZE(max98090_drcatk_text), max98090_drcatk_text); + +static const char * max98090_drcrls_text[] = + { "8s", "4s", "2s", "1s", "0.5s", "0.25s", "0.125s", "0.0625s" }; + +static const struct soc_enum max98090_drcrls_enum = + SOC_ENUM_SINGLE(M98090_REG_DRC_TIMING, M98090_DRCRLS_SHIFT, + ARRAY_SIZE(max98090_drcrls_text), max98090_drcrls_text); + +static const char * max98090_alccmp_text[] = + { "1:1", "1:1.5", "1:2", "1:4", "1:INF" }; + +static const struct soc_enum max98090_alccmp_enum = + SOC_ENUM_SINGLE(M98090_REG_DRC_COMPRESSOR, M98090_DRCCMP_SHIFT, + ARRAY_SIZE(max98090_alccmp_text), max98090_alccmp_text); + +static const char * max98090_drcexp_text[] = { "1:1", "2:1", "3:1" }; + +static const struct soc_enum max98090_drcexp_enum = + SOC_ENUM_SINGLE(M98090_REG_DRC_EXPANDER, M98090_DRCEXP_SHIFT, + ARRAY_SIZE(max98090_drcexp_text), max98090_drcexp_text); + +static const struct soc_enum max98090_dac_perfmode_enum = + SOC_ENUM_SINGLE(M98090_REG_DAC_CONTROL, M98090_PERFMODE_SHIFT, + ARRAY_SIZE(max98090_perf_pwr_text), max98090_perf_pwr_text); + +static const struct soc_enum max98090_dachp_enum = + SOC_ENUM_SINGLE(M98090_REG_DAC_CONTROL, M98090_DACHP_SHIFT, + ARRAY_SIZE(max98090_pwr_perf_text), max98090_pwr_perf_text); + +static const struct soc_enum max98090_adchp_enum = + SOC_ENUM_SINGLE(M98090_REG_ADC_CONTROL, M98090_ADCHP_SHIFT, + ARRAY_SIZE(max98090_pwr_perf_text), max98090_pwr_perf_text); + +static const struct snd_kcontrol_new max98090_snd_controls[] = { + SOC_ENUM("MIC Bias VCM Bandgap", max98090_vcmbandgap_enum), + + SOC_SINGLE("DMIC MIC Comp Filter Config", M98090_REG_DIGITAL_MIC_CONFIG, + M98090_DMIC_COMP_SHIFT, M98090_DMIC_COMP_NUM - 1, 0), + + SOC_SINGLE_EXT_TLV("MIC1 Boost Volume", + M98090_REG_MIC1_INPUT_LEVEL, M98090_MIC_PA1EN_SHIFT, + M98090_MIC_PA1EN_NUM - 1, 0, max98090_get_enab_tlv, + max98090_put_enab_tlv, max98090_micboost_tlv), + + SOC_SINGLE_EXT_TLV("MIC2 Boost Volume", + M98090_REG_MIC2_INPUT_LEVEL, M98090_MIC_PA2EN_SHIFT, + M98090_MIC_PA2EN_NUM - 1, 0, max98090_get_enab_tlv, + max98090_put_enab_tlv, max98090_micboost_tlv), + + SOC_SINGLE_TLV("MIC1 Volume", M98090_REG_MIC1_INPUT_LEVEL, + M98090_MIC_PGAM1_SHIFT, M98090_MIC_PGAM1_NUM - 1, 1, + max98090_mic_tlv), + + SOC_SINGLE_TLV("MIC2 Volume", M98090_REG_MIC2_INPUT_LEVEL, + M98090_MIC_PGAM2_SHIFT, M98090_MIC_PGAM2_NUM - 1, 1, + max98090_mic_tlv), + + SOC_SINGLE_RANGE_TLV("LINEA Single Ended Volume", + M98090_REG_LINE_INPUT_LEVEL, M98090_MIXG135_SHIFT, 0, + M98090_MIXG135_NUM - 1, 1, max98090_line_single_ended_tlv), + + SOC_SINGLE_RANGE_TLV("LINEB Single Ended Volume", + M98090_REG_LINE_INPUT_LEVEL, M98090_MIXG246_SHIFT, 0, + M98090_MIXG246_NUM - 1, 1, max98090_line_single_ended_tlv), + + SOC_SINGLE_RANGE_TLV("LINEA Volume", M98090_REG_LINE_INPUT_LEVEL, + M98090_LINAPGA_SHIFT, 0, M98090_LINAPGA_NUM - 1, 1, + max98090_line_tlv), + + SOC_SINGLE_RANGE_TLV("LINEB Volume", M98090_REG_LINE_INPUT_LEVEL, + M98090_LINBPGA_SHIFT, 0, M98090_LINBPGA_NUM - 1, 1, + max98090_line_tlv), + + SOC_SINGLE("LINEA Ext Resistor Gain Mode", M98090_REG_INPUT_MODE, + M98090_EXTBUFA_SHIFT, M98090_EXTBUFA_NUM - 1, 0), + SOC_SINGLE("LINEB Ext Resistor Gain Mode", M98090_REG_INPUT_MODE, + M98090_EXTBUFB_SHIFT, M98090_EXTBUFB_NUM - 1, 0), + + SOC_SINGLE_TLV("ADCL Boost Volume", M98090_REG_LEFT_ADC_LEVEL, + M98090_AVLG_SHIFT, M98090_AVLG_NUM - 1, 0, + max98090_avg_tlv), + SOC_SINGLE_TLV("ADCR Boost Volume", M98090_REG_RIGHT_ADC_LEVEL, + M98090_AVRG_SHIFT, M98090_AVLG_NUM - 1, 0, + max98090_avg_tlv), + + SOC_SINGLE_TLV("ADCL Volume", M98090_REG_LEFT_ADC_LEVEL, + M98090_AVL_SHIFT, M98090_AVL_NUM - 1, 1, + max98090_av_tlv), + SOC_SINGLE_TLV("ADCR Volume", M98090_REG_RIGHT_ADC_LEVEL, + M98090_AVR_SHIFT, M98090_AVR_NUM - 1, 1, + max98090_av_tlv), + + SOC_ENUM("ADC Oversampling Rate", max98090_osr128_enum), + SOC_SINGLE("ADC Quantizer Dither", M98090_REG_ADC_CONTROL, + M98090_ADCDITHER_SHIFT, M98090_ADCDITHER_NUM - 1, 0), + SOC_ENUM("ADC High Performance Mode", max98090_adchp_enum), + + SOC_SINGLE("DAC Mono Mode", M98090_REG_IO_CONFIGURATION, + M98090_DMONO_SHIFT, M98090_DMONO_NUM - 1, 0), + SOC_SINGLE("SDIN Mode", M98090_REG_IO_CONFIGURATION, + M98090_SDIEN_SHIFT, M98090_SDIEN_NUM - 1, 0), + SOC_SINGLE("SDOUT Mode", M98090_REG_IO_CONFIGURATION, + M98090_SDOEN_SHIFT, M98090_SDOEN_NUM - 1, 0), + SOC_SINGLE("SDOUT Hi-Z Mode", M98090_REG_IO_CONFIGURATION, + M98090_HIZOFF_SHIFT, M98090_HIZOFF_NUM - 1, 1), + SOC_ENUM("Filter Mode", max98090_mode_enum), + SOC_SINGLE("Record Path DC Blocking", M98090_REG_FILTER_CONFIG, + M98090_AHPF_SHIFT, M98090_AHPF_NUM - 1, 0), + SOC_SINGLE("Playback Path DC Blocking", M98090_REG_FILTER_CONFIG, + M98090_DHPF_SHIFT, M98090_DHPF_NUM - 1, 0), + SOC_SINGLE_TLV("Digital BQ Volume", M98090_REG_ADC_BIQUAD_LEVEL, + M98090_AVBQ_SHIFT, M98090_AVBQ_NUM - 1, 1, max98090_dv_tlv), + SOC_SINGLE_EXT_TLV("Digital Sidetone Volume", + M98090_REG_ADC_SIDETONE, M98090_DVST_SHIFT, + M98090_DVST_NUM - 1, 1, max98090_get_enab_tlv, + max98090_put_enab_tlv, max98090_micboost_tlv), + SOC_SINGLE_TLV("Digital Coarse Volume", M98090_REG_DAI_PLAYBACK_LEVEL, + M98090_DVG_SHIFT, M98090_DVG_NUM - 1, 0, + max98090_dvg_tlv), + SOC_SINGLE_TLV("Digital Volume", M98090_REG_DAI_PLAYBACK_LEVEL, + M98090_DV_SHIFT, M98090_DV_NUM - 1, 1, + max98090_dv_tlv), + SND_SOC_BYTES("EQ Coefficients", M98090_REG_EQUALIZER_BASE, 105), + SOC_SINGLE("Digital EQ 3 Band Switch", M98090_REG_DSP_FILTER_ENABLE, + M98090_EQ3BANDEN_SHIFT, M98090_EQ3BANDEN_NUM - 1, 0), + SOC_SINGLE("Digital EQ 5 Band Switch", M98090_REG_DSP_FILTER_ENABLE, + M98090_EQ5BANDEN_SHIFT, M98090_EQ5BANDEN_NUM - 1, 0), + SOC_SINGLE("Digital EQ 7 Band Switch", M98090_REG_DSP_FILTER_ENABLE, + M98090_EQ7BANDEN_SHIFT, M98090_EQ7BANDEN_NUM - 1, 0), + SOC_SINGLE("Digital EQ Clipping Detection", M98090_REG_DAI_PLAYBACK_LEVEL_EQ, + M98090_EQCLPN_SHIFT, M98090_EQCLPN_NUM - 1, + 1), + SOC_SINGLE_TLV("Digital EQ Volume", M98090_REG_DAI_PLAYBACK_LEVEL_EQ, + M98090_DVEQ_SHIFT, M98090_DVEQ_NUM - 1, 1, + max98090_dv_tlv), + + SOC_SINGLE("ALC Enable", M98090_REG_DRC_TIMING, + M98090_DRCEN_SHIFT, M98090_DRCEN_NUM - 1, 0), + SOC_ENUM("ALC Attack Time", max98090_drcatk_enum), + SOC_ENUM("ALC Release Time", max98090_drcrls_enum), + SOC_SINGLE_TLV("ALC Make Up Volume", M98090_REG_DRC_GAIN, + M98090_DRCG_SHIFT, M98090_DRCG_NUM - 1, 0, + max98090_alcmakeup_tlv), + SOC_ENUM("ALC Compression Ratio", max98090_alccmp_enum), + SOC_ENUM("ALC Expansion Ratio", max98090_drcexp_enum), + SOC_SINGLE_TLV("ALC Compression Threshold Volume", + M98090_REG_DRC_COMPRESSOR, M98090_DRCTHC_SHIFT, + M98090_DRCTHC_NUM - 1, 1, max98090_alccomp_tlv), + SOC_SINGLE_TLV("ALC Expansion Threshold Volume", + M98090_REG_DRC_EXPANDER, M98090_DRCTHE_SHIFT, + M98090_DRCTHE_NUM - 1, 1, max98090_drcexp_tlv), + + SOC_ENUM("DAC HP Playback Performance Mode", + max98090_dac_perfmode_enum), + SOC_ENUM("DAC High Performance Mode", max98090_dachp_enum), + + SOC_SINGLE_TLV("Headphone Left Mixer Volume", + M98090_REG_HP_CONTROL, M98090_MIXHPLG_SHIFT, + M98090_MIXHPLG_NUM - 1, 1, max98090_mixout_tlv), + SOC_SINGLE_TLV("Headphone Right Mixer Volume", + M98090_REG_HP_CONTROL, M98090_MIXHPRG_SHIFT, + M98090_MIXHPRG_NUM - 1, 1, max98090_mixout_tlv), + + SOC_SINGLE_TLV("Speaker Left Mixer Volume", + M98090_REG_SPK_CONTROL, M98090_MIXSPLG_SHIFT, + M98090_MIXSPLG_NUM - 1, 1, max98090_mixout_tlv), + SOC_SINGLE_TLV("Speaker Right Mixer Volume", + M98090_REG_SPK_CONTROL, M98090_MIXSPRG_SHIFT, + M98090_MIXSPRG_NUM - 1, 1, max98090_mixout_tlv), + + SOC_SINGLE_TLV("Receiver Left Mixer Volume", + M98090_REG_RCV_LOUTL_CONTROL, M98090_MIXRCVLG_SHIFT, + M98090_MIXRCVLG_NUM - 1, 1, max98090_mixout_tlv), + SOC_SINGLE_TLV("Receiver Right Mixer Volume", + M98090_REG_LOUTR_CONTROL, M98090_MIXRCVRG_SHIFT, + M98090_MIXRCVRG_NUM - 1, 1, max98090_mixout_tlv), + + SOC_DOUBLE_R_TLV("Headphone Volume", M98090_REG_LEFT_HP_VOLUME, + M98090_REG_RIGHT_HP_VOLUME, M98090_HPVOLL_SHIFT, + M98090_HPVOLL_NUM - 1, 0, max98090_hp_tlv), + + SOC_DOUBLE_R_RANGE_TLV("Speaker Volume", + M98090_REG_LEFT_SPK_VOLUME, M98090_REG_RIGHT_SPK_VOLUME, + M98090_SPVOLL_SHIFT, 24, M98090_SPVOLL_NUM - 1 + 24, + 0, max98090_spk_tlv), + + SOC_DOUBLE_R_TLV("Receiver Volume", M98090_REG_RCV_LOUTL_VOLUME, + M98090_REG_LOUTR_VOLUME, M98090_RCVLVOL_SHIFT, + M98090_RCVLVOL_NUM - 1, 0, max98090_rcv_lout_tlv), + + SOC_SINGLE("Headphone Left Switch", M98090_REG_LEFT_HP_VOLUME, + M98090_HPLM_SHIFT, 1, 1), + SOC_SINGLE("Headphone Right Switch", M98090_REG_RIGHT_HP_VOLUME, + M98090_HPRM_SHIFT, 1, 1), + + SOC_SINGLE("Speaker Left Switch", M98090_REG_LEFT_SPK_VOLUME, + M98090_SPLM_SHIFT, 1, 1), + SOC_SINGLE("Speaker Right Switch", M98090_REG_RIGHT_SPK_VOLUME, + M98090_SPRM_SHIFT, 1, 1), + + SOC_SINGLE("Receiver Left Switch", M98090_REG_RCV_LOUTL_VOLUME, + M98090_RCVLM_SHIFT, 1, 1), + SOC_SINGLE("Receiver Right Switch", M98090_REG_LOUTR_VOLUME, + M98090_RCVRM_SHIFT, 1, 1), + + SOC_SINGLE("Zero-Crossing Detection", M98090_REG_LEVEL_CONTROL, + M98090_ZDENN_SHIFT, M98090_ZDENN_NUM - 1, 1), + SOC_SINGLE("Enhanced Vol Smoothing", M98090_REG_LEVEL_CONTROL, + M98090_VS2ENN_SHIFT, M98090_VS2ENN_NUM - 1, 1), + SOC_SINGLE("Volume Adjustment Smoothing", M98090_REG_LEVEL_CONTROL, + M98090_VSENN_SHIFT, M98090_VSENN_NUM - 1, 1), + + SND_SOC_BYTES("Biquad Coefficients", M98090_REG_RECORD_BIQUAD_BASE, 15), + SOC_SINGLE("Biquad Switch", M98090_REG_DSP_FILTER_ENABLE, + M98090_ADCBQEN_SHIFT, M98090_ADCBQEN_NUM - 1, 0), +}; + +static const struct snd_kcontrol_new max98091_snd_controls[] = { + + SOC_SINGLE("DMIC34 Zeropad", M98090_REG_SAMPLE_RATE, + M98090_DMIC34_ZEROPAD_SHIFT, + M98090_DMIC34_ZEROPAD_NUM - 1, 0), + + SOC_ENUM("Filter DMIC34 Mode", max98090_filter_dmic34mode_enum), + SOC_SINGLE("DMIC34 DC Blocking", M98090_REG_FILTER_CONFIG, + M98090_FLT_DMIC34HPF_SHIFT, + M98090_FLT_DMIC34HPF_NUM - 1, 0), + + SOC_SINGLE_TLV("DMIC3 Boost Volume", M98090_REG_DMIC3_VOLUME, + M98090_DMIC_AV3G_SHIFT, M98090_DMIC_AV3G_NUM - 1, 0, + max98090_avg_tlv), + SOC_SINGLE_TLV("DMIC4 Boost Volume", M98090_REG_DMIC4_VOLUME, + M98090_DMIC_AV4G_SHIFT, M98090_DMIC_AV4G_NUM - 1, 0, + max98090_avg_tlv), + + SOC_SINGLE_TLV("DMIC3 Volume", M98090_REG_DMIC3_VOLUME, + M98090_DMIC_AV3_SHIFT, M98090_DMIC_AV3_NUM - 1, 1, + max98090_av_tlv), + SOC_SINGLE_TLV("DMIC4 Volume", M98090_REG_DMIC4_VOLUME, + M98090_DMIC_AV4_SHIFT, M98090_DMIC_AV4_NUM - 1, 1, + max98090_av_tlv), + + SND_SOC_BYTES("DMIC34 Biquad Coefficients", + M98090_REG_DMIC34_BIQUAD_BASE, 15), + SOC_SINGLE("DMIC34 Biquad Switch", M98090_REG_DSP_FILTER_ENABLE, + M98090_DMIC34BQEN_SHIFT, M98090_DMIC34BQEN_NUM - 1, 0), + + SOC_SINGLE_TLV("DMIC34 BQ PreAttenuation Volume", + M98090_REG_DMIC34_BQ_PREATTEN, M98090_AV34BQ_SHIFT, + M98090_AV34BQ_NUM - 1, 1, max98090_dv_tlv), +}; + +static int max98090_micinput_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = w->codec; + struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec); + + unsigned int val = snd_soc_read(codec, w->reg); + + if (w->reg == M98090_REG_MIC1_INPUT_LEVEL) + val = (val & M98090_MIC_PA1EN_MASK) >> M98090_MIC_PA1EN_SHIFT; + else + val = (val & M98090_MIC_PA2EN_MASK) >> M98090_MIC_PA2EN_SHIFT; + + + if (val >= 1) { + if (w->reg == M98090_REG_MIC1_INPUT_LEVEL) { + max98090->pa1en = val - 1; /* Update for volatile */ + } else { + max98090->pa2en = val - 1; /* Update for volatile */ + } + } + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + /* If turning on, set to most recently selected volume */ + if (w->reg == M98090_REG_MIC1_INPUT_LEVEL) + val = max98090->pa1en + 1; + else + val = max98090->pa2en + 1; + break; + case SND_SOC_DAPM_POST_PMD: + /* If turning off, turn off */ + val = 0; + break; + default: + return -EINVAL; + } + + if (w->reg == M98090_REG_MIC1_INPUT_LEVEL) + snd_soc_update_bits(codec, w->reg, M98090_MIC_PA1EN_MASK, + val << M98090_MIC_PA1EN_SHIFT); + else + snd_soc_update_bits(codec, w->reg, M98090_MIC_PA2EN_MASK, + val << M98090_MIC_PA2EN_SHIFT); + + return 0; +} + +static const char *mic1_mux_text[] = { "IN12", "IN56" }; + +static const struct soc_enum mic1_mux_enum = + SOC_ENUM_SINGLE(M98090_REG_INPUT_MODE, M98090_EXTMIC1_SHIFT, + ARRAY_SIZE(mic1_mux_text), mic1_mux_text); + +static const struct snd_kcontrol_new max98090_mic1_mux = + SOC_DAPM_ENUM("MIC1 Mux", mic1_mux_enum); + +static const char *mic2_mux_text[] = { "IN34", "IN56" }; + +static const struct soc_enum mic2_mux_enum = + SOC_ENUM_SINGLE(M98090_REG_INPUT_MODE, M98090_EXTMIC2_SHIFT, + ARRAY_SIZE(mic2_mux_text), mic2_mux_text); + +static const struct snd_kcontrol_new max98090_mic2_mux = + SOC_DAPM_ENUM("MIC2 Mux", mic2_mux_enum); + +static const char * max98090_micpre_text[] = { "Off", "On" }; + +static const struct soc_enum max98090_pa1en_enum = + SOC_ENUM_SINGLE(M98090_REG_MIC1_INPUT_LEVEL, M98090_MIC_PA1EN_SHIFT, + ARRAY_SIZE(max98090_micpre_text), max98090_micpre_text); + +static const struct soc_enum max98090_pa2en_enum = + SOC_ENUM_SINGLE(M98090_REG_MIC2_INPUT_LEVEL, M98090_MIC_PA2EN_SHIFT, + ARRAY_SIZE(max98090_micpre_text), max98090_micpre_text); + +/* LINEA mixer switch */ +static const struct snd_kcontrol_new max98090_linea_mixer_controls[] = { + SOC_DAPM_SINGLE("IN1 Switch", M98090_REG_LINE_INPUT_CONFIG, + M98090_IN1SEEN_SHIFT, 1, 0), + SOC_DAPM_SINGLE("IN3 Switch", M98090_REG_LINE_INPUT_CONFIG, + M98090_IN3SEEN_SHIFT, 1, 0), + SOC_DAPM_SINGLE("IN5 Switch", M98090_REG_LINE_INPUT_CONFIG, + M98090_IN5SEEN_SHIFT, 1, 0), + SOC_DAPM_SINGLE("IN34 Switch", M98090_REG_LINE_INPUT_CONFIG, + M98090_IN34DIFF_SHIFT, 1, 0), +}; + +/* LINEB mixer switch */ +static const struct snd_kcontrol_new max98090_lineb_mixer_controls[] = { + SOC_DAPM_SINGLE("IN2 Switch", M98090_REG_LINE_INPUT_CONFIG, + M98090_IN2SEEN_SHIFT, 1, 0), + SOC_DAPM_SINGLE("IN4 Switch", M98090_REG_LINE_INPUT_CONFIG, + M98090_IN4SEEN_SHIFT, 1, 0), + SOC_DAPM_SINGLE("IN6 Switch", M98090_REG_LINE_INPUT_CONFIG, + M98090_IN6SEEN_SHIFT, 1, 0), + SOC_DAPM_SINGLE("IN56 Switch", M98090_REG_LINE_INPUT_CONFIG, + M98090_IN56DIFF_SHIFT, 1, 0), +}; + +/* Left ADC mixer switch */ +static const struct snd_kcontrol_new max98090_left_adc_mixer_controls[] = { + SOC_DAPM_SINGLE("IN12 Switch", M98090_REG_LEFT_ADC_MIXER, + M98090_MIXADL_IN12DIFF_SHIFT, 1, 0), + SOC_DAPM_SINGLE("IN34 Switch", M98090_REG_LEFT_ADC_MIXER, + M98090_MIXADL_IN34DIFF_SHIFT, 1, 0), + SOC_DAPM_SINGLE("IN56 Switch", M98090_REG_LEFT_ADC_MIXER, + M98090_MIXADL_IN65DIFF_SHIFT, 1, 0), + SOC_DAPM_SINGLE("LINEA Switch", M98090_REG_LEFT_ADC_MIXER, + M98090_MIXADL_LINEA_SHIFT, 1, 0), + SOC_DAPM_SINGLE("LINEB Switch", M98090_REG_LEFT_ADC_MIXER, + M98090_MIXADL_LINEB_SHIFT, 1, 0), + SOC_DAPM_SINGLE("MIC1 Switch", M98090_REG_LEFT_ADC_MIXER, + M98090_MIXADL_MIC1_SHIFT, 1, 0), + SOC_DAPM_SINGLE("MIC2 Switch", M98090_REG_LEFT_ADC_MIXER, + M98090_MIXADL_MIC2_SHIFT, 1, 0), +}; + +/* Right ADC mixer switch */ +static const struct snd_kcontrol_new max98090_right_adc_mixer_controls[] = { + SOC_DAPM_SINGLE("IN12 Switch", M98090_REG_RIGHT_ADC_MIXER, + M98090_MIXADR_IN12DIFF_SHIFT, 1, 0), + SOC_DAPM_SINGLE("IN34 Switch", M98090_REG_RIGHT_ADC_MIXER, + M98090_MIXADR_IN34DIFF_SHIFT, 1, 0), + SOC_DAPM_SINGLE("IN56 Switch", M98090_REG_RIGHT_ADC_MIXER, + M98090_MIXADR_IN65DIFF_SHIFT, 1, 0), + SOC_DAPM_SINGLE("LINEA Switch", M98090_REG_RIGHT_ADC_MIXER, + M98090_MIXADR_LINEA_SHIFT, 1, 0), + SOC_DAPM_SINGLE("LINEB Switch", M98090_REG_RIGHT_ADC_MIXER, + M98090_MIXADR_LINEB_SHIFT, 1, 0), + SOC_DAPM_SINGLE("MIC1 Switch", M98090_REG_RIGHT_ADC_MIXER, + M98090_MIXADR_MIC1_SHIFT, 1, 0), + SOC_DAPM_SINGLE("MIC2 Switch", M98090_REG_RIGHT_ADC_MIXER, + M98090_MIXADR_MIC2_SHIFT, 1, 0), +}; + +static const char *lten_mux_text[] = { "Normal", "Loopthrough" }; + +static const struct soc_enum ltenl_mux_enum = + SOC_ENUM_SINGLE(M98090_REG_IO_CONFIGURATION, M98090_LTEN_SHIFT, + ARRAY_SIZE(lten_mux_text), lten_mux_text); + +static const struct soc_enum ltenr_mux_enum = + SOC_ENUM_SINGLE(M98090_REG_IO_CONFIGURATION, M98090_LTEN_SHIFT, + ARRAY_SIZE(lten_mux_text), lten_mux_text); + +static const struct snd_kcontrol_new max98090_ltenl_mux = + SOC_DAPM_ENUM("LTENL Mux", ltenl_mux_enum); + +static const struct snd_kcontrol_new max98090_ltenr_mux = + SOC_DAPM_ENUM("LTENR Mux", ltenr_mux_enum); + +static const char *lben_mux_text[] = { "Normal", "Loopback" }; + +static const struct soc_enum lbenl_mux_enum = + SOC_ENUM_SINGLE(M98090_REG_IO_CONFIGURATION, M98090_LBEN_SHIFT, + ARRAY_SIZE(lben_mux_text), lben_mux_text); + +static const struct soc_enum lbenr_mux_enum = + SOC_ENUM_SINGLE(M98090_REG_IO_CONFIGURATION, M98090_LBEN_SHIFT, + ARRAY_SIZE(lben_mux_text), lben_mux_text); + +static const struct snd_kcontrol_new max98090_lbenl_mux = + SOC_DAPM_ENUM("LBENL Mux", lbenl_mux_enum); + +static const struct snd_kcontrol_new max98090_lbenr_mux = + SOC_DAPM_ENUM("LBENR Mux", lbenr_mux_enum); + +static const char *stenl_mux_text[] = { "Normal", "Sidetone Left" }; + +static const char *stenr_mux_text[] = { "Normal", "Sidetone Right" }; + +static const struct soc_enum stenl_mux_enum = + SOC_ENUM_SINGLE(M98090_REG_ADC_SIDETONE, M98090_DSTSL_SHIFT, + ARRAY_SIZE(stenl_mux_text), stenl_mux_text); + +static const struct soc_enum stenr_mux_enum = + SOC_ENUM_SINGLE(M98090_REG_ADC_SIDETONE, M98090_DSTSR_SHIFT, + ARRAY_SIZE(stenr_mux_text), stenr_mux_text); + +static const struct snd_kcontrol_new max98090_stenl_mux = + SOC_DAPM_ENUM("STENL Mux", stenl_mux_enum); + +static const struct snd_kcontrol_new max98090_stenr_mux = + SOC_DAPM_ENUM("STENR Mux", stenr_mux_enum); + +/* Left speaker mixer switch */ +static const struct + snd_kcontrol_new max98090_left_speaker_mixer_controls[] = { + SOC_DAPM_SINGLE("Left DAC Switch", M98090_REG_LEFT_SPK_MIXER, + M98090_MIXSPL_DACL_SHIFT, 1, 0), + SOC_DAPM_SINGLE("Right DAC Switch", M98090_REG_LEFT_SPK_MIXER, + M98090_MIXSPL_DACR_SHIFT, 1, 0), + SOC_DAPM_SINGLE("LINEA Switch", M98090_REG_LEFT_SPK_MIXER, + M98090_MIXSPL_LINEA_SHIFT, 1, 0), + SOC_DAPM_SINGLE("LINEB Switch", M98090_REG_LEFT_SPK_MIXER, + M98090_MIXSPL_LINEB_SHIFT, 1, 0), + SOC_DAPM_SINGLE("MIC1 Switch", M98090_REG_LEFT_SPK_MIXER, + M98090_MIXSPL_MIC1_SHIFT, 1, 0), + SOC_DAPM_SINGLE("MIC2 Switch", M98090_REG_LEFT_SPK_MIXER, + M98090_MIXSPL_MIC2_SHIFT, 1, 0), +}; + +/* Right speaker mixer switch */ +static const struct + snd_kcontrol_new max98090_right_speaker_mixer_controls[] = { + SOC_DAPM_SINGLE("Left DAC Switch", M98090_REG_RIGHT_SPK_MIXER, + M98090_MIXSPR_DACL_SHIFT, 1, 0), + SOC_DAPM_SINGLE("Right DAC Switch", M98090_REG_RIGHT_SPK_MIXER, + M98090_MIXSPR_DACR_SHIFT, 1, 0), + SOC_DAPM_SINGLE("LINEA Switch", M98090_REG_RIGHT_SPK_MIXER, + M98090_MIXSPR_LINEA_SHIFT, 1, 0), + SOC_DAPM_SINGLE("LINEB Switch", M98090_REG_RIGHT_SPK_MIXER, + M98090_MIXSPR_LINEB_SHIFT, 1, 0), + SOC_DAPM_SINGLE("MIC1 Switch", M98090_REG_RIGHT_SPK_MIXER, + M98090_MIXSPR_MIC1_SHIFT, 1, 0), + SOC_DAPM_SINGLE("MIC2 Switch", M98090_REG_RIGHT_SPK_MIXER, + M98090_MIXSPR_MIC2_SHIFT, 1, 0), +}; + +/* Left headphone mixer switch */ +static const struct snd_kcontrol_new max98090_left_hp_mixer_controls[] = { + SOC_DAPM_SINGLE("Left DAC Switch", M98090_REG_LEFT_HP_MIXER, + M98090_MIXHPL_DACL_SHIFT, 1, 0), + SOC_DAPM_SINGLE("Right DAC Switch", M98090_REG_LEFT_HP_MIXER, + M98090_MIXHPL_DACR_SHIFT, 1, 0), + SOC_DAPM_SINGLE("LINEA Switch", M98090_REG_LEFT_HP_MIXER, + M98090_MIXHPL_LINEA_SHIFT, 1, 0), + SOC_DAPM_SINGLE("LINEB Switch", M98090_REG_LEFT_HP_MIXER, + M98090_MIXHPL_LINEB_SHIFT, 1, 0), + SOC_DAPM_SINGLE("MIC1 Switch", M98090_REG_LEFT_HP_MIXER, + M98090_MIXHPL_MIC1_SHIFT, 1, 0), + SOC_DAPM_SINGLE("MIC2 Switch", M98090_REG_LEFT_HP_MIXER, + M98090_MIXHPL_MIC2_SHIFT, 1, 0), +}; + +/* Right headphone mixer switch */ +static const struct snd_kcontrol_new max98090_right_hp_mixer_controls[] = { + SOC_DAPM_SINGLE("Left DAC Switch", M98090_REG_RIGHT_HP_MIXER, + M98090_MIXHPR_DACL_SHIFT, 1, 0), + SOC_DAPM_SINGLE("Right DAC Switch", M98090_REG_RIGHT_HP_MIXER, + M98090_MIXHPR_DACR_SHIFT, 1, 0), + SOC_DAPM_SINGLE("LINEA Switch", M98090_REG_RIGHT_HP_MIXER, + M98090_MIXHPR_LINEA_SHIFT, 1, 0), + SOC_DAPM_SINGLE("LINEB Switch", M98090_REG_RIGHT_HP_MIXER, + M98090_MIXHPR_LINEB_SHIFT, 1, 0), + SOC_DAPM_SINGLE("MIC1 Switch", M98090_REG_RIGHT_HP_MIXER, + M98090_MIXHPR_MIC1_SHIFT, 1, 0), + SOC_DAPM_SINGLE("MIC2 Switch", M98090_REG_RIGHT_HP_MIXER, + M98090_MIXHPR_MIC2_SHIFT, 1, 0), +}; + +/* Left receiver mixer switch */ +static const struct snd_kcontrol_new max98090_left_rcv_mixer_controls[] = { + SOC_DAPM_SINGLE("Left DAC Switch", M98090_REG_RCV_LOUTL_MIXER, + M98090_MIXRCVL_DACL_SHIFT, 1, 0), + SOC_DAPM_SINGLE("Right DAC Switch", M98090_REG_RCV_LOUTL_MIXER, + M98090_MIXRCVL_DACR_SHIFT, 1, 0), + SOC_DAPM_SINGLE("LINEA Switch", M98090_REG_RCV_LOUTL_MIXER, + M98090_MIXRCVL_LINEA_SHIFT, 1, 0), + SOC_DAPM_SINGLE("LINEB Switch", M98090_REG_RCV_LOUTL_MIXER, + M98090_MIXRCVL_LINEB_SHIFT, 1, 0), + SOC_DAPM_SINGLE("MIC1 Switch", M98090_REG_RCV_LOUTL_MIXER, + M98090_MIXRCVL_MIC1_SHIFT, 1, 0), + SOC_DAPM_SINGLE("MIC2 Switch", M98090_REG_RCV_LOUTL_MIXER, + M98090_MIXRCVL_MIC2_SHIFT, 1, 0), +}; + +/* Right receiver mixer switch */ +static const struct snd_kcontrol_new max98090_right_rcv_mixer_controls[] = { + SOC_DAPM_SINGLE("Left DAC Switch", M98090_REG_LOUTR_MIXER, + M98090_MIXRCVR_DACL_SHIFT, 1, 0), + SOC_DAPM_SINGLE("Right DAC Switch", M98090_REG_LOUTR_MIXER, + M98090_MIXRCVR_DACR_SHIFT, 1, 0), + SOC_DAPM_SINGLE("LINEA Switch", M98090_REG_LOUTR_MIXER, + M98090_MIXRCVR_LINEA_SHIFT, 1, 0), + SOC_DAPM_SINGLE("LINEB Switch", M98090_REG_LOUTR_MIXER, + M98090_MIXRCVR_LINEB_SHIFT, 1, 0), + SOC_DAPM_SINGLE("MIC1 Switch", M98090_REG_LOUTR_MIXER, + M98090_MIXRCVR_MIC1_SHIFT, 1, 0), + SOC_DAPM_SINGLE("MIC2 Switch", M98090_REG_LOUTR_MIXER, + M98090_MIXRCVR_MIC2_SHIFT, 1, 0), +}; + +static const char *linmod_mux_text[] = { "Left Only", "Left and Right" }; + +static const struct soc_enum linmod_mux_enum = + SOC_ENUM_SINGLE(M98090_REG_LOUTR_MIXER, M98090_LINMOD_SHIFT, + ARRAY_SIZE(linmod_mux_text), linmod_mux_text); + +static const struct snd_kcontrol_new max98090_linmod_mux = + SOC_DAPM_ENUM("LINMOD Mux", linmod_mux_enum); + +static const char *mixhpsel_mux_text[] = { "DAC Only", "HP Mixer" }; + +/* + * This is a mux as it selects the HP output, but to DAPM it is a Mixer enable + */ +static const struct soc_enum mixhplsel_mux_enum = + SOC_ENUM_SINGLE(M98090_REG_HP_CONTROL, M98090_MIXHPLSEL_SHIFT, + ARRAY_SIZE(mixhpsel_mux_text), mixhpsel_mux_text); + +static const struct snd_kcontrol_new max98090_mixhplsel_mux = + SOC_DAPM_ENUM("MIXHPLSEL Mux", mixhplsel_mux_enum); + +static const struct soc_enum mixhprsel_mux_enum = + SOC_ENUM_SINGLE(M98090_REG_HP_CONTROL, M98090_MIXHPRSEL_SHIFT, + ARRAY_SIZE(mixhpsel_mux_text), mixhpsel_mux_text); + +static const struct snd_kcontrol_new max98090_mixhprsel_mux = + SOC_DAPM_ENUM("MIXHPRSEL Mux", mixhprsel_mux_enum); + +static const struct snd_soc_dapm_widget max98090_dapm_widgets[] = { + + SND_SOC_DAPM_INPUT("MIC1"), + SND_SOC_DAPM_INPUT("MIC2"), + SND_SOC_DAPM_INPUT("DMICL"), + SND_SOC_DAPM_INPUT("DMICR"), + SND_SOC_DAPM_INPUT("IN1"), + SND_SOC_DAPM_INPUT("IN2"), + SND_SOC_DAPM_INPUT("IN3"), + SND_SOC_DAPM_INPUT("IN4"), + SND_SOC_DAPM_INPUT("IN5"), + SND_SOC_DAPM_INPUT("IN6"), + SND_SOC_DAPM_INPUT("IN12"), + SND_SOC_DAPM_INPUT("IN34"), + SND_SOC_DAPM_INPUT("IN56"), + + SND_SOC_DAPM_SUPPLY("MICBIAS", M98090_REG_INPUT_ENABLE, + M98090_MBEN_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("SHDN", M98090_REG_DEVICE_SHUTDOWN, + M98090_SHDNN_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("SDIEN", M98090_REG_IO_CONFIGURATION, + M98090_SDIEN_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("SDOEN", M98090_REG_IO_CONFIGURATION, + M98090_SDOEN_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DMICL_ENA", M98090_REG_DIGITAL_MIC_ENABLE, + M98090_DIGMICL_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DMICR_ENA", M98090_REG_DIGITAL_MIC_ENABLE, + M98090_DIGMICR_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("AHPF", M98090_REG_FILTER_CONFIG, + M98090_AHPF_SHIFT, 0, NULL, 0), + +/* + * Note: Sysclk and misc power supplies are taken care of by SHDN + */ + + SND_SOC_DAPM_MUX("MIC1 Mux", SND_SOC_NOPM, + 0, 0, &max98090_mic1_mux), + + SND_SOC_DAPM_MUX("MIC2 Mux", SND_SOC_NOPM, + 0, 0, &max98090_mic2_mux), + + SND_SOC_DAPM_PGA_E("MIC1 Input", M98090_REG_MIC1_INPUT_LEVEL, + M98090_MIC_PA1EN_SHIFT, 0, NULL, 0, max98090_micinput_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_PGA_E("MIC2 Input", M98090_REG_MIC2_INPUT_LEVEL, + M98090_MIC_PA2EN_SHIFT, 0, NULL, 0, max98090_micinput_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MIXER("LINEA Mixer", SND_SOC_NOPM, 0, 0, + &max98090_linea_mixer_controls[0], + ARRAY_SIZE(max98090_linea_mixer_controls)), + + SND_SOC_DAPM_MIXER("LINEB Mixer", SND_SOC_NOPM, 0, 0, + &max98090_lineb_mixer_controls[0], + ARRAY_SIZE(max98090_lineb_mixer_controls)), + + SND_SOC_DAPM_PGA("LINEA Input", M98090_REG_INPUT_ENABLE, + M98090_LINEAEN_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_PGA("LINEB Input", M98090_REG_INPUT_ENABLE, + M98090_LINEBEN_SHIFT, 0, NULL, 0), + + SND_SOC_DAPM_MIXER("Left ADC Mixer", SND_SOC_NOPM, 0, 0, + &max98090_left_adc_mixer_controls[0], + ARRAY_SIZE(max98090_left_adc_mixer_controls)), + + SND_SOC_DAPM_MIXER("Right ADC Mixer", SND_SOC_NOPM, 0, 0, + &max98090_right_adc_mixer_controls[0], + ARRAY_SIZE(max98090_right_adc_mixer_controls)), + + SND_SOC_DAPM_ADC("ADCL", NULL, M98090_REG_INPUT_ENABLE, + M98090_ADLEN_SHIFT, 0), + SND_SOC_DAPM_ADC("ADCR", NULL, M98090_REG_INPUT_ENABLE, + M98090_ADREN_SHIFT, 0), + + SND_SOC_DAPM_AIF_OUT("AIFOUTL", "HiFi Capture", 0, + SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AIFOUTR", "HiFi Capture", 1, + SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_MUX("LBENL Mux", SND_SOC_NOPM, + 0, 0, &max98090_lbenl_mux), + + SND_SOC_DAPM_MUX("LBENR Mux", SND_SOC_NOPM, + 0, 0, &max98090_lbenr_mux), + + SND_SOC_DAPM_MUX("LTENL Mux", SND_SOC_NOPM, + 0, 0, &max98090_ltenl_mux), + + SND_SOC_DAPM_MUX("LTENR Mux", SND_SOC_NOPM, + 0, 0, &max98090_ltenr_mux), + + SND_SOC_DAPM_MUX("STENL Mux", SND_SOC_NOPM, + 0, 0, &max98090_stenl_mux), + + SND_SOC_DAPM_MUX("STENR Mux", SND_SOC_NOPM, + 0, 0, &max98090_stenr_mux), + + SND_SOC_DAPM_AIF_IN("AIFINL", "HiFi Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("AIFINR", "HiFi Playback", 1, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_DAC("DACL", NULL, M98090_REG_OUTPUT_ENABLE, + M98090_DALEN_SHIFT, 0), + SND_SOC_DAPM_DAC("DACR", NULL, M98090_REG_OUTPUT_ENABLE, + M98090_DAREN_SHIFT, 0), + + SND_SOC_DAPM_MIXER("Left Headphone Mixer", SND_SOC_NOPM, 0, 0, + &max98090_left_hp_mixer_controls[0], + ARRAY_SIZE(max98090_left_hp_mixer_controls)), + + SND_SOC_DAPM_MIXER("Right Headphone Mixer", SND_SOC_NOPM, 0, 0, + &max98090_right_hp_mixer_controls[0], + ARRAY_SIZE(max98090_right_hp_mixer_controls)), + + SND_SOC_DAPM_MIXER("Left Speaker Mixer", SND_SOC_NOPM, 0, 0, + &max98090_left_speaker_mixer_controls[0], + ARRAY_SIZE(max98090_left_speaker_mixer_controls)), + + SND_SOC_DAPM_MIXER("Right Speaker Mixer", SND_SOC_NOPM, 0, 0, + &max98090_right_speaker_mixer_controls[0], + ARRAY_SIZE(max98090_right_speaker_mixer_controls)), + + SND_SOC_DAPM_MIXER("Left Receiver Mixer", SND_SOC_NOPM, 0, 0, + &max98090_left_rcv_mixer_controls[0], + ARRAY_SIZE(max98090_left_rcv_mixer_controls)), + + SND_SOC_DAPM_MIXER("Right Receiver Mixer", SND_SOC_NOPM, 0, 0, + &max98090_right_rcv_mixer_controls[0], + ARRAY_SIZE(max98090_right_rcv_mixer_controls)), + + SND_SOC_DAPM_MUX("LINMOD Mux", M98090_REG_LOUTR_MIXER, + M98090_LINMOD_SHIFT, 0, &max98090_linmod_mux), + + SND_SOC_DAPM_MUX("MIXHPLSEL Mux", M98090_REG_HP_CONTROL, + M98090_MIXHPLSEL_SHIFT, 0, &max98090_mixhplsel_mux), + + SND_SOC_DAPM_MUX("MIXHPRSEL Mux", M98090_REG_HP_CONTROL, + M98090_MIXHPRSEL_SHIFT, 0, &max98090_mixhprsel_mux), + + SND_SOC_DAPM_PGA("HP Left Out", M98090_REG_OUTPUT_ENABLE, + M98090_HPLEN_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_PGA("HP Right Out", M98090_REG_OUTPUT_ENABLE, + M98090_HPREN_SHIFT, 0, NULL, 0), + + SND_SOC_DAPM_PGA("SPK Left Out", M98090_REG_OUTPUT_ENABLE, + M98090_SPLEN_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_PGA("SPK Right Out", M98090_REG_OUTPUT_ENABLE, + M98090_SPREN_SHIFT, 0, NULL, 0), + + SND_SOC_DAPM_PGA("RCV Left Out", M98090_REG_OUTPUT_ENABLE, + M98090_RCVLEN_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_PGA("RCV Right Out", M98090_REG_OUTPUT_ENABLE, + M98090_RCVREN_SHIFT, 0, NULL, 0), + + SND_SOC_DAPM_OUTPUT("HPL"), + SND_SOC_DAPM_OUTPUT("HPR"), + SND_SOC_DAPM_OUTPUT("SPKL"), + SND_SOC_DAPM_OUTPUT("SPKR"), + SND_SOC_DAPM_OUTPUT("RCVL"), + SND_SOC_DAPM_OUTPUT("RCVR"), +}; + +static const struct snd_soc_dapm_widget max98091_dapm_widgets[] = { + + SND_SOC_DAPM_INPUT("DMIC3"), + SND_SOC_DAPM_INPUT("DMIC4"), + + SND_SOC_DAPM_SUPPLY("DMIC3_ENA", M98090_REG_DIGITAL_MIC_ENABLE, + M98090_DIGMIC3_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DMIC4_ENA", M98090_REG_DIGITAL_MIC_ENABLE, + M98090_DIGMIC4_SHIFT, 0, NULL, 0), +}; + +static const struct snd_soc_dapm_route max98090_dapm_routes[] = { + + {"MIC1 Input", NULL, "MIC1"}, + {"MIC2 Input", NULL, "MIC2"}, + + {"DMICL", NULL, "DMICL_ENA"}, + {"DMICR", NULL, "DMICR_ENA"}, + {"DMICL", NULL, "AHPF"}, + {"DMICR", NULL, "AHPF"}, + + /* MIC1 input mux */ + {"MIC1 Mux", "IN12", "IN12"}, + {"MIC1 Mux", "IN56", "IN56"}, + + /* MIC2 input mux */ + {"MIC2 Mux", "IN34", "IN34"}, + {"MIC2 Mux", "IN56", "IN56"}, + + {"MIC1 Input", NULL, "MIC1 Mux"}, + {"MIC2 Input", NULL, "MIC2 Mux"}, + + /* Left ADC input mixer */ + {"Left ADC Mixer", "IN12 Switch", "IN12"}, + {"Left ADC Mixer", "IN34 Switch", "IN34"}, + {"Left ADC Mixer", "IN56 Switch", "IN56"}, + {"Left ADC Mixer", "LINEA Switch", "LINEA Input"}, + {"Left ADC Mixer", "LINEB Switch", "LINEB Input"}, + {"Left ADC Mixer", "MIC1 Switch", "MIC1 Input"}, + {"Left ADC Mixer", "MIC2 Switch", "MIC2 Input"}, + + /* Right ADC input mixer */ + {"Right ADC Mixer", "IN12 Switch", "IN12"}, + {"Right ADC Mixer", "IN34 Switch", "IN34"}, + {"Right ADC Mixer", "IN56 Switch", "IN56"}, + {"Right ADC Mixer", "LINEA Switch", "LINEA Input"}, + {"Right ADC Mixer", "LINEB Switch", "LINEB Input"}, + {"Right ADC Mixer", "MIC1 Switch", "MIC1 Input"}, + {"Right ADC Mixer", "MIC2 Switch", "MIC2 Input"}, + + /* Line A input mixer */ + {"LINEA Mixer", "IN1 Switch", "IN1"}, + {"LINEA Mixer", "IN3 Switch", "IN3"}, + {"LINEA Mixer", "IN5 Switch", "IN5"}, + {"LINEA Mixer", "IN34 Switch", "IN34"}, + + /* Line B input mixer */ + {"LINEB Mixer", "IN2 Switch", "IN2"}, + {"LINEB Mixer", "IN4 Switch", "IN4"}, + {"LINEB Mixer", "IN6 Switch", "IN6"}, + {"LINEB Mixer", "IN56 Switch", "IN56"}, + + {"LINEA Input", NULL, "LINEA Mixer"}, + {"LINEB Input", NULL, "LINEB Mixer"}, + + /* Inputs */ + {"ADCL", NULL, "Left ADC Mixer"}, + {"ADCR", NULL, "Right ADC Mixer"}, + {"ADCL", NULL, "SHDN"}, + {"ADCR", NULL, "SHDN"}, + + {"LBENL Mux", "Normal", "ADCL"}, + {"LBENL Mux", "Normal", "DMICL"}, + {"LBENL Mux", "Loopback", "LTENL Mux"}, + {"LBENR Mux", "Normal", "ADCR"}, + {"LBENR Mux", "Normal", "DMICR"}, + {"LBENR Mux", "Loopback", "LTENR Mux"}, + + {"AIFOUTL", NULL, "LBENL Mux"}, + {"AIFOUTR", NULL, "LBENR Mux"}, + {"AIFOUTL", NULL, "SHDN"}, + {"AIFOUTR", NULL, "SHDN"}, + {"AIFOUTL", NULL, "SDOEN"}, + {"AIFOUTR", NULL, "SDOEN"}, + + {"LTENL Mux", "Normal", "AIFINL"}, + {"LTENL Mux", "Loopthrough", "LBENL Mux"}, + {"LTENR Mux", "Normal", "AIFINR"}, + {"LTENR Mux", "Loopthrough", "LBENR Mux"}, + + {"DACL", NULL, "LTENL Mux"}, + {"DACR", NULL, "LTENR Mux"}, + + {"STENL Mux", "Sidetone Left", "ADCL"}, + {"STENL Mux", "Sidetone Left", "DMICL"}, + {"STENR Mux", "Sidetone Right", "ADCR"}, + {"STENR Mux", "Sidetone Right", "DMICR"}, + {"DACL", "NULL", "STENL Mux"}, + {"DACR", "NULL", "STENL Mux"}, + + {"AIFINL", NULL, "SHDN"}, + {"AIFINR", NULL, "SHDN"}, + {"AIFINL", NULL, "SDIEN"}, + {"AIFINR", NULL, "SDIEN"}, + {"DACL", NULL, "SHDN"}, + {"DACR", NULL, "SHDN"}, + + /* Left headphone output mixer */ + {"Left Headphone Mixer", "Left DAC Switch", "DACL"}, + {"Left Headphone Mixer", "Right DAC Switch", "DACR"}, + {"Left Headphone Mixer", "MIC1 Switch", "MIC1 Input"}, + {"Left Headphone Mixer", "MIC2 Switch", "MIC2 Input"}, + {"Left Headphone Mixer", "LINEA Switch", "LINEA Input"}, + {"Left Headphone Mixer", "LINEB Switch", "LINEB Input"}, + + /* Right headphone output mixer */ + {"Right Headphone Mixer", "Left DAC Switch", "DACL"}, + {"Right Headphone Mixer", "Right DAC Switch", "DACR"}, + {"Right Headphone Mixer", "MIC1 Switch", "MIC1 Input"}, + {"Right Headphone Mixer", "MIC2 Switch", "MIC2 Input"}, + {"Right Headphone Mixer", "LINEA Switch", "LINEA Input"}, + {"Right Headphone Mixer", "LINEB Switch", "LINEB Input"}, + + /* Left speaker output mixer */ + {"Left Speaker Mixer", "Left DAC Switch", "DACL"}, + {"Left Speaker Mixer", "Right DAC Switch", "DACR"}, + {"Left Speaker Mixer", "MIC1 Switch", "MIC1 Input"}, + {"Left Speaker Mixer", "MIC2 Switch", "MIC2 Input"}, + {"Left Speaker Mixer", "LINEA Switch", "LINEA Input"}, + {"Left Speaker Mixer", "LINEB Switch", "LINEB Input"}, + + /* Right speaker output mixer */ + {"Right Speaker Mixer", "Left DAC Switch", "DACL"}, + {"Right Speaker Mixer", "Right DAC Switch", "DACR"}, + {"Right Speaker Mixer", "MIC1 Switch", "MIC1 Input"}, + {"Right Speaker Mixer", "MIC2 Switch", "MIC2 Input"}, + {"Right Speaker Mixer", "LINEA Switch", "LINEA Input"}, + {"Right Speaker Mixer", "LINEB Switch", "LINEB Input"}, + + /* Left Receiver output mixer */ + {"Left Receiver Mixer", "Left DAC Switch", "DACL"}, + {"Left Receiver Mixer", "Right DAC Switch", "DACR"}, + {"Left Receiver Mixer", "MIC1 Switch", "MIC1 Input"}, + {"Left Receiver Mixer", "MIC2 Switch", "MIC2 Input"}, + {"Left Receiver Mixer", "LINEA Switch", "LINEA Input"}, + {"Left Receiver Mixer", "LINEB Switch", "LINEB Input"}, + + /* Right Receiver output mixer */ + {"Right Receiver Mixer", "Left DAC Switch", "DACL"}, + {"Right Receiver Mixer", "Right DAC Switch", "DACR"}, + {"Right Receiver Mixer", "MIC1 Switch", "MIC1 Input"}, + {"Right Receiver Mixer", "MIC2 Switch", "MIC2 Input"}, + {"Right Receiver Mixer", "LINEA Switch", "LINEA Input"}, + {"Right Receiver Mixer", "LINEB Switch", "LINEB Input"}, + + {"MIXHPLSEL Mux", "HP Mixer", "Left Headphone Mixer"}, + + /* + * Disable this for lowest power if bypassing + * the DAC with an analog signal + */ + {"HP Left Out", NULL, "DACL"}, + {"HP Left Out", NULL, "MIXHPLSEL Mux"}, + + {"MIXHPRSEL Mux", "HP Mixer", "Right Headphone Mixer"}, + + /* + * Disable this for lowest power if bypassing + * the DAC with an analog signal + */ + {"HP Right Out", NULL, "DACR"}, + {"HP Right Out", NULL, "MIXHPRSEL Mux"}, + + {"SPK Left Out", NULL, "Left Speaker Mixer"}, + {"SPK Right Out", NULL, "Right Speaker Mixer"}, + {"RCV Left Out", NULL, "Left Receiver Mixer"}, + + {"LINMOD Mux", "Left and Right", "Right Receiver Mixer"}, + {"LINMOD Mux", "Left Only", "Left Receiver Mixer"}, + {"RCV Right Out", NULL, "LINMOD Mux"}, + + {"HPL", NULL, "HP Left Out"}, + {"HPR", NULL, "HP Right Out"}, + {"SPKL", NULL, "SPK Left Out"}, + {"SPKR", NULL, "SPK Right Out"}, + {"RCVL", NULL, "RCV Left Out"}, + {"RCVR", NULL, "RCV Right Out"}, + +}; + +static const struct snd_soc_dapm_route max98091_dapm_routes[] = { + + /* DMIC inputs */ + {"DMIC3", NULL, "DMIC3_ENA"}, + {"DMIC4", NULL, "DMIC4_ENA"}, + {"DMIC3", NULL, "AHPF"}, + {"DMIC4", NULL, "AHPF"}, + +}; + +static int max98090_add_widgets(struct snd_soc_codec *codec) +{ + struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec); + struct snd_soc_dapm_context *dapm = &codec->dapm; + + snd_soc_add_codec_controls(codec, max98090_snd_controls, + ARRAY_SIZE(max98090_snd_controls)); + + if (max98090->devtype == MAX98091) { + snd_soc_add_codec_controls(codec, max98091_snd_controls, + ARRAY_SIZE(max98091_snd_controls)); + } + + snd_soc_dapm_new_controls(dapm, max98090_dapm_widgets, + ARRAY_SIZE(max98090_dapm_widgets)); + + snd_soc_dapm_add_routes(dapm, max98090_dapm_routes, + ARRAY_SIZE(max98090_dapm_routes)); + + if (max98090->devtype == MAX98091) { + snd_soc_dapm_new_controls(dapm, max98091_dapm_widgets, + ARRAY_SIZE(max98091_dapm_widgets)); + + snd_soc_dapm_add_routes(dapm, max98091_dapm_routes, + ARRAY_SIZE(max98091_dapm_routes)); + + } + + return 0; +} + +static const int pclk_rates[] = { + 12000000, 12000000, 13000000, 13000000, + 16000000, 16000000, 19200000, 19200000 +}; + +static const int lrclk_rates[] = { + 8000, 16000, 8000, 16000, + 8000, 16000, 8000, 16000 +}; + +static const int user_pclk_rates[] = { + 13000000, 13000000 +}; + +static const int user_lrclk_rates[] = { + 44100, 48000 +}; + +static const unsigned long long ni_value[] = { + 3528, 768 +}; + +static const unsigned long long mi_value[] = { + 8125, 1625 +}; + +static void max98090_configure_bclk(struct snd_soc_codec *codec) +{ + struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec); + unsigned long long ni; + int i; + + if (!max98090->sysclk) { + dev_err(codec->dev, "No SYSCLK configured\n"); + return; + } + + if (!max98090->bclk || !max98090->lrclk) { + dev_err(codec->dev, "No audio clocks configured\n"); + return; + } + + /* Skip configuration when operating as slave */ + if (!(snd_soc_read(codec, M98090_REG_MASTER_MODE) & + M98090_MAS_MASK)) { + return; + } + + /* Check for supported PCLK to LRCLK ratios */ + for (i = 0; i < ARRAY_SIZE(pclk_rates); i++) { + if ((pclk_rates[i] == max98090->sysclk) && + (lrclk_rates[i] == max98090->lrclk)) { + dev_dbg(codec->dev, + "Found supported PCLK to LRCLK rates 0x%x\n", + i + 0x8); + + snd_soc_update_bits(codec, M98090_REG_CLOCK_MODE, + M98090_FREQ_MASK, + (i + 0x8) << M98090_FREQ_SHIFT); + snd_soc_update_bits(codec, M98090_REG_CLOCK_MODE, + M98090_USE_M1_MASK, 0); + return; + } + } + + /* Check for user calculated MI and NI ratios */ + for (i = 0; i < ARRAY_SIZE(user_pclk_rates); i++) { + if ((user_pclk_rates[i] == max98090->sysclk) && + (user_lrclk_rates[i] == max98090->lrclk)) { + dev_dbg(codec->dev, + "Found user supported PCLK to LRCLK rates\n"); + dev_dbg(codec->dev, "i %d ni %lld mi %lld\n", + i, ni_value[i], mi_value[i]); + + snd_soc_update_bits(codec, M98090_REG_CLOCK_MODE, + M98090_FREQ_MASK, 0); + snd_soc_update_bits(codec, M98090_REG_CLOCK_MODE, + M98090_USE_M1_MASK, + 1 << M98090_USE_M1_SHIFT); + + snd_soc_write(codec, M98090_REG_CLOCK_RATIO_NI_MSB, + (ni_value[i] >> 8) & 0x7F); + snd_soc_write(codec, M98090_REG_CLOCK_RATIO_NI_LSB, + ni_value[i] & 0xFF); + snd_soc_write(codec, M98090_REG_CLOCK_RATIO_MI_MSB, + (mi_value[i] >> 8) & 0x7F); + snd_soc_write(codec, M98090_REG_CLOCK_RATIO_MI_LSB, + mi_value[i] & 0xFF); + + return; + } + } + + /* + * Calculate based on MI = 65536 (not as good as either method above) + */ + snd_soc_update_bits(codec, M98090_REG_CLOCK_MODE, + M98090_FREQ_MASK, 0); + snd_soc_update_bits(codec, M98090_REG_CLOCK_MODE, + M98090_USE_M1_MASK, 0); + + /* + * Configure NI when operating as master + * Note: There is a small, but significant audio quality improvement + * by calculating ni and mi. + */ + ni = 65536ULL * (max98090->lrclk < 50000 ? 96ULL : 48ULL) + * (unsigned long long int)max98090->lrclk; + do_div(ni, (unsigned long long int)max98090->sysclk); + dev_info(codec->dev, "No better method found\n"); + dev_info(codec->dev, "Calculating ni %lld with mi 65536\n", ni); + snd_soc_write(codec, M98090_REG_CLOCK_RATIO_NI_MSB, + (ni >> 8) & 0x7F); + snd_soc_write(codec, M98090_REG_CLOCK_RATIO_NI_LSB, ni & 0xFF); +} + +static int max98090_dai_set_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec); + struct max98090_cdata *cdata; + u8 regval; + + max98090->dai_fmt = fmt; + cdata = &max98090->dai[0]; + + if (fmt != cdata->fmt) { + cdata->fmt = fmt; + + regval = 0; + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + /* Set to slave mode PLL - MAS mode off */ + snd_soc_write(codec, + M98090_REG_CLOCK_RATIO_NI_MSB, 0x00); + snd_soc_write(codec, + M98090_REG_CLOCK_RATIO_NI_LSB, 0x00); + snd_soc_update_bits(codec, M98090_REG_CLOCK_MODE, + M98090_USE_M1_MASK, 0); + break; + case SND_SOC_DAIFMT_CBM_CFM: + /* Set to master mode */ + if (max98090->tdm_slots == 4) { + /* TDM */ + regval |= M98090_MAS_MASK | + M98090_BSEL_64; + } else if (max98090->tdm_slots == 3) { + /* TDM */ + regval |= M98090_MAS_MASK | + M98090_BSEL_48; + } else { + /* Few TDM slots, or No TDM */ + regval |= M98090_MAS_MASK | + M98090_BSEL_32; + } + break; + case SND_SOC_DAIFMT_CBS_CFM: + case SND_SOC_DAIFMT_CBM_CFS: + default: + dev_err(codec->dev, "DAI clock mode unsupported"); + return -EINVAL; + } + snd_soc_write(codec, M98090_REG_MASTER_MODE, regval); + + regval = 0; + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + regval |= M98090_DLY_MASK; + break; + case SND_SOC_DAIFMT_LEFT_J: + break; + case SND_SOC_DAIFMT_RIGHT_J: + regval |= M98090_RJ_MASK; + break; + case SND_SOC_DAIFMT_DSP_A: + /* Not supported mode */ + default: + dev_err(codec->dev, "DAI format unsupported"); + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_NB_IF: + regval |= M98090_WCI_MASK; + break; + case SND_SOC_DAIFMT_IB_NF: + regval |= M98090_BCI_MASK; + break; + case SND_SOC_DAIFMT_IB_IF: + regval |= M98090_BCI_MASK|M98090_WCI_MASK; + break; + default: + dev_err(codec->dev, "DAI invert mode unsupported"); + return -EINVAL; + } + + /* + * This accommodates an inverted logic in the MAX98090 chip + * for Bit Clock Invert (BCI). The inverted logic is only + * seen for the case of TDM mode. The remaining cases have + * normal logic. + */ + if (max98090->tdm_slots > 1) { + regval ^= M98090_BCI_MASK; + } + + snd_soc_write(codec, + M98090_REG_INTERFACE_FORMAT, regval); + } + + return 0; +} + +static int max98090_set_tdm_slot(struct snd_soc_dai *codec_dai, + unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec); + struct max98090_cdata *cdata; + cdata = &max98090->dai[0]; + + if (slots < 0 || slots > 4) + return -EINVAL; + + max98090->tdm_slots = slots; + max98090->tdm_width = slot_width; + + if (max98090->tdm_slots > 1) { + /* SLOTL SLOTR SLOTDLY */ + snd_soc_write(codec, M98090_REG_TDM_FORMAT, + 0 << M98090_TDM_SLOTL_SHIFT | + 1 << M98090_TDM_SLOTR_SHIFT | + 0 << M98090_TDM_SLOTDLY_SHIFT); + + /* FSW TDM */ + snd_soc_update_bits(codec, M98090_REG_TDM_CONTROL, + M98090_TDM_MASK, + M98090_TDM_MASK); + } + + /* + * Normally advisable to set TDM first, but this permits either order + */ + cdata->fmt = 0; + max98090_dai_set_fmt(codec_dai, max98090->dai_fmt); + + return 0; +} + +static int max98090_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) +{ + struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec); + int ret; + + switch (level) { + case SND_SOC_BIAS_ON: + if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + ret = regcache_sync(max98090->regmap); + + if (ret != 0) { + dev_err(codec->dev, + "Failed to sync cache: %d\n", ret); + return ret; + } + } + + if (max98090->jack_state == M98090_JACK_STATE_HEADSET) { + /* + * Set to normal bias level. + */ + snd_soc_update_bits(codec, M98090_REG_MIC_BIAS_VOLTAGE, + M98090_MBVSEL_MASK, M98090_MBVSEL_2V8); + } + break; + + case SND_SOC_BIAS_PREPARE: + break; + + case SND_SOC_BIAS_STANDBY: + case SND_SOC_BIAS_OFF: + /* Set internal pull-up to lowest power mode */ + snd_soc_update_bits(codec, M98090_REG_JACK_DETECT, + M98090_JDWK_MASK, M98090_JDWK_MASK); + regcache_mark_dirty(max98090->regmap); + break; + } + codec->dapm.bias_level = level; + return 0; +} + +static const int comp_pclk_rates[] = { + 11289600, 12288000, 12000000, 13000000, 19200000 +}; + +static const int dmic_micclk[] = { + 2, 2, 2, 2, 4, 2 +}; + +static const int comp_lrclk_rates[] = { + 8000, 16000, 32000, 44100, 48000, 96000 +}; + +static const int dmic_comp[6][6] = { + {7, 8, 3, 3, 3, 3}, + {7, 8, 3, 3, 3, 3}, + {7, 8, 3, 3, 3, 3}, + {7, 8, 3, 1, 1, 1}, + {7, 8, 3, 1, 2, 2}, + {7, 8, 3, 3, 3, 3} +}; + +static int max98090_dai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec); + struct max98090_cdata *cdata; + int i, j; + + cdata = &max98090->dai[0]; + max98090->bclk = snd_soc_params_to_bclk(params); + if (params_channels(params) == 1) + max98090->bclk *= 2; + + max98090->lrclk = params_rate(params); + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + snd_soc_update_bits(codec, M98090_REG_INTERFACE_FORMAT, + M98090_WS_MASK, 0); + break; + default: + return -EINVAL; + } + + max98090_configure_bclk(codec); + + cdata->rate = max98090->lrclk; + + /* Update filter mode */ + if (max98090->lrclk < 24000) + snd_soc_update_bits(codec, M98090_REG_FILTER_CONFIG, + M98090_MODE_MASK, 0); + else + snd_soc_update_bits(codec, M98090_REG_FILTER_CONFIG, + M98090_MODE_MASK, M98090_MODE_MASK); + + /* Update sample rate mode */ + if (max98090->lrclk < 50000) + snd_soc_update_bits(codec, M98090_REG_FILTER_CONFIG, + M98090_DHF_MASK, 0); + else + snd_soc_update_bits(codec, M98090_REG_FILTER_CONFIG, + M98090_DHF_MASK, M98090_DHF_MASK); + + /* Check for supported PCLK to LRCLK ratios */ + for (j = 0; j < ARRAY_SIZE(comp_pclk_rates); j++) { + if (comp_pclk_rates[j] == max98090->sysclk) { + break; + } + } + + for (i = 0; i < ARRAY_SIZE(comp_lrclk_rates) - 1; i++) { + if (max98090->lrclk <= (comp_lrclk_rates[i] + + comp_lrclk_rates[i + 1]) / 2) { + break; + } + } + + snd_soc_update_bits(codec, M98090_REG_DIGITAL_MIC_ENABLE, + M98090_MICCLK_MASK, + dmic_micclk[j] << M98090_MICCLK_SHIFT); + + snd_soc_update_bits(codec, M98090_REG_DIGITAL_MIC_CONFIG, + M98090_DMIC_COMP_MASK, + dmic_comp[j][i] << M98090_DMIC_COMP_SHIFT); + + return 0; +} + +/* + * PLL / Sysclk + */ +static int max98090_dai_set_sysclk(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_codec *codec = dai->codec; + struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec); + + /* Requested clock frequency is already setup */ + if (freq == max98090->sysclk) + return 0; + + /* Setup clocks for slave mode, and using the PLL + * PSCLK = 0x01 (when master clk is 10MHz to 20MHz) + * 0x02 (when master clk is 20MHz to 40MHz).. + * 0x03 (when master clk is 40MHz to 60MHz).. + */ + if ((freq >= 10000000) && (freq < 20000000)) { + snd_soc_write(codec, M98090_REG_SYSTEM_CLOCK, + M98090_PSCLK_DIV1); + } else if ((freq >= 20000000) && (freq < 40000000)) { + snd_soc_write(codec, M98090_REG_SYSTEM_CLOCK, + M98090_PSCLK_DIV2); + } else if ((freq >= 40000000) && (freq < 60000000)) { + snd_soc_write(codec, M98090_REG_SYSTEM_CLOCK, + M98090_PSCLK_DIV4); + } else { + dev_err(codec->dev, "Invalid master clock frequency\n"); + return -EINVAL; + } + + max98090->sysclk = freq; + + max98090_configure_bclk(codec); + + return 0; +} + +static int max98090_dai_digital_mute(struct snd_soc_dai *codec_dai, int mute) +{ + struct snd_soc_codec *codec = codec_dai->codec; + int regval; + + regval = mute ? M98090_DVM_MASK : 0; + snd_soc_update_bits(codec, M98090_REG_DAI_PLAYBACK_LEVEL, + M98090_DVM_MASK, regval); + + return 0; +} + +static void max98090_jack_work(struct work_struct *work) +{ + struct max98090_priv *max98090 = container_of(work, + struct max98090_priv, + jack_work.work); + struct snd_soc_codec *codec = max98090->codec; + struct snd_soc_dapm_context *dapm = &codec->dapm; + int status = 0; + int reg; + + /* Read a second time */ + if (max98090->jack_state == M98090_JACK_STATE_NO_HEADSET) { + + /* Strong pull up allows mic detection */ + snd_soc_update_bits(codec, M98090_REG_JACK_DETECT, + M98090_JDWK_MASK, 0); + + msleep(50); + + reg = snd_soc_read(codec, M98090_REG_JACK_STATUS); + + /* Weak pull up allows only insertion detection */ + snd_soc_update_bits(codec, M98090_REG_JACK_DETECT, + M98090_JDWK_MASK, M98090_JDWK_MASK); + } else { + reg = snd_soc_read(codec, M98090_REG_JACK_STATUS); + } + + reg = snd_soc_read(codec, M98090_REG_JACK_STATUS); + + switch (reg & (M98090_LSNS_MASK | M98090_JKSNS_MASK)) { + case M98090_LSNS_MASK | M98090_JKSNS_MASK: + dev_dbg(codec->dev, "No Headset Detected\n"); + + max98090->jack_state = M98090_JACK_STATE_NO_HEADSET; + + status |= 0; + + break; + + case 0: + if (max98090->jack_state == + M98090_JACK_STATE_HEADSET) { + + dev_dbg(codec->dev, + "Headset Button Down Detected\n"); + + /* + * max98090_headset_button_event(codec) + * could be defined, then called here. + */ + + status |= SND_JACK_HEADSET; + status |= SND_JACK_BTN_0; + + break; + } + + /* Line is reported as Headphone */ + /* Nokia Headset is reported as Headphone */ + /* Mono Headphone is reported as Headphone */ + dev_dbg(codec->dev, "Headphone Detected\n"); + + max98090->jack_state = M98090_JACK_STATE_HEADPHONE; + + status |= SND_JACK_HEADPHONE; + + break; + + case M98090_JKSNS_MASK: + dev_dbg(codec->dev, "Headset Detected\n"); + + max98090->jack_state = M98090_JACK_STATE_HEADSET; + + status |= SND_JACK_HEADSET; + + break; + + default: + dev_dbg(codec->dev, "Unrecognized Jack Status\n"); + break; + } + + snd_soc_jack_report(max98090->jack, status, + SND_JACK_HEADSET | SND_JACK_BTN_0); + + snd_soc_dapm_sync(dapm); +} + +static irqreturn_t max98090_interrupt(int irq, void *data) +{ + struct snd_soc_codec *codec = data; + struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec); + int ret; + unsigned int mask; + unsigned int active; + + dev_dbg(codec->dev, "***** max98090_interrupt *****\n"); + + ret = regmap_read(max98090->regmap, M98090_REG_INTERRUPT_S, &mask); + + if (ret != 0) { + dev_err(codec->dev, + "failed to read M98090_REG_INTERRUPT_S: %d\n", + ret); + return IRQ_NONE; + } + + ret = regmap_read(max98090->regmap, M98090_REG_DEVICE_STATUS, &active); + + if (ret != 0) { + dev_err(codec->dev, + "failed to read M98090_REG_DEVICE_STATUS: %d\n", + ret); + return IRQ_NONE; + } + + dev_dbg(codec->dev, "active=0x%02x mask=0x%02x -> active=0x%02x\n", + active, mask, active & mask); + + active &= mask; + + if (!active) + return IRQ_NONE; + + if (active & M98090_CLD_MASK) { + dev_err(codec->dev, "M98090_CLD_MASK\n"); + } + + if (active & M98090_SLD_MASK) { + dev_dbg(codec->dev, "M98090_SLD_MASK\n"); + } + + if (active & M98090_ULK_MASK) { + dev_err(codec->dev, "M98090_ULK_MASK\n"); + } + + if (active & M98090_JDET_MASK) { + dev_dbg(codec->dev, "M98090_JDET_MASK\n"); + + pm_wakeup_event(codec->dev, 100); + + schedule_delayed_work(&max98090->jack_work, + msecs_to_jiffies(100)); + } + + if (active & M98090_DRCACT_MASK) { + dev_dbg(codec->dev, "M98090_DRCACT_MASK\n"); + } + + if (active & M98090_DRCCLP_MASK) { + dev_err(codec->dev, "M98090_DRCCLP_MASK\n"); + } + + return IRQ_HANDLED; +} + +/** + * max98090_mic_detect - Enable microphone detection via the MAX98090 IRQ + * + * @codec: MAX98090 codec + * @jack: jack to report detection events on + * + * Enable microphone detection via IRQ on the MAX98090. If GPIOs are + * being used to bring out signals to the processor then only platform + * data configuration is needed for MAX98090 and processor GPIOs should + * be configured using snd_soc_jack_add_gpios() instead. + * + * If no jack is supplied detection will be disabled. + */ +int max98090_mic_detect(struct snd_soc_codec *codec, + struct snd_soc_jack *jack) +{ + struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "max98090_mic_detect\n"); + + max98090->jack = jack; + if (jack) { + snd_soc_update_bits(codec, M98090_REG_INTERRUPT_S, + M98090_IJDET_MASK, + 1 << M98090_IJDET_SHIFT); + } else { + snd_soc_update_bits(codec, M98090_REG_INTERRUPT_S, + M98090_IJDET_MASK, + 0); + } + + /* Send an initial empty report */ + snd_soc_jack_report(max98090->jack, 0, + SND_JACK_HEADSET | SND_JACK_BTN_0); + + schedule_delayed_work(&max98090->jack_work, + msecs_to_jiffies(100)); + + return 0; +} +EXPORT_SYMBOL_GPL(max98090_mic_detect); + +#define MAX98090_RATES SNDRV_PCM_RATE_8000_96000 +#define MAX98090_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE) + +static struct snd_soc_dai_ops max98090_dai_ops = { + .set_sysclk = max98090_dai_set_sysclk, + .set_fmt = max98090_dai_set_fmt, + .set_tdm_slot = max98090_set_tdm_slot, + .hw_params = max98090_dai_hw_params, + .digital_mute = max98090_dai_digital_mute, +}; + +static struct snd_soc_dai_driver max98090_dai[] = { +{ + .name = "HiFi", + .playback = { + .stream_name = "HiFi Playback", + .channels_min = 2, + .channels_max = 2, + .rates = MAX98090_RATES, + .formats = MAX98090_FORMATS, + }, + .capture = { + .stream_name = "HiFi Capture", + .channels_min = 1, + .channels_max = 2, + .rates = MAX98090_RATES, + .formats = MAX98090_FORMATS, + }, + .ops = &max98090_dai_ops, +} +}; + +static void max98090_handle_pdata(struct snd_soc_codec *codec) +{ + struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec); + struct max98090_pdata *pdata = max98090->pdata; + + if (!pdata) { + dev_err(codec->dev, "No platform data\n"); + return; + } + +} + +static int max98090_probe(struct snd_soc_codec *codec) +{ + struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec); + struct max98090_cdata *cdata; + int ret = 0; + + dev_dbg(codec->dev, "max98090_probe\n"); + + max98090->codec = codec; + + codec->control_data = max98090->regmap; + + ret = snd_soc_codec_set_cache_io(codec, 8, 8, SND_SOC_REGMAP); + if (ret != 0) { + dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret); + return ret; + } + + /* Reset the codec, the DSP core, and disable all interrupts */ + max98090_reset(max98090); + + /* Initialize private data */ + + max98090->sysclk = (unsigned)-1; + + cdata = &max98090->dai[0]; + cdata->rate = (unsigned)-1; + cdata->fmt = (unsigned)-1; + + max98090->lin_state = 0; + max98090->pa1en = 0; + max98090->pa2en = 0; + max98090->extmic_mux = 0; + + ret = snd_soc_read(codec, M98090_REG_REVISION_ID); + if (ret < 0) { + dev_err(codec->dev, "Failed to read device revision: %d\n", + ret); + goto err_access; + } + + if ((ret >= M98090_REVA) && (ret <= M98090_REVA + 0x0f)) { + max98090->devtype = MAX98090; + dev_info(codec->dev, "MAX98090 REVID=0x%02x\n", ret); + } else if ((ret >= M98091_REVA) && (ret <= M98091_REVA + 0x0f)) { + max98090->devtype = MAX98091; + dev_info(codec->dev, "MAX98091 REVID=0x%02x\n", ret); + } else { + max98090->devtype = MAX98090; + dev_err(codec->dev, "Unrecognized revision 0x%02x\n", ret); + } + + max98090->jack_state = M98090_JACK_STATE_NO_HEADSET; + + INIT_DELAYED_WORK(&max98090->jack_work, max98090_jack_work); + + /* Enable jack detection */ + snd_soc_write(codec, M98090_REG_JACK_DETECT, + M98090_JDETEN_MASK | M98090_JDEB_25MS); + + /* Register for interrupts */ + dev_dbg(codec->dev, "irq = %d\n", max98090->irq); + + ret = request_threaded_irq(max98090->irq, NULL, + max98090_interrupt, IRQF_TRIGGER_FALLING, + "max98090_interrupt", codec); + if (ret < 0) { + dev_err(codec->dev, "request_irq failed: %d\n", + ret); + } + + /* + * Clear any old interrupts. + * An old interrupt ocurring prior to installing the ISR + * can keep a new interrupt from generating a trigger. + */ + snd_soc_read(codec, M98090_REG_DEVICE_STATUS); + + /* High Performance is default */ + snd_soc_update_bits(codec, M98090_REG_DAC_CONTROL, + M98090_DACHP_MASK, + 1 << M98090_DACHP_SHIFT); + snd_soc_update_bits(codec, M98090_REG_DAC_CONTROL, + M98090_PERFMODE_MASK, + 0 << M98090_PERFMODE_SHIFT); + snd_soc_update_bits(codec, M98090_REG_ADC_CONTROL, + M98090_ADCHP_MASK, + 1 << M98090_ADCHP_SHIFT); + + /* Turn on VCM bandgap reference */ + snd_soc_write(codec, M98090_REG_BIAS_CONTROL, + M98090_VCM_MODE_MASK); + + max98090_handle_pdata(codec); + + max98090_add_widgets(codec); + +err_access: + return ret; +} + +static int max98090_remove(struct snd_soc_codec *codec) +{ + struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec); + + cancel_delayed_work_sync(&max98090->jack_work); + + return 0; +} + +static struct snd_soc_codec_driver soc_codec_dev_max98090 = { + .probe = max98090_probe, + .remove = max98090_remove, + .set_bias_level = max98090_set_bias_level, +}; + +static const struct regmap_config max98090_regmap = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = MAX98090_MAX_REGISTER, + .reg_defaults = max98090_reg, + .num_reg_defaults = ARRAY_SIZE(max98090_reg), + .volatile_reg = max98090_volatile_register, + .readable_reg = max98090_readable_register, + .cache_type = REGCACHE_RBTREE, +}; + +static int max98090_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct max98090_priv *max98090; + int ret; + + pr_debug("max98090_i2c_probe\n"); + + max98090 = devm_kzalloc(&i2c->dev, sizeof(struct max98090_priv), + GFP_KERNEL); + if (max98090 == NULL) + return -ENOMEM; + + max98090->devtype = id->driver_data; + i2c_set_clientdata(i2c, max98090); + max98090->control_data = i2c; + max98090->pdata = i2c->dev.platform_data; + max98090->irq = i2c->irq; + + max98090->regmap = regmap_init_i2c(i2c, &max98090_regmap); + if (IS_ERR(max98090->regmap)) { + ret = PTR_ERR(max98090->regmap); + dev_err(&i2c->dev, "Failed to allocate regmap: %d\n", ret); + goto err_enable; + } + + ret = snd_soc_register_codec(&i2c->dev, + &soc_codec_dev_max98090, max98090_dai, + ARRAY_SIZE(max98090_dai)); + if (ret < 0) + regmap_exit(max98090->regmap); + +err_enable: + return ret; +} + +static int max98090_i2c_remove(struct i2c_client *client) +{ + struct max98090_priv *max98090 = dev_get_drvdata(&client->dev); + snd_soc_unregister_codec(&client->dev); + regmap_exit(max98090->regmap); + return 0; +} + +static int max98090_runtime_resume(struct device *dev) +{ + struct max98090_priv *max98090 = dev_get_drvdata(dev); + + regcache_cache_only(max98090->regmap, false); + + regcache_sync(max98090->regmap); + + return 0; +} + +static int max98090_runtime_suspend(struct device *dev) +{ + struct max98090_priv *max98090 = dev_get_drvdata(dev); + + regcache_cache_only(max98090->regmap, true); + + return 0; +} + +static struct dev_pm_ops max98090_pm = { + SET_RUNTIME_PM_OPS(max98090_runtime_suspend, + max98090_runtime_resume, NULL) +}; + +static const struct i2c_device_id max98090_i2c_id[] = { + { "max98090", MAX98090 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, max98090_i2c_id); + +static struct i2c_driver max98090_i2c_driver = { + .driver = { + .name = "max98090", + .owner = THIS_MODULE, + .pm = &max98090_pm, + }, + .probe = max98090_i2c_probe, + .remove = max98090_i2c_remove, + .id_table = max98090_i2c_id, +}; + +module_i2c_driver(max98090_i2c_driver); + +MODULE_DESCRIPTION("ALSA SoC MAX98090 driver"); +MODULE_AUTHOR("Peter Hsiang, Jesse Marroqin, Jerry Wong"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/max98090.h b/sound/soc/codecs/max98090.h new file mode 100755 index 0000000..7e103f2 --- /dev/null +++ b/sound/soc/codecs/max98090.h @@ -0,0 +1,1549 @@ +/* + * max98090.h -- MAX98090 ALSA SoC Audio driver + * + * Copyright 2011-2012 Maxim Integrated Products + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _MAX98090_H +#define _MAX98090_H + +#include + +/* One can override the Linux version here with an explicit version number */ +#define M98090_LINUX_VERSION LINUX_VERSION_CODE + +/* + * MAX98090 Register Definitions + */ + +#define M98090_REG_SOFTWARE_RESET 0x00 +#define M98090_REG_DEVICE_STATUS 0x01 +#define M98090_REG_JACK_STATUS 0x02 +#define M98090_REG_INTERRUPT_S 0x03 +#define M98090_REG_QUICK_SYSTEM_CLOCK 0x04 +#define M98090_REG_QUICK_SAMPLE_RATE 0x05 +#define M98090_REG_DAI_INTERFACE 0x06 +#define M98090_REG_DAC_PATH 0x07 +#define M98090_REG_MIC_DIRECT_TO_ADC 0x08 +#define M98090_REG_LINE_TO_ADC 0x09 +#define M98090_REG_ANALOG_MIC_LOOP 0x0A +#define M98090_REG_ANALOG_LINE_LOOP 0x0B +#define M98090_REG_RESERVED 0x0C +#define M98090_REG_LINE_INPUT_CONFIG 0x0D +#define M98090_REG_LINE_INPUT_LEVEL 0x0E +#define M98090_REG_INPUT_MODE 0x0F +#define M98090_REG_MIC1_INPUT_LEVEL 0x10 +#define M98090_REG_MIC2_INPUT_LEVEL 0x11 +#define M98090_REG_MIC_BIAS_VOLTAGE 0x12 +#define M98090_REG_DIGITAL_MIC_ENABLE 0x13 +#define M98090_REG_DIGITAL_MIC_CONFIG 0x14 +#define M98090_REG_LEFT_ADC_MIXER 0x15 +#define M98090_REG_RIGHT_ADC_MIXER 0x16 +#define M98090_REG_LEFT_ADC_LEVEL 0x17 +#define M98090_REG_RIGHT_ADC_LEVEL 0x18 +#define M98090_REG_ADC_BIQUAD_LEVEL 0x19 +#define M98090_REG_ADC_SIDETONE 0x1A +#define M98090_REG_SYSTEM_CLOCK 0x1B +#define M98090_REG_CLOCK_MODE 0x1C +#define M98090_REG_CLOCK_RATIO_NI_MSB 0x1D +#define M98090_REG_CLOCK_RATIO_NI_LSB 0x1E +#define M98090_REG_CLOCK_RATIO_MI_MSB 0x1F +#define M98090_REG_CLOCK_RATIO_MI_LSB 0x20 +#define M98090_REG_MASTER_MODE 0x21 +#define M98090_REG_INTERFACE_FORMAT 0x22 +#define M98090_REG_TDM_CONTROL 0x23 +#define M98090_REG_TDM_FORMAT 0x24 +#define M98090_REG_IO_CONFIGURATION 0x25 +#define M98090_REG_FILTER_CONFIG 0x26 +#define M98090_REG_DAI_PLAYBACK_LEVEL 0x27 +#define M98090_REG_DAI_PLAYBACK_LEVEL_EQ 0x28 +#define M98090_REG_LEFT_HP_MIXER 0x29 +#define M98090_REG_RIGHT_HP_MIXER 0x2A +#define M98090_REG_HP_CONTROL 0x2B +#define M98090_REG_LEFT_HP_VOLUME 0x2C +#define M98090_REG_RIGHT_HP_VOLUME 0x2D +#define M98090_REG_LEFT_SPK_MIXER 0x2E +#define M98090_REG_RIGHT_SPK_MIXER 0x2F +#define M98090_REG_SPK_CONTROL 0x30 +#define M98090_REG_LEFT_SPK_VOLUME 0x31 +#define M98090_REG_RIGHT_SPK_VOLUME 0x32 +#define M98090_REG_DRC_TIMING 0x33 +#define M98090_REG_DRC_COMPRESSOR 0x34 +#define M98090_REG_DRC_EXPANDER 0x35 +#define M98090_REG_DRC_GAIN 0x36 +#define M98090_REG_RCV_LOUTL_MIXER 0x37 +#define M98090_REG_RCV_LOUTL_CONTROL 0x38 +#define M98090_REG_RCV_LOUTL_VOLUME 0x39 +#define M98090_REG_LOUTR_MIXER 0x3A +#define M98090_REG_LOUTR_CONTROL 0x3B +#define M98090_REG_LOUTR_VOLUME 0x3C +#define M98090_REG_JACK_DETECT 0x3D +#define M98090_REG_INPUT_ENABLE 0x3E +#define M98090_REG_OUTPUT_ENABLE 0x3F +#define M98090_REG_LEVEL_CONTROL 0x40 +#define M98090_REG_DSP_FILTER_ENABLE 0x41 +#define M98090_REG_BIAS_CONTROL 0x42 +#define M98090_REG_DAC_CONTROL 0x43 +#define M98090_REG_ADC_CONTROL 0x44 +#define M98090_REG_DEVICE_SHUTDOWN 0x45 +#define M98090_REG_EQUALIZER_BASE 0x46 +#define M98090_REG_RECORD_BIQUAD_BASE 0xAF +#define M98090_REG_DMIC3_VOLUME 0xBE +#define M98090_REG_DMIC4_VOLUME 0xBF +#define M98090_REG_DMIC34_BQ_PREATTEN 0xC0 +#define M98090_REG_RECORD_TDM_SLOT 0xC1 +#define M98090_REG_SAMPLE_RATE 0xC2 +#define M98090_REG_DMIC34_BIQUAD_BASE 0xC3 +#define M98090_REG_REVISION_ID 0xFF + +#define M98090_REG_CNT (0xFF+1) +#define MAX98090_MAX_REGISTER 0xFF + +/* MAX98090 Register Bit Fields */ + +/* + * M98090_REG_SOFTWARE_RESET + */ +#define M98090_SWRESET_MASK (1<<7) +#define M98090_SWRESET_SHIFT 7 +#define M98090_SWRESET_WIDTH 1 + +/* + * M98090_REG_DEVICE_STATUS + */ +#define M98090_CLD_MASK (1<<7) +#define M98090_CLD_SHIFT 7 +#define M98090_CLD_WIDTH 1 +#define M98090_SLD_MASK (1<<6) +#define M98090_SLD_SHIFT 6 +#define M98090_SLD_WIDTH 1 +#define M98090_ULK_MASK (1<<5) +#define M98090_ULK_SHIFT 5 +#define M98090_ULK_WIDTH 1 +#define M98090_JDET_MASK (1<<2) +#define M98090_JDET_SHIFT 2 +#define M98090_JDET_WIDTH 1 +#define M98090_DRCACT_MASK (1<<1) +#define M98090_DRCACT_SHIFT 1 +#define M98090_DRCACT_WIDTH 1 +#define M98090_DRCCLP_MASK (1<<0) +#define M98090_DRCCLP_SHIFT 0 +#define M98090_DRCCLP_WIDTH 1 + +/* + * M98090_REG_JACK_STATUS + */ +#define M98090_LSNS_MASK (1<<2) +#define M98090_LSNS_SHIFT 2 +#define M98090_LSNS_WIDTH 1 +#define M98090_JKSNS_MASK (1<<1) +#define M98090_JKSNS_SHIFT 1 +#define M98090_JKSNS_WIDTH 1 + +/* + * M98090_REG_INTERRUPT_S + */ +#define M98090_ICLD_MASK (1<<7) +#define M98090_ICLD_SHIFT 7 +#define M98090_ICLD_WIDTH 1 +#define M98090_ISLD_MASK (1<<6) +#define M98090_ISLD_SHIFT 6 +#define M98090_ISLD_WIDTH 1 +#define M98090_IULK_MASK (1<<5) +#define M98090_IULK_SHIFT 5 +#define M98090_IULK_WIDTH 1 +#define M98090_IJDET_MASK (1<<2) +#define M98090_IJDET_SHIFT 2 +#define M98090_IJDET_WIDTH 1 +#define M98090_IDRCACT_MASK (1<<1) +#define M98090_IDRCACT_SHIFT 1 +#define M98090_IDRCACT_WIDTH 1 +#define M98090_IDRCCLP_MASK (1<<0) +#define M98090_IDRCCLP_SHIFT 0 +#define M98090_IDRCCLP_WIDTH 1 + +/* + * M98090_REG_QUICK_SYSTEM_CLOCK + */ +#define M98090_26M_MASK (1<<7) +#define M98090_26M_SHIFT 7 +#define M98090_26M_WIDTH 1 +#define M98090_19P2M_MASK (1<<6) +#define M98090_19P2M_SHIFT 6 +#define M98090_19P2M_WIDTH 1 +#define M98090_13M_MASK (1<<5) +#define M98090_13M_SHIFT 5 +#define M98090_13M_WIDTH 1 +#define M98090_12P288M_MASK (1<<4) +#define M98090_12P288M_SHIFT 4 +#define M98090_12P288M_WIDTH 1 +#define M98090_12M_MASK (1<<3) +#define M98090_12M_SHIFT 3 +#define M98090_12M_WIDTH 1 +#define M98090_11P2896M_MASK (1<<2) +#define M98090_11P2896M_SHIFT 2 +#define M98090_11P2896M_WIDTH 1 +#define M98090_256FS_MASK (1<<0) +#define M98090_256FS_SHIFT 0 +#define M98090_256FS_WIDTH 1 +#define M98090_CLK_ALL_SHIFT 0 +#define M98090_CLK_ALL_WIDTH 8 +#define M98090_CLK_ALL_NUM (1<> 8) & 0xff) +#define M98090_BYTE0(w) (w & 0xff) + +/* Silicon revision number */ +#define M98090_REVA 0x40 +#define M98091_REVA 0x50 + +enum max98090_type { + MAX98090, + MAX98091, +}; + +struct max98090_cdata { + unsigned int rate; + unsigned int fmt; +}; + +struct max98090_priv { + struct regmap *regmap; + struct snd_soc_codec *codec; + enum max98090_type devtype; + void *control_data; + struct max98090_pdata *pdata; + unsigned int sysclk; + unsigned int bclk; + unsigned int lrclk; + struct max98090_cdata dai[1]; + int irq; + int jack_state; + struct delayed_work jack_work; + struct snd_soc_jack *jack; + unsigned int dai_fmt; + int tdm_slots; + int tdm_width; + u8 lin_state; + unsigned int pa1en; + unsigned int pa2en; + unsigned int extmic_mux; + unsigned int sidetone; +}; + +int max98090_mic_detect(struct snd_soc_codec *codec, + struct snd_soc_jack *jack); + +#endif -- cgit v0.10.2 From 262d62eb55e105b6335ec0e9ba89f6ec7247cb48 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Wed, 6 Feb 2013 17:24:00 +0100 Subject: ALSA: add missing HAS_IOPORT and GENERIC_HARDIRQS dependencies MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix these two compile errors on s390 which does not have HAS_IOPORT nor GENERIC_HARDIRQS: sound/pci/lx6464es/lx6464es.c: In function ‘snd_lx6464es_free’: sound/pci/lx6464es/lx6464es.c:565:2: error: implicit declaration of function ‘ioport_unmap’ sound/soc/codecs/wm8903.c: In function ‘wm8903_set_pdata_irq_trigger’: sound/soc/codecs/wm8903.c:1954:9: error: implicit declaration of function ‘irq_get_irq_data’ Signed-off-by: Heiko Carstens Signed-off-by: Takashi Iwai diff --git a/sound/pci/Kconfig b/sound/pci/Kconfig index 947cfb4..fe6fa93 100644 --- a/sound/pci/Kconfig +++ b/sound/pci/Kconfig @@ -678,6 +678,7 @@ config SND_LOLA config SND_LX6464ES tristate "Digigram LX6464ES" + depends on HAS_IOPORT select SND_PCM help Say Y here to include support for Digigram LX6464ES boards. diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 3a84782..615c478 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -98,7 +98,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_WM8782 select SND_SOC_WM8804 if SND_SOC_I2C_AND_SPI select SND_SOC_WM8900 if I2C - select SND_SOC_WM8903 if I2C + select SND_SOC_WM8903 if I2C && GENERIC_HARDIRQS select SND_SOC_WM8904 if I2C select SND_SOC_WM8940 if I2C select SND_SOC_WM8955 if I2C -- cgit v0.10.2 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 diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 4b099c6..b5d5b20 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 v0.10.2 From e38b9b7478d57701fbcbaafdde169aa1a88d0eca Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 6 Feb 2013 13:52:42 +0000 Subject: ASoC: compress: Only mute playback streams Otherwise capture activity on a compressed DAI would mute any playback on the same DAI. Signed-off-by: Mark Brown Acked-by: Vinod Koul Acked-by: Liam Girdwood diff --git a/sound/soc/soc-compress.c b/sound/soc/soc-compress.c index c81aeec..35726cb 100644 --- a/sound/soc/soc-compress.c +++ b/sound/soc/soc-compress.c @@ -116,13 +116,12 @@ static int soc_compr_free(struct snd_compr_stream *cstream) if (cstream->direction == SND_COMPRESS_PLAYBACK) { cpu_dai->playback_active--; codec_dai->playback_active--; + snd_soc_dai_digital_mute(codec_dai, 1); } else { cpu_dai->capture_active--; codec_dai->capture_active--; } - snd_soc_dai_digital_mute(codec_dai, 1); - cpu_dai->active--; codec_dai->active--; codec->active--; @@ -179,10 +178,16 @@ static int soc_compr_trigger(struct snd_compr_stream *cstream, int cmd) goto out; } - if (cmd == SNDRV_PCM_TRIGGER_START) - snd_soc_dai_digital_mute(codec_dai, 0); - else if (cmd == SNDRV_PCM_TRIGGER_STOP) - snd_soc_dai_digital_mute(codec_dai, 1); + if (cstream->direction == SND_COMPRESS_PLAYBACK) { + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + snd_soc_dai_digital_mute(codec_dai, 0); + break; + case SNDRV_PCM_TRIGGER_STOP: + snd_soc_dai_digital_mute(codec_dai, 1); + break; + } + } out: mutex_unlock(&rtd->pcm_mutex); -- cgit v0.10.2 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 diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index f82a64d..3c92514 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 6c59272..0be1826 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 5e02f26..0fee8fa 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 85236da..899c4fb 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 v0.10.2 From da18396f949ecaa45007d3aeb1b81bd6da092811 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 6 Feb 2013 15:44:07 +0000 Subject: ASoC: core: Allow digital mute for capture Help avoid noise from the power up of the capture path propagating through into the start of the recording (especially noise caused by the ramp of microphone biases) by keeping the capture muted until after we've finished powering things up with DAPM in the same manner we do for playback. This allows us to take advantage of soft mute support in the hardware more effectively and is more consistent. The core code using the existing digital mute operation is updated to take advantage of this. Some additional cases in the soc-pcm code and suspend will need separate handling but these are less practically relevant than the main runtime stream start/stop case. Rather than refactor the digital mute function in every single driver a new operation is added for drivers taking advantage of this functionality, the old operation should be phased out over time. Signed-off-by: Mark Brown Acked-by Vinod Koul Acked-by: Liam Girdwood diff --git a/include/sound/soc-dai.h b/include/sound/soc-dai.h index 3953cea..a680f23 100644 --- a/include/sound/soc-dai.h +++ b/include/sound/soc-dai.h @@ -126,7 +126,8 @@ int snd_soc_dai_set_channel_map(struct snd_soc_dai *dai, int snd_soc_dai_set_tristate(struct snd_soc_dai *dai, int tristate); /* Digital Audio Interface mute */ -int snd_soc_dai_digital_mute(struct snd_soc_dai *dai, int mute); +int snd_soc_dai_digital_mute(struct snd_soc_dai *dai, int mute, + int direction); struct snd_soc_dai_ops { /* @@ -157,6 +158,7 @@ struct snd_soc_dai_ops { * Called by soc-core to minimise any pops. */ int (*digital_mute)(struct snd_soc_dai *dai, int mute); + int (*mute_stream)(struct snd_soc_dai *dai, int mute, int stream); /* * ALSA PCM audio operations - all optional. diff --git a/sound/soc/soc-compress.c b/sound/soc/soc-compress.c index 35726cb..b5b3db7 100644 --- a/sound/soc/soc-compress.c +++ b/sound/soc/soc-compress.c @@ -116,12 +116,13 @@ static int soc_compr_free(struct snd_compr_stream *cstream) if (cstream->direction == SND_COMPRESS_PLAYBACK) { cpu_dai->playback_active--; codec_dai->playback_active--; - snd_soc_dai_digital_mute(codec_dai, 1); } else { cpu_dai->capture_active--; codec_dai->capture_active--; } + snd_soc_dai_digital_mute(codec_dai, 1, cstream->direction); + cpu_dai->active--; codec_dai->active--; codec->active--; @@ -178,15 +179,13 @@ static int soc_compr_trigger(struct snd_compr_stream *cstream, int cmd) goto out; } - if (cstream->direction == SND_COMPRESS_PLAYBACK) { - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - snd_soc_dai_digital_mute(codec_dai, 0); - break; - case SNDRV_PCM_TRIGGER_STOP: - snd_soc_dai_digital_mute(codec_dai, 1); - break; - } + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + snd_soc_dai_digital_mute(codec_dai, 0, cstream->direction); + break; + case SNDRV_PCM_TRIGGER_STOP: + snd_soc_dai_digital_mute(codec_dai, 1, cstream->direction); + break; } out: diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 2370063..4eac227 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -3540,12 +3540,20 @@ EXPORT_SYMBOL_GPL(snd_soc_dai_set_tristate); * snd_soc_dai_digital_mute - configure DAI system or master clock. * @dai: DAI * @mute: mute enable + * @direction: stream to mute * * Mutes the DAI DAC. */ -int snd_soc_dai_digital_mute(struct snd_soc_dai *dai, int mute) +int snd_soc_dai_digital_mute(struct snd_soc_dai *dai, int mute, + int direction) { - if (dai->driver && dai->driver->ops->digital_mute) + if (!dai->driver) + return -ENOTSUPP; + + if (dai->driver->ops->mute_stream) + return dai->driver->ops->mute_stream(dai, mute, direction); + else if (direction == SNDRV_PCM_STREAM_PLAYBACK && + dai->driver->ops->digital_mute) return dai->driver->ops->digital_mute(dai, mute); else return -ENOTSUPP; diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 1e36bc8..4d664f3 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -3247,14 +3247,16 @@ static int snd_soc_dai_link_event(struct snd_soc_dapm_widget *w, break; case SND_SOC_DAPM_POST_PMU: - ret = snd_soc_dai_digital_mute(sink, 0); + ret = snd_soc_dai_digital_mute(sink, 0, + SNDRV_PCM_STREAM_PLAYBACK); if (ret != 0 && ret != -ENOTSUPP) dev_warn(sink->dev, "ASoC: Failed to unmute: %d\n", ret); ret = 0; break; case SND_SOC_DAPM_PRE_PMD: - ret = snd_soc_dai_digital_mute(sink, 1); + ret = snd_soc_dai_digital_mute(sink, 1, + SNDRV_PCM_STREAM_PLAYBACK); if (ret != 0 && ret != -ENOTSUPP) dev_warn(sink->dev, "ASoC: Failed to mute: %d\n", ret); ret = 0; diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index cf191e6..d675b4a 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -383,8 +383,7 @@ static int soc_pcm_close(struct snd_pcm_substream *substream) /* Muting the DAC suppresses artifacts caused during digital * shutdown, for example from stopping clocks. */ - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - snd_soc_dai_digital_mute(codec_dai, 1); + snd_soc_dai_digital_mute(codec_dai, 1, substream->stream); if (cpu_dai->driver->ops->shutdown) cpu_dai->driver->ops->shutdown(substream, cpu_dai); @@ -488,7 +487,7 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream) snd_soc_dapm_stream_event(rtd, substream->stream, SND_SOC_DAPM_STREAM_START); - snd_soc_dai_digital_mute(codec_dai, 0); + snd_soc_dai_digital_mute(codec_dai, 0, substream->stream); out: mutex_unlock(&rtd->pcm_mutex); @@ -586,7 +585,7 @@ static int soc_pcm_hw_free(struct snd_pcm_substream *substream) /* apply codec digital mute */ if (!codec->active) - snd_soc_dai_digital_mute(codec_dai, 1); + snd_soc_dai_digital_mute(codec_dai, 1, substream->stream); /* free any machine hw params */ if (rtd->dai_link->ops && rtd->dai_link->ops->hw_free) -- cgit v0.10.2 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 diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 3c92514..e80f835 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 0be1826..e8c9442 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 v0.10.2 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 diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 899c4fb..54243c4 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 v0.10.2 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 diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 54243c4..b9af281b 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index b5d5b20..1f8ce21 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 v0.10.2 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 diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index 639a282..710dae8 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 1f8ce21..bb9179e 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 710dae8..fb7a32e 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 v0.10.2 From bdaacea35960ae72e84bd481e69324da9f4d4de5 Mon Sep 17 00:00:00 2001 From: Chris Rattray Date: Fri, 8 Feb 2013 14:32:15 +0000 Subject: ASoC: wm_adsp: round to 4-byte boundary for coeff file blocks Signed-off-by: Chris Rattray Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index be45e2b..f3f7e75 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -711,6 +711,7 @@ static int wm_adsp_load_coeff(struct wm_adsp *dsp) int ret, pos, blocks, type, offset, reg; char *file; struct wm_adsp_buf *buf; + int tmp; file = kzalloc(PAGE_SIZE, GFP_KERNEL); if (file == NULL) @@ -842,7 +843,12 @@ static int wm_adsp_load_coeff(struct wm_adsp *dsp) } } - pos += le32_to_cpu(blk->len) + sizeof(*blk); + tmp = le32_to_cpu(blk->len) % 4; + if (tmp) + pos += le32_to_cpu(blk->len) + (4 - tmp) + sizeof(*blk); + else + pos += le32_to_cpu(blk->len) + sizeof(*blk); + blocks++; } -- cgit v0.10.2 From e9a25e04b845aade311aaa268a696c5c4ff3eece Mon Sep 17 00:00:00 2001 From: Matt Gruskin Date: Sat, 9 Feb 2013 12:56:35 -0500 Subject: ALSA: usb-audio: add support for M-Audio FT C600 Adds quirks and mixer support for the M-Audio Fast Track C600 USB audio interface. This device is very similar to the C400 - the C600 simply has some more inputs and outputs, so the existing C400 support is extended to support this device as well. Signed-off-by: Matt Gruskin Signed-off-by: Takashi Iwai diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index e90daf8..638e7f7 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -807,6 +807,7 @@ static void volume_control_quirks(struct usb_mixer_elem_info *cval, { switch (cval->mixer->chip->usb_id) { case USB_ID(0x0763, 0x2030): /* M-Audio Fast Track C400 */ + case USB_ID(0x0763, 0x2031): /* M-Audio Fast Track C600 */ if (strcmp(kctl->id.name, "Effect Duration") == 0) { cval->min = 0x0000; cval->max = 0xffff; diff --git a/sound/usb/mixer_maps.c b/sound/usb/mixer_maps.c index 0e2ed3d..cc2dd1f 100644 --- a/sound/usb/mixer_maps.c +++ b/sound/usb/mixer_maps.c @@ -380,6 +380,10 @@ static struct usbmix_ctl_map usbmix_ctl_maps[] = { .selector_map = c400_selectors, }, { + .id = USB_ID(0x0763, 0x2031), + .selector_map = c400_selectors, + }, + { .id = USB_ID(0x08bb, 0x2702), .map = linex_map, .ignore_ctl_error = 1, diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index 15520de..497d274 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -637,7 +637,7 @@ static int snd_nativeinstruments_create_mixer(struct usb_mixer_interface *mixer, } /* M-Audio FastTrack Ultra quirks */ -/* FTU Effect switch (also used by C400) */ +/* FTU Effect switch (also used by C400/C600) */ struct snd_ftu_eff_switch_priv_val { struct usb_mixer_interface *mixer; int cached_value; @@ -1029,32 +1029,45 @@ void snd_emuusb_set_samplerate(struct snd_usb_audio *chip, } } -/* M-Audio Fast Track C400 */ -/* C400 volume controls, this control needs a volume quirk, see mixer.c */ +/* M-Audio Fast Track C400/C600 */ +/* C400/C600 volume controls, this control needs a volume quirk, see mixer.c */ static int snd_c400_create_vol_ctls(struct usb_mixer_interface *mixer) { char name[64]; unsigned int cmask, offset; int out, chan, err; + int num_outs = 0; + int num_ins = 0; const unsigned int id = 0x40; const int val_type = USB_MIXER_S16; const int control = 1; - for (chan = 0; chan < 10; chan++) { - for (out = 0; out < 6; out++) { - if (chan < 6) { + switch (mixer->chip->usb_id) { + case USB_ID(0x0763, 0x2030): + num_outs = 6; + num_ins = 4; + break; + case USB_ID(0x0763, 0x2031): + num_outs = 8; + num_ins = 6; + break; + } + + for (chan = 0; chan < num_outs + num_ins; chan++) { + for (out = 0; out < num_outs; out++) { + if (chan < num_outs) { snprintf(name, sizeof(name), "PCM%d-Out%d Playback Volume", chan + 1, out + 1); } else { snprintf(name, sizeof(name), "In%d-Out%d Playback Volume", - chan - 5, out + 1); + chan - num_outs + 1, out + 1); } cmask = (out == 0) ? 0 : 1 << (out - 1); - offset = chan * 6; + offset = chan * num_outs; err = snd_create_std_mono_ctl_offset(mixer, id, control, cmask, val_type, offset, name, &snd_usb_mixer_vol_tlv); @@ -1110,20 +1123,33 @@ static int snd_c400_create_effect_vol_ctls(struct usb_mixer_interface *mixer) char name[64]; unsigned int cmask; int chan, err; + int num_outs = 0; + int num_ins = 0; const unsigned int id = 0x42; const int val_type = USB_MIXER_S16; const int control = 1; - for (chan = 0; chan < 10; chan++) { - if (chan < 6) { + switch (mixer->chip->usb_id) { + case USB_ID(0x0763, 0x2030): + num_outs = 6; + num_ins = 4; + break; + case USB_ID(0x0763, 0x2031): + num_outs = 8; + num_ins = 6; + break; + } + + for (chan = 0; chan < num_outs + num_ins; chan++) { + if (chan < num_outs) { snprintf(name, sizeof(name), "Effect Send DOut%d", chan + 1); } else { snprintf(name, sizeof(name), "Effect Send AIn%d", - chan - 5); + chan - num_outs + 1); } cmask = (chan == 0) ? 0 : 1 << (chan - 1); @@ -1142,20 +1168,33 @@ static int snd_c400_create_effect_ret_vol_ctls(struct usb_mixer_interface *mixer char name[64]; unsigned int cmask; int chan, err; + int num_outs = 0; + int offset = 0; const unsigned int id = 0x40; const int val_type = USB_MIXER_S16; const int control = 1; - const int chan_id[6] = { 0, 7, 2, 9, 4, 0xb }; - const unsigned int offset = 0x3c; - /* { 0x3c, 0x43, 0x3e, 0x45, 0x40, 0x47 } */ - for (chan = 0; chan < 6; chan++) { + switch (mixer->chip->usb_id) { + case USB_ID(0x0763, 0x2030): + num_outs = 6; + offset = 0x3c; + /* { 0x3c, 0x43, 0x3e, 0x45, 0x40, 0x47 } */ + break; + case USB_ID(0x0763, 0x2031): + num_outs = 8; + offset = 0x70; + /* { 0x70, 0x79, 0x72, 0x7b, 0x74, 0x7d, 0x76, 0x7f } */ + break; + } + + for (chan = 0; chan < num_outs; chan++) { snprintf(name, sizeof(name), "Effect Return %d", chan + 1); - cmask = (chan_id[chan] == 0) ? 0 : 1 << (chan_id[chan] - 1); + cmask = (chan == 0) ? 0 : + 1 << (chan + (chan % 2) * num_outs - 1); err = snd_create_std_mono_ctl_offset(mixer, id, control, cmask, val_type, offset, name, &snd_usb_mixer_vol_tlv); @@ -1299,6 +1338,7 @@ int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer) break; case USB_ID(0x0763, 0x2030): /* M-Audio Fast Track C400 */ + case USB_ID(0x0763, 0x2031): /* M-Audio Fast Track C400 */ err = snd_c400_create_mixer(mixer); break; diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index 81f70a7..f94397b 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -367,6 +367,7 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt) switch (subs->stream->chip->usb_id) { case USB_ID(0x0763, 0x2030): /* M-Audio Fast Track C400 */ + case USB_ID(0x0763, 0x2031): /* M-Audio Fast Track C600 */ if (is_playback) { implicit_fb = 1; ep = 0x81; diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h index 820580a..c39f898 100644 --- a/sound/usb/quirks-table.h +++ b/sound/usb/quirks-table.h @@ -2326,6 +2326,77 @@ YAMAHA_DEVICE(0x7010, "UB99"), } }, { + USB_DEVICE_VENDOR_SPEC(0x0763, 0x2031), + .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) { + /* .vendor_name = "M-Audio", */ + /* .product_name = "Fast Track C600", */ + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_COMPOSITE, + .data = &(const struct snd_usb_audio_quirk[]) { + { + .ifnum = 1, + .type = QUIRK_AUDIO_STANDARD_MIXER, + }, + /* Playback */ + { + .ifnum = 2, + .type = QUIRK_AUDIO_FIXED_ENDPOINT, + .data = &(const struct audioformat) { + .formats = SNDRV_PCM_FMTBIT_S24_3LE, + .channels = 8, + .iface = 2, + .altsetting = 1, + .altset_idx = 1, + .attributes = UAC_EP_CS_ATTR_SAMPLE_RATE, + .endpoint = 0x01, + .ep_attr = 0x09, + .rates = SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000, + .rate_min = 44100, + .rate_max = 96000, + .nr_rates = 4, + .rate_table = (unsigned int[]) { + 44100, 48000, 88200, 96000 + }, + .clock = 0x80, + } + }, + /* Capture */ + { + .ifnum = 3, + .type = QUIRK_AUDIO_FIXED_ENDPOINT, + .data = &(const struct audioformat) { + .formats = SNDRV_PCM_FMTBIT_S24_3LE, + .channels = 6, + .iface = 3, + .altsetting = 1, + .altset_idx = 1, + .attributes = UAC_EP_CS_ATTR_SAMPLE_RATE, + .endpoint = 0x81, + .ep_attr = 0x05, + .rates = SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000, + .rate_min = 44100, + .rate_max = 96000, + .nr_rates = 4, + .rate_table = (unsigned int[]) { + 44100, 48000, 88200, 96000 + }, + .clock = 0x80, + } + }, + /* MIDI */ + { + .ifnum = -1 /* Interface = 4 */ + } + } + } +}, +{ USB_DEVICE_VENDOR_SPEC(0x0763, 0x2080), .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { /* .vendor_name = "M-Audio", */ diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index 2c97185..7d7ad0b 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -863,13 +863,14 @@ void snd_usb_endpoint_start_quirk(struct snd_usb_endpoint *ep) ep->skip_packets = 4; /* - * M-Audio Fast Track C400 - when packets are not skipped, real world - * latency varies by approx. +/- 50 frames (at 96KHz) each time the - * stream is (re)started. When skipping packets 16 at endpoint start - * up, the real world latency is stable within +/- 1 frame (also + * M-Audio Fast Track C400/C600 - when packets are not skipped, real + * world latency varies by approx. +/- 50 frames (at 96KHz) each time + * the stream is (re)started. When skipping packets 16 at endpoint + * start up, the real world latency is stable within +/- 1 frame (also * across power cycles). */ - if (ep->chip->usb_id == USB_ID(0x0763, 0x2030) && + if ((ep->chip->usb_id == USB_ID(0x0763, 0x2030) || + ep->chip->usb_id == USB_ID(0x0763, 0x2031)) && ep->type == SND_USB_ENDPOINT_TYPE_DATA) ep->skip_packets = 16; } -- cgit v0.10.2 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 diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index fb7a32e..b1e099a 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 v0.10.2 From 17ac8e5c6d3478dcfeb75ed5716ca7e5cee612f0 Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Mon, 11 Feb 2013 13:44:53 +0000 Subject: ALSA: core: don't return uninitialized snd_compr_tstamp The snd_compr_update_tstamp() can only fill in the snd_compr_tstamp if the codec implements the pointer() function. If that happened the code was previously returning uninitialized garbage in the tstamp because it wasn't initialized anywhere. This change zero-fills the tstamp in the two places it is used before calling snd_compr_update_tstamp(), and also has snd_compr_update_tstamp() return an error indication if it can't provide a tstamp. For the case of snd_compr_calc_avail() it ignores this error because we still need to return info on the available buffer space even if we can't provide tstamp info - when the tstamp is not valid all fields are now guaranteed to be zero. Signed-off-by: Richard Fitzgerald Signed-off-by: Mark Brown Acked-by: Vinod Koul Signed-off-by: Takashi Iwai diff --git a/sound/core/compress_offload.c b/sound/core/compress_offload.c index ad11dc9..2d62068 100644 --- a/sound/core/compress_offload.c +++ b/sound/core/compress_offload.c @@ -144,16 +144,17 @@ static int snd_compr_free(struct inode *inode, struct file *f) return 0; } -static void snd_compr_update_tstamp(struct snd_compr_stream *stream, +static int snd_compr_update_tstamp(struct snd_compr_stream *stream, struct snd_compr_tstamp *tstamp) { if (!stream->ops->pointer) - return; + return -ENOTSUPP; stream->ops->pointer(stream, tstamp); pr_debug("dsp consumed till %d total %d bytes\n", tstamp->byte_offset, tstamp->copied_total); stream->runtime->hw_pointer = tstamp->byte_offset; stream->runtime->total_bytes_transferred = tstamp->copied_total; + return 0; } static size_t snd_compr_calc_avail(struct snd_compr_stream *stream, @@ -161,7 +162,9 @@ static size_t snd_compr_calc_avail(struct snd_compr_stream *stream, { long avail_calc; /*this needs to be signed variable */ + memset(avail, 0, sizeof(*avail)); snd_compr_update_tstamp(stream, &avail->tstamp); + /* Still need to return avail even if tstamp can't be filled in */ /* FIXME: This needs to be different for capture stream, available is # of compressed data, for playback it's @@ -517,11 +520,14 @@ out: static inline int snd_compr_tstamp(struct snd_compr_stream *stream, unsigned long arg) { - struct snd_compr_tstamp tstamp; + struct snd_compr_tstamp tstamp = {0}; + int ret; - snd_compr_update_tstamp(stream, &tstamp); - return copy_to_user((struct snd_compr_tstamp __user *)arg, - &tstamp, sizeof(tstamp)) ? -EFAULT : 0; + ret = snd_compr_update_tstamp(stream, &tstamp); + if (ret == 0) + ret = copy_to_user((struct snd_compr_tstamp __user *)arg, + &tstamp, sizeof(tstamp)) ? -EFAULT : 0; + return ret; } static int snd_compr_pause(struct snd_compr_stream *stream) -- cgit v0.10.2 From f49a59c4471d81a233e09dda45187cc44fda009d Mon Sep 17 00:00:00 2001 From: Denis Efremov Date: Mon, 11 Feb 2013 19:04:06 +0400 Subject: ALSA: rme32.c irq enabling after spin_lock_irq According to the other code in this driver and similar code in rme96 it seems, that spin_lock_irq in snd_rme32_capture_close function should be paired with spin_unlock_irq. Found by Linux Driver Verification project (linuxtesting.org). Signed-off-by: Denis Efremov Cc: Signed-off-by: Takashi Iwai diff --git a/sound/pci/rme32.c b/sound/pci/rme32.c index 2450663..0ecd410 100644 --- a/sound/pci/rme32.c +++ b/sound/pci/rme32.c @@ -1017,7 +1017,7 @@ static int snd_rme32_capture_close(struct snd_pcm_substream *substream) spin_lock_irq(&rme32->lock); rme32->capture_substream = NULL; rme32->capture_periodsize = 0; - spin_unlock(&rme32->lock); + spin_unlock_irq(&rme32->lock); return 0; } -- cgit v0.10.2 From dacae5a19b4cbe1b5e3a86de23ea74cbe9ec9652 Mon Sep 17 00:00:00 2001 From: Denis Efremov Date: Mon, 11 Feb 2013 19:49:48 +0400 Subject: ALSA: ali5451: remove irq enabling in pointer callback snd_ali_pointer function is called with local interrupts disabled. However it seems very strange to reenable them in such way. Found by Linux Driver Verification project (linuxtesting.org). Signed-off-by: Denis Efremov Cc: Signed-off-by: Takashi Iwai diff --git a/sound/pci/ali5451/ali5451.c b/sound/pci/ali5451/ali5451.c index 136a393..e760af9 100644 --- a/sound/pci/ali5451/ali5451.c +++ b/sound/pci/ali5451/ali5451.c @@ -1435,7 +1435,7 @@ static snd_pcm_uframes_t snd_ali_pointer(struct snd_pcm_substream *substream) spin_lock(&codec->reg_lock); if (!pvoice->running) { - spin_unlock_irq(&codec->reg_lock); + spin_unlock(&codec->reg_lock); return 0; } outb(pvoice->number, ALI_REG(codec, ALI_GC_CIR)); -- cgit v0.10.2 From a10807aef5cab5568e70a2d1597305999d93f85d Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Mon, 11 Feb 2013 13:39:08 -0200 Subject: ASoC: fsl: imx-audmux: Fix sparse warning Fix the following sparse warning: sound/soc/fsl/imx-audmux.c:182:3: warning: symbol 'audmux_type' was not declared. Should it be static? Signed-off-by: Fabio Estevam Signed-off-by: Mark Brown diff --git a/sound/soc/fsl/imx-audmux.c b/sound/soc/fsl/imx-audmux.c index 251f4d9..fab912e 100644 --- a/sound/soc/fsl/imx-audmux.c +++ b/sound/soc/fsl/imx-audmux.c @@ -176,7 +176,7 @@ static inline void audmux_debugfs_remove(void) } #endif -enum imx_audmux_type { +static enum imx_audmux_type { IMX21_AUDMUX, IMX31_AUDMUX, } audmux_type; -- cgit v0.10.2 From 41c4d554e75ccbdb043878049cf2671dd49598a5 Mon Sep 17 00:00:00 2001 From: Sebastien Guiriec Date: Mon, 11 Feb 2013 14:02:42 +0100 Subject: ASoC: omap-mcpdm: Remove useless ressource get. Remove unused memory ressource get from McPDM driver. Signed-off-by: Sebastien Guiriec Acked-by: Peter Ujfalusi Signed-off-by: Mark Brown diff --git a/sound/soc/omap/omap-mcpdm.c b/sound/soc/omap/omap-mcpdm.c index 2fe8be2..5ca11bd 100644 --- a/sound/soc/omap/omap-mcpdm.c +++ b/sound/soc/omap/omap-mcpdm.c @@ -449,10 +449,6 @@ static int asoc_mcpdm_probe(struct platform_device *pdev) omap_mcpdm_dai_dma_params[0].port_addr = res->start + MCPDM_REG_DN_DATA; omap_mcpdm_dai_dma_params[1].port_addr = res->start + MCPDM_REG_UP_DATA; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (res == NULL) - return -ENOMEM; - res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "dn_link"); if (!res) return -ENODEV; -- cgit v0.10.2 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 diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index de512c5..9eaa8b1 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 v0.10.2 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 diff --git a/Documentation/sound/alsa/HD-Audio-Models.txt b/Documentation/sound/alsa/HD-Audio-Models.txt index 16dfe57..bb8b0dc 100644 --- a/Documentation/sound/alsa/HD-Audio-Models.txt +++ b/Documentation/sound/alsa/HD-Audio-Models.txt @@ -53,7 +53,7 @@ ALC882/883/885/888/889 acer-aspire-8930g Acer Aspire 8330G/6935G acer-aspire Acer Aspire others inv-dmic Inverted internal mic workaround - no-primary-hp VAIO Z workaround (for fixed speaker DAC) + no-primary-hp VAIO Z/VGC-LN51JGB workaround (for fixed speaker DAC) ALC861/660 ========== diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 9eaa8b1..48c9d10 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 v0.10.2 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 diff --git a/sound/pci/hda/ca0132_regs.h b/sound/pci/hda/ca0132_regs.h index 831ca9c..07e7609 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 b1e099a..fe07664 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 v0.10.2 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 diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index fe07664..db02c1e 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index e80f835..04b5738 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 e8c9442..23ca172 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 v0.10.2 From 43cd8bf1c8d8f6e897ed0f2c4bd50a4266b5c36e Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 6 Feb 2013 16:57:29 +0000 Subject: ASoC: arizona: Automatically manage input mutes For optimal performance the inputs should be kept muted until after power up. Since there are few use cases for muting inputs during capture move the mutes to automatic control. Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c index d824c98..ac948a6 100644 --- a/sound/soc/codecs/arizona.c +++ b/sound/soc/codecs/arizona.c @@ -335,6 +335,23 @@ EXPORT_SYMBOL_GPL(arizona_ng_hold); int arizona_in_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { + unsigned int reg; + + if (w->shift % 2) + reg = ARIZONA_ADC_DIGITAL_VOLUME_1L + ((w->shift / 2) * 8); + else + reg = ARIZONA_ADC_DIGITAL_VOLUME_1R + ((w->shift / 2) * 8); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + snd_soc_update_bits(w->codec, reg, ARIZONA_IN1L_MUTE, 0); + break; + case SND_SOC_DAPM_PRE_PMD: + snd_soc_update_bits(w->codec, reg, ARIZONA_IN1L_MUTE, + ARIZONA_IN1L_MUTE); + break; + } + return 0; } EXPORT_SYMBOL_GPL(arizona_in_ev); diff --git a/sound/soc/codecs/wm5102.c b/sound/soc/codecs/wm5102.c index 5e85b64..ab69c83 100644 --- a/sound/soc/codecs/wm5102.c +++ b/sound/soc/codecs/wm5102.c @@ -636,19 +636,6 @@ SOC_SINGLE_RANGE_TLV("IN3L Volume", ARIZONA_IN3L_CONTROL, SOC_SINGLE_RANGE_TLV("IN3R Volume", ARIZONA_IN3R_CONTROL, ARIZONA_IN3R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), -SOC_SINGLE("IN1L Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_1L, - ARIZONA_IN1L_MUTE_SHIFT, 1, 1), -SOC_SINGLE("IN1R Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_1R, - ARIZONA_IN1R_MUTE_SHIFT, 1, 1), -SOC_SINGLE("IN2L Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_2L, - ARIZONA_IN2L_MUTE_SHIFT, 1, 1), -SOC_SINGLE("IN2R Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_2R, - ARIZONA_IN2R_MUTE_SHIFT, 1, 1), -SOC_SINGLE("IN3L Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_3L, - ARIZONA_IN3L_MUTE_SHIFT, 1, 1), -SOC_SINGLE("IN3R Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_3R, - ARIZONA_IN3R_MUTE_SHIFT, 1, 1), - SOC_SINGLE_TLV("IN1L Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_1L, ARIZONA_IN1L_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv), SOC_SINGLE_TLV("IN1R Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_1R, diff --git a/sound/soc/codecs/wm5110.c b/sound/soc/codecs/wm5110.c index 2319937..a163132 100644 --- a/sound/soc/codecs/wm5110.c +++ b/sound/soc/codecs/wm5110.c @@ -80,23 +80,6 @@ SOC_SINGLE_RANGE_TLV("IN3L Volume", ARIZONA_IN3L_CONTROL, SOC_SINGLE_RANGE_TLV("IN3R Volume", ARIZONA_IN3R_CONTROL, ARIZONA_IN3R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), -SOC_SINGLE("IN1L Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_1L, - ARIZONA_IN1L_MUTE_SHIFT, 1, 1), -SOC_SINGLE("IN1R Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_1R, - ARIZONA_IN1R_MUTE_SHIFT, 1, 1), -SOC_SINGLE("IN2L Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_2L, - ARIZONA_IN2L_MUTE_SHIFT, 1, 1), -SOC_SINGLE("IN2R Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_2R, - ARIZONA_IN2R_MUTE_SHIFT, 1, 1), -SOC_SINGLE("IN3L Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_3L, - ARIZONA_IN3L_MUTE_SHIFT, 1, 1), -SOC_SINGLE("IN3R Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_3R, - ARIZONA_IN3R_MUTE_SHIFT, 1, 1), -SOC_SINGLE("IN4L Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_4L, - ARIZONA_IN4L_MUTE_SHIFT, 1, 1), -SOC_SINGLE("IN4R Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_4R, - ARIZONA_IN4R_MUTE_SHIFT, 1, 1), - SOC_SINGLE_TLV("IN1L Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_1L, ARIZONA_IN1L_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv), SOC_SINGLE_TLV("IN1R Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_1R, -- cgit v0.10.2 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 diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 6af5ade..78897d0 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index bb9179e..4cea6bb6 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 v0.10.2 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 diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index 7d941ef..941bf6c 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 v0.10.2 From 9727b490e543de956b8ba356e2d5499097d0b7a2 Mon Sep 17 00:00:00 2001 From: Jeeja KP Date: Thu, 14 Feb 2013 16:52:51 +0530 Subject: ALSA: compress: add support for gapless playback this add new API for sound compress to support gapless playback. As noted in Documentation change, we add API to send metadata of encoder and padding delay to DSP. Also add API for indicating EOF and switching to subsequent track Also bump the compress API version Signed-off-by: Jeeja KP Signed-off-by: Vinod Koul Signed-off-by: Takashi Iwai diff --git a/Documentation/sound/alsa/compress_offload.txt b/Documentation/sound/alsa/compress_offload.txt index 90e9b3a..0bcc551 100644 --- a/Documentation/sound/alsa/compress_offload.txt +++ b/Documentation/sound/alsa/compress_offload.txt @@ -145,6 +145,52 @@ Modifications include: - Addition of encoding options when required (derived from OpenMAX IL) - Addition of rateControlSupported (missing in OpenMAX AL) +Gapless Playback +================ +When playing thru an album, the decoders have the ability to skip the encoder +delay and padding and directly move from one track content to another. The end +user can perceive this as gapless playback as we dont have silence while +switching from one track to another + +Also, there might be low-intensity noises due to encoding. Perfect gapless is +difficult to reach with all types of compressed data, but works fine with most +music content. The decoder needs to know the encoder delay and encoder padding. +So we need to pass this to DSP. This metadata is extracted from ID3/MP4 headers +and are not present by default in the bitstream, hence the need for a new +interface to pass this information to the DSP. Also DSP and userspace needs to +switch from one track to another and start using data for second track. + +The main additions are: + +- set_metadata +This routine sets the encoder delay and encoder padding. This can be used by +decoder to strip the silence. This needs to be set before the data in the track +is written. + +- set_next_track +This routine tells DSP that metadata and write operation sent after this would +correspond to subsequent track + +- partial drain +This is called when end of file is reached. The userspace can inform DSP that +EOF is reached and now DSP can start skipping padding delay. Also next write +data would belong to next track + +Sequence flow for gapless would be: +- Open +- Get caps / codec caps +- Set params +- Set metadata of the first track +- Fill data of the first track +- Trigger start +- User-space finished sending all, +- Indicaite next track data by sending set_next_track +- Set metadata of the next track +- then call partial_drain to flush most of buffer in DSP +- Fill data of the next track +- DSP switches to second track +(note: order for partial_drain and write for next track can be reversed as well) + Not supported: - Support for VoIP/circuit-switched calls is not the target of this diff --git a/include/sound/compress_driver.h b/include/sound/compress_driver.h index f2912ab..ff6c741 100644 --- a/include/sound/compress_driver.h +++ b/include/sound/compress_driver.h @@ -71,6 +71,8 @@ struct snd_compr_runtime { * @runtime: pointer to runtime structure * @device: device pointer * @direction: stream direction, playback/recording + * @metadata_set: metadata set flag, true when set + * @next_track: has userspace signall next track transistion, true when set * @private_data: pointer to DSP private data */ struct snd_compr_stream { @@ -79,6 +81,8 @@ struct snd_compr_stream { struct snd_compr_runtime *runtime; struct snd_compr *device; enum snd_compr_direction direction; + bool metadata_set; + bool next_track; void *private_data; }; @@ -110,6 +114,10 @@ struct snd_compr_ops { struct snd_compr_params *params); int (*get_params)(struct snd_compr_stream *stream, struct snd_codec *params); + int (*set_metadata)(struct snd_compr_stream *stream, + struct snd_compr_metadata *metadata); + int (*get_metadata)(struct snd_compr_stream *stream, + struct snd_compr_metadata *metadata); int (*trigger)(struct snd_compr_stream *stream, int cmd); int (*pointer)(struct snd_compr_stream *stream, struct snd_compr_tstamp *tstamp); diff --git a/include/uapi/sound/compress_offload.h b/include/uapi/sound/compress_offload.h index 05341a4..d630163 100644 --- a/include/uapi/sound/compress_offload.h +++ b/include/uapi/sound/compress_offload.h @@ -30,7 +30,7 @@ #include -#define SNDRV_COMPRESS_VERSION SNDRV_PROTOCOL_VERSION(0, 1, 0) +#define SNDRV_COMPRESS_VERSION SNDRV_PROTOCOL_VERSION(0, 1, 1) /** * struct snd_compressed_buffer: compressed buffer * @fragment_size: size of buffer fragment in bytes @@ -122,6 +122,27 @@ struct snd_compr_codec_caps { }; /** + * @SNDRV_COMPRESS_ENCODER_PADDING: no of samples appended by the encoder at the + * end of the track + * @SNDRV_COMPRESS_ENCODER_DELAY: no of samples inserted by the encoder at the + * beginning of the track + */ +enum { + SNDRV_COMPRESS_ENCODER_PADDING = 1, + SNDRV_COMPRESS_ENCODER_DELAY = 2, +}; + +/** + * struct snd_compr_metadata: compressed stream metadata + * @key: key id + * @value: key value + */ +struct snd_compr_metadata { + __u32 key; + __u32 value[8]; +}; + +/** * compress path ioctl definitions * SNDRV_COMPRESS_GET_CAPS: Query capability of DSP * SNDRV_COMPRESS_GET_CODEC_CAPS: Query capability of a codec @@ -145,6 +166,10 @@ struct snd_compr_codec_caps { struct snd_compr_codec_caps) #define SNDRV_COMPRESS_SET_PARAMS _IOW('C', 0x12, struct snd_compr_params) #define SNDRV_COMPRESS_GET_PARAMS _IOR('C', 0x13, struct snd_codec) +#define SNDRV_COMPRESS_SET_METADATA _IOW('C', 0x14,\ + struct snd_compr_metadata) +#define SNDRV_COMPRESS_GET_METADATA _IOWR('C', 0x15,\ + struct snd_compr_metadata) #define SNDRV_COMPRESS_TSTAMP _IOR('C', 0x20, struct snd_compr_tstamp) #define SNDRV_COMPRESS_AVAIL _IOR('C', 0x21, struct snd_compr_avail) #define SNDRV_COMPRESS_PAUSE _IO('C', 0x30) @@ -152,10 +177,14 @@ struct snd_compr_codec_caps { #define SNDRV_COMPRESS_START _IO('C', 0x32) #define SNDRV_COMPRESS_STOP _IO('C', 0x33) #define SNDRV_COMPRESS_DRAIN _IO('C', 0x34) +#define SNDRV_COMPRESS_NEXT_TRACK _IO('C', 0x35) +#define SNDRV_COMPRESS_PARTIAL_DRAIN _IO('C', 0x36) /* * TODO * 1. add mmap support * */ #define SND_COMPR_TRIGGER_DRAIN 7 /*FIXME move this to pcm.h */ +#define SND_COMPR_TRIGGER_NEXT_TRACK 8 +#define SND_COMPR_TRIGGER_PARTIAL_DRAIN 9 #endif diff --git a/sound/core/compress_offload.c b/sound/core/compress_offload.c index 2d62068..c84abc8 100644 --- a/sound/core/compress_offload.c +++ b/sound/core/compress_offload.c @@ -486,6 +486,8 @@ snd_compr_set_params(struct snd_compr_stream *stream, unsigned long arg) if (retval) goto out; stream->runtime->state = SNDRV_PCM_STATE_SETUP; + stream->metadata_set = false; + stream->next_track = false; } else { return -EPERM; } @@ -517,6 +519,49 @@ out: return retval; } +static int +snd_compr_get_metadata(struct snd_compr_stream *stream, unsigned long arg) +{ + struct snd_compr_metadata metadata; + int retval; + + if (!stream->ops->get_metadata) + return -ENXIO; + + if (copy_from_user(&metadata, (void __user *)arg, sizeof(metadata))) + return -EFAULT; + + retval = stream->ops->get_metadata(stream, &metadata); + if (retval != 0) + return retval; + + if (copy_to_user((void __user *)arg, &metadata, sizeof(metadata))) + return -EFAULT; + + return 0; +} + +static int +snd_compr_set_metadata(struct snd_compr_stream *stream, unsigned long arg) +{ + struct snd_compr_metadata metadata; + int retval; + + if (!stream->ops->set_metadata) + return -ENXIO; + /* + * we should allow parameter change only when stream has been + * opened not in other cases + */ + if (copy_from_user(&metadata, (void __user *)arg, sizeof(metadata))) + return -EFAULT; + + retval = stream->ops->set_metadata(stream, &metadata); + stream->metadata_set = true; + + return retval; +} + static inline int snd_compr_tstamp(struct snd_compr_stream *stream, unsigned long arg) { @@ -600,6 +645,44 @@ static int snd_compr_drain(struct snd_compr_stream *stream) return retval; } +static int snd_compr_next_track(struct snd_compr_stream *stream) +{ + int retval; + + /* only a running stream can transition to next track */ + if (stream->runtime->state != SNDRV_PCM_STATE_RUNNING) + return -EPERM; + + /* you can signal next track isf this is intended to be a gapless stream + * and current track metadata is set + */ + if (stream->metadata_set == false) + return -EPERM; + + retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_NEXT_TRACK); + if (retval != 0) + return retval; + stream->metadata_set = false; + stream->next_track = true; + return 0; +} + +static int snd_compr_partial_drain(struct snd_compr_stream *stream) +{ + int retval; + if (stream->runtime->state == SNDRV_PCM_STATE_PREPARED || + stream->runtime->state == SNDRV_PCM_STATE_SETUP) + return -EPERM; + /* stream can be drained only when next track has been signalled */ + if (stream->next_track == false) + return -EPERM; + + retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_PARTIAL_DRAIN); + + stream->next_track = false; + return retval; +} + static long snd_compr_ioctl(struct file *f, unsigned int cmd, unsigned long arg) { struct snd_compr_file *data = f->private_data; @@ -629,6 +712,12 @@ static long snd_compr_ioctl(struct file *f, unsigned int cmd, unsigned long arg) case _IOC_NR(SNDRV_COMPRESS_GET_PARAMS): retval = snd_compr_get_params(stream, arg); break; + case _IOC_NR(SNDRV_COMPRESS_SET_METADATA): + retval = snd_compr_set_metadata(stream, arg); + break; + case _IOC_NR(SNDRV_COMPRESS_GET_METADATA): + retval = snd_compr_get_metadata(stream, arg); + break; case _IOC_NR(SNDRV_COMPRESS_TSTAMP): retval = snd_compr_tstamp(stream, arg); break; @@ -650,6 +739,13 @@ static long snd_compr_ioctl(struct file *f, unsigned int cmd, unsigned long arg) case _IOC_NR(SNDRV_COMPRESS_DRAIN): retval = snd_compr_drain(stream); break; + case _IOC_NR(SNDRV_COMPRESS_PARTIAL_DRAIN): + retval = snd_compr_partial_drain(stream); + break; + case _IOC_NR(SNDRV_COMPRESS_NEXT_TRACK): + retval = snd_compr_next_track(stream); + break; + } mutex_unlock(&stream->device->lock); return retval; -- cgit v0.10.2 From f3c90242a3b9e32f510229c4c1313df6ca7f1667 Mon Sep 17 00:00:00 2001 From: Raymond Yau Date: Thu, 14 Feb 2013 20:37:22 +0800 Subject: ALSA: au88x0 - Define channel map for au88x0 Define channel map for playback, capture devices of au88x0 Signed-off-by: Raymond Yau Signed-off-by: Takashi Iwai diff --git a/sound/pci/au88x0/au88x0_pcm.c b/sound/pci/au88x0/au88x0_pcm.c index a4184bb..b46dc9b 100644 --- a/sound/pci/au88x0/au88x0_pcm.c +++ b/sound/pci/au88x0/au88x0_pcm.c @@ -650,6 +650,29 @@ static int snd_vortex_new_pcm(vortex_t *chip, int idx, int nr) snd_dma_pci_data(chip->pci_dev), 0x10000, 0x10000); + switch (VORTEX_PCM_TYPE(pcm)) { + case VORTEX_PCM_ADB: + err = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK, + snd_pcm_std_chmaps, + VORTEX_IS_QUAD(chip) ? 4 : 2, + 0, NULL); + if (err < 0) + return err; + err = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_CAPTURE, + snd_pcm_std_chmaps, 2, 0, NULL); + if (err < 0) + return err; + break; +#ifdef CHIP_AU8830 + case VORTEX_PCM_A3D: + err = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK, + snd_pcm_std_chmaps, 1, 0, NULL); + if (err < 0) + return err; + break; +#endif + }; + if (VORTEX_PCM_TYPE(pcm) == VORTEX_PCM_SPDIF) { for (i = 0; i < ARRAY_SIZE(snd_vortex_mixer_spdif); i++) { kctl = snd_ctl_new1(&snd_vortex_mixer_spdif[i], chip); -- cgit v0.10.2 From ef5c2eba2412596f1a022c11caf74428bffd9abe Mon Sep 17 00:00:00 2001 From: Adam Thomson Date: Thu, 14 Feb 2013 12:02:51 +0000 Subject: ASoC: codecs: Add da7213 codec This patch adds support for the Dialog DA7213 audio codec. Signed-off-by: Adam Thomson Signed-off-by: Mark Brown diff --git a/include/sound/da7213.h b/include/sound/da7213.h new file mode 100644 index 0000000..673f5c3 --- /dev/null +++ b/include/sound/da7213.h @@ -0,0 +1,52 @@ +/* + * da7213.h - DA7213 ASoC Codec Driver Platform Data + * + * Copyright (c) 2013 Dialog Semiconductor + * + * Author: Adam Thomson + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _DA7213_PDATA_H +#define _DA7213_PDATA_H + +enum da7213_micbias_voltage { + DA7213_MICBIAS_1_6V = 0, + DA7213_MICBIAS_2_2V = 1, + DA7213_MICBIAS_2_5V = 2, + DA7213_MICBIAS_3_0V = 3, +}; + +enum da7213_dmic_data_sel { + DA7213_DMIC_DATA_LRISE_RFALL = 0, + DA7213_DMIC_DATA_LFALL_RRISE = 1, +}; + +enum da7213_dmic_samplephase { + DA7213_DMIC_SAMPLE_ON_CLKEDGE = 0, + DA7213_DMIC_SAMPLE_BETWEEN_CLKEDGE = 1, +}; + +enum da7213_dmic_clk_rate { + DA7213_DMIC_CLK_3_0MHZ = 0, + DA7213_DMIC_CLK_1_5MHZ = 1, +}; + +struct da7213_platform_data { + /* Mic Bias voltage */ + enum da7213_micbias_voltage micbias1_lvl; + enum da7213_micbias_voltage micbias2_lvl; + + /* DMIC config */ + enum da7213_dmic_data_sel dmic_data_sel; + enum da7213_dmic_samplephase dmic_samplephase; + enum da7213_dmic_clk_rate dmic_clk_rate; + + /* MCLK squaring config */ + bool mclk_squaring; +}; + +#endif /* _DA7213_PDATA_H */ diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 3a84782..751476a 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -36,6 +36,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_CS4271 if SND_SOC_I2C_AND_SPI select SND_SOC_CX20442 select SND_SOC_DA7210 if I2C + select SND_SOC_DA7213 if I2C select SND_SOC_DA732X if I2C select SND_SOC_DA9055 if I2C select SND_SOC_DFBMCS320 @@ -247,6 +248,9 @@ config SND_SOC_L3 config SND_SOC_DA7210 tristate +config SND_SOC_DA7213 + tristate + config SND_SOC_DA732X tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index f6e8e36..6a3b3c3 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -23,6 +23,7 @@ snd-soc-cs4270-objs := cs4270.o snd-soc-cs4271-objs := cs4271.o snd-soc-cx20442-objs := cx20442.o snd-soc-da7210-objs := da7210.o +snd-soc-da7213-objs := da7213.o snd-soc-da732x-objs := da732x.o snd-soc-da9055-objs := da9055.o snd-soc-dfbmcs320-objs := dfbmcs320.o @@ -147,6 +148,7 @@ obj-$(CONFIG_SND_SOC_CS4270) += snd-soc-cs4270.o obj-$(CONFIG_SND_SOC_CS4271) += snd-soc-cs4271.o obj-$(CONFIG_SND_SOC_CX20442) += snd-soc-cx20442.o obj-$(CONFIG_SND_SOC_DA7210) += snd-soc-da7210.o +obj-$(CONFIG_SND_SOC_DA7213) += snd-soc-da7213.o obj-$(CONFIG_SND_SOC_DA732X) += snd-soc-da732x.o obj-$(CONFIG_SND_SOC_DA9055) += snd-soc-da9055.o obj-$(CONFIG_SND_SOC_DFBMCS320) += snd-soc-dfbmcs320.o diff --git a/sound/soc/codecs/da7213.c b/sound/soc/codecs/da7213.c new file mode 100644 index 0000000..41230ad --- /dev/null +++ b/sound/soc/codecs/da7213.c @@ -0,0 +1,1599 @@ +/* + * DA7213 ALSA SoC Codec Driver + * + * Copyright (c) 2013 Dialog Semiconductor + * + * Author: Adam Thomson + * Based on DA9055 ALSA SoC codec driver. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "da7213.h" + + +/* Gain and Volume */ +static const unsigned int aux_vol_tlv[] = { + TLV_DB_RANGE_HEAD(2), + /* -54dB */ + 0x0, 0x11, TLV_DB_SCALE_ITEM(-5400, 0, 0), + /* -52.5dB to 15dB */ + 0x12, 0x3f, TLV_DB_SCALE_ITEM(-5250, 150, 0) +}; + +static const unsigned int digital_gain_tlv[] = { + TLV_DB_RANGE_HEAD(2), + 0x0, 0x07, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 1), + /* -78dB to 12dB */ + 0x08, 0x7f, TLV_DB_SCALE_ITEM(-7800, 75, 0) +}; + +static const unsigned int alc_analog_gain_tlv[] = { + TLV_DB_RANGE_HEAD(2), + 0x0, 0x0, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 1), + /* 0dB to 36dB */ + 0x01, 0x07, TLV_DB_SCALE_ITEM(0, 600, 0) +}; + +static const DECLARE_TLV_DB_SCALE(mic_vol_tlv, -600, 600, 0); +static const DECLARE_TLV_DB_SCALE(mixin_gain_tlv, -450, 150, 0); +static const DECLARE_TLV_DB_SCALE(eq_gain_tlv, -1050, 150, 0); +static const DECLARE_TLV_DB_SCALE(hp_vol_tlv, -5700, 100, 0); +static const DECLARE_TLV_DB_SCALE(lineout_vol_tlv, -4800, 100, 0); +static const DECLARE_TLV_DB_SCALE(alc_threshold_tlv, -9450, 150, 0); +static const DECLARE_TLV_DB_SCALE(alc_gain_tlv, 0, 600, 0); + +/* ADC and DAC voice mode (8kHz) high pass cutoff value */ +static const char * const da7213_voice_hpf_corner_txt[] = { + "2.5Hz", "25Hz", "50Hz", "100Hz", "150Hz", "200Hz", "300Hz", "400Hz" +}; + +static const struct soc_enum da7213_dac_voice_hpf_corner = + SOC_ENUM_SINGLE(DA7213_DAC_FILTERS1, DA7213_VOICE_HPF_CORNER_SHIFT, + DA7213_VOICE_HPF_CORNER_MAX, + da7213_voice_hpf_corner_txt); + +static const struct soc_enum da7213_adc_voice_hpf_corner = + SOC_ENUM_SINGLE(DA7213_ADC_FILTERS1, DA7213_VOICE_HPF_CORNER_SHIFT, + DA7213_VOICE_HPF_CORNER_MAX, + da7213_voice_hpf_corner_txt); + +/* ADC and DAC high pass filter cutoff value */ +static const char * const da7213_audio_hpf_corner_txt[] = { + "Fs/24000", "Fs/12000", "Fs/6000", "Fs/3000" +}; + +static const struct soc_enum da7213_dac_audio_hpf_corner = + SOC_ENUM_SINGLE(DA7213_DAC_FILTERS1, DA7213_AUDIO_HPF_CORNER_SHIFT, + DA7213_AUDIO_HPF_CORNER_MAX, + da7213_audio_hpf_corner_txt); + +static const struct soc_enum da7213_adc_audio_hpf_corner = + SOC_ENUM_SINGLE(DA7213_ADC_FILTERS1, DA7213_AUDIO_HPF_CORNER_SHIFT, + DA7213_AUDIO_HPF_CORNER_MAX, + da7213_audio_hpf_corner_txt); + +/* Gain ramping rate value */ +static const char * const da7213_gain_ramp_rate_txt[] = { + "nominal rate * 8", "nominal rate * 16", "nominal rate / 16", + "nominal rate / 32" +}; + +static const struct soc_enum da7213_gain_ramp_rate = + SOC_ENUM_SINGLE(DA7213_GAIN_RAMP_CTRL, DA7213_GAIN_RAMP_RATE_SHIFT, + DA7213_GAIN_RAMP_RATE_MAX, da7213_gain_ramp_rate_txt); + +/* DAC noise gate setup time value */ +static const char * const da7213_dac_ng_setup_time_txt[] = { + "256 samples", "512 samples", "1024 samples", "2048 samples" +}; + +static const struct soc_enum da7213_dac_ng_setup_time = + SOC_ENUM_SINGLE(DA7213_DAC_NG_SETUP_TIME, + DA7213_DAC_NG_SETUP_TIME_SHIFT, + DA7213_DAC_NG_SETUP_TIME_MAX, + da7213_dac_ng_setup_time_txt); + +/* DAC noise gate rampup rate value */ +static const char * const da7213_dac_ng_rampup_txt[] = { + "0.02 ms/dB", "0.16 ms/dB" +}; + +static const struct soc_enum da7213_dac_ng_rampup_rate = + SOC_ENUM_SINGLE(DA7213_DAC_NG_SETUP_TIME, + DA7213_DAC_NG_RAMPUP_RATE_SHIFT, + DA7213_DAC_NG_RAMP_RATE_MAX, + da7213_dac_ng_rampup_txt); + +/* DAC noise gate rampdown rate value */ +static const char * const da7213_dac_ng_rampdown_txt[] = { + "0.64 ms/dB", "20.48 ms/dB" +}; + +static const struct soc_enum da7213_dac_ng_rampdown_rate = + SOC_ENUM_SINGLE(DA7213_DAC_NG_SETUP_TIME, + DA7213_DAC_NG_RAMPDN_RATE_SHIFT, + DA7213_DAC_NG_RAMP_RATE_MAX, + da7213_dac_ng_rampdown_txt); + +/* DAC soft mute rate value */ +static const char * const da7213_dac_soft_mute_rate_txt[] = { + "1", "2", "4", "8", "16", "32", "64" +}; + +static const struct soc_enum da7213_dac_soft_mute_rate = + SOC_ENUM_SINGLE(DA7213_DAC_FILTERS5, DA7213_DAC_SOFTMUTE_RATE_SHIFT, + DA7213_DAC_SOFTMUTE_RATE_MAX, + da7213_dac_soft_mute_rate_txt); + +/* ALC Attack Rate select */ +static const char * const da7213_alc_attack_rate_txt[] = { + "44/fs", "88/fs", "176/fs", "352/fs", "704/fs", "1408/fs", "2816/fs", + "5632/fs", "11264/fs", "22528/fs", "45056/fs", "90112/fs", "180224/fs" +}; + +static const struct soc_enum da7213_alc_attack_rate = + SOC_ENUM_SINGLE(DA7213_ALC_CTRL2, DA7213_ALC_ATTACK_SHIFT, + DA7213_ALC_ATTACK_MAX, da7213_alc_attack_rate_txt); + +/* ALC Release Rate select */ +static const char * const da7213_alc_release_rate_txt[] = { + "176/fs", "352/fs", "704/fs", "1408/fs", "2816/fs", "5632/fs", + "11264/fs", "22528/fs", "45056/fs", "90112/fs", "180224/fs" +}; + +static const struct soc_enum da7213_alc_release_rate = + SOC_ENUM_SINGLE(DA7213_ALC_CTRL2, DA7213_ALC_RELEASE_SHIFT, + DA7213_ALC_RELEASE_MAX, da7213_alc_release_rate_txt); + +/* ALC Hold Time select */ +static const char * const da7213_alc_hold_time_txt[] = { + "62/fs", "124/fs", "248/fs", "496/fs", "992/fs", "1984/fs", "3968/fs", + "7936/fs", "15872/fs", "31744/fs", "63488/fs", "126976/fs", + "253952/fs", "507904/fs", "1015808/fs", "2031616/fs" +}; + +static const struct soc_enum da7213_alc_hold_time = + SOC_ENUM_SINGLE(DA7213_ALC_CTRL3, DA7213_ALC_HOLD_SHIFT, + DA7213_ALC_HOLD_MAX, da7213_alc_hold_time_txt); + +/* ALC Input Signal Tracking rate select */ +static const char * const da7213_alc_integ_rate_txt[] = { + "1/4", "1/16", "1/256", "1/65536" +}; + +static const struct soc_enum da7213_alc_integ_attack_rate = + SOC_ENUM_SINGLE(DA7213_ALC_CTRL3, DA7213_ALC_INTEG_ATTACK_SHIFT, + DA7213_ALC_INTEG_MAX, da7213_alc_integ_rate_txt); + +static const struct soc_enum da7213_alc_integ_release_rate = + SOC_ENUM_SINGLE(DA7213_ALC_CTRL3, DA7213_ALC_INTEG_RELEASE_SHIFT, + DA7213_ALC_INTEG_MAX, da7213_alc_integ_rate_txt); + + +/* + * Control Functions + */ + +static int da7213_get_alc_data(struct snd_soc_codec *codec, u8 reg_val) +{ + int mid_data, top_data; + int sum = 0; + u8 iteration; + + for (iteration = 0; iteration < DA7213_ALC_AVG_ITERATIONS; + iteration++) { + /* Select the left or right channel and capture data */ + snd_soc_write(codec, DA7213_ALC_CIC_OP_LVL_CTRL, reg_val); + + /* Select middle 8 bits for read back from data register */ + snd_soc_write(codec, DA7213_ALC_CIC_OP_LVL_CTRL, + reg_val | DA7213_ALC_DATA_MIDDLE); + mid_data = snd_soc_read(codec, DA7213_ALC_CIC_OP_LVL_DATA); + + /* Select top 8 bits for read back from data register */ + snd_soc_write(codec, DA7213_ALC_CIC_OP_LVL_CTRL, + reg_val | DA7213_ALC_DATA_TOP); + top_data = snd_soc_read(codec, DA7213_ALC_CIC_OP_LVL_DATA); + + sum += ((mid_data << 8) | (top_data << 16)); + } + + return sum / DA7213_ALC_AVG_ITERATIONS; +} + +static void da7213_alc_calib_man(struct snd_soc_codec *codec) +{ + u8 reg_val; + int avg_left_data, avg_right_data, offset_l, offset_r; + + /* Calculate average for Left and Right data */ + /* Left Data */ + avg_left_data = da7213_get_alc_data(codec, + DA7213_ALC_CIC_OP_CHANNEL_LEFT); + /* Right Data */ + avg_right_data = da7213_get_alc_data(codec, + DA7213_ALC_CIC_OP_CHANNEL_RIGHT); + + /* Calculate DC offset */ + offset_l = -avg_left_data; + offset_r = -avg_right_data; + + reg_val = (offset_l & DA7213_ALC_OFFSET_15_8) >> 8; + snd_soc_write(codec, DA7213_ALC_OFFSET_MAN_M_L, reg_val); + reg_val = (offset_l & DA7213_ALC_OFFSET_19_16) >> 16; + snd_soc_write(codec, DA7213_ALC_OFFSET_MAN_U_L, reg_val); + + reg_val = (offset_r & DA7213_ALC_OFFSET_15_8) >> 8; + snd_soc_write(codec, DA7213_ALC_OFFSET_MAN_M_R, reg_val); + reg_val = (offset_r & DA7213_ALC_OFFSET_19_16) >> 16; + snd_soc_write(codec, DA7213_ALC_OFFSET_MAN_U_R, reg_val); + + /* Enable analog/digital gain mode & offset cancellation */ + snd_soc_update_bits(codec, DA7213_ALC_CTRL1, + DA7213_ALC_OFFSET_EN | DA7213_ALC_SYNC_MODE, + DA7213_ALC_OFFSET_EN | DA7213_ALC_SYNC_MODE); +} + +static void da7213_alc_calib_auto(struct snd_soc_codec *codec) +{ + u8 alc_ctrl1; + + /* Begin auto calibration and wait for completion */ + snd_soc_update_bits(codec, DA7213_ALC_CTRL1, DA7213_ALC_AUTO_CALIB_EN, + DA7213_ALC_AUTO_CALIB_EN); + do { + alc_ctrl1 = snd_soc_read(codec, DA7213_ALC_CTRL1); + } while (alc_ctrl1 & DA7213_ALC_AUTO_CALIB_EN); + + /* If auto calibration fails, fall back to digital gain only mode */ + if (alc_ctrl1 & DA7213_ALC_CALIB_OVERFLOW) { + dev_warn(codec->dev, + "ALC auto calibration failed with overflow\n"); + snd_soc_update_bits(codec, DA7213_ALC_CTRL1, + DA7213_ALC_OFFSET_EN | DA7213_ALC_SYNC_MODE, + 0); + } else { + /* Enable analog/digital gain mode & offset cancellation */ + snd_soc_update_bits(codec, DA7213_ALC_CTRL1, + DA7213_ALC_OFFSET_EN | DA7213_ALC_SYNC_MODE, + DA7213_ALC_OFFSET_EN | DA7213_ALC_SYNC_MODE); + } + +} + +static void da7213_alc_calib(struct snd_soc_codec *codec) +{ + struct da7213_priv *da7213 = snd_soc_codec_get_drvdata(codec); + u8 adc_l_ctrl, adc_r_ctrl; + u8 mixin_l_sel, mixin_r_sel; + u8 mic_1_ctrl, mic_2_ctrl; + + /* Save current values from ADC control registers */ + adc_l_ctrl = snd_soc_read(codec, DA7213_ADC_L_CTRL); + adc_r_ctrl = snd_soc_read(codec, DA7213_ADC_R_CTRL); + + /* Save current values from MIXIN_L/R_SELECT registers */ + mixin_l_sel = snd_soc_read(codec, DA7213_MIXIN_L_SELECT); + mixin_r_sel = snd_soc_read(codec, DA7213_MIXIN_R_SELECT); + + /* Save current values from MIC control registers */ + mic_1_ctrl = snd_soc_read(codec, DA7213_MIC_1_CTRL); + mic_2_ctrl = snd_soc_read(codec, DA7213_MIC_2_CTRL); + + /* Enable ADC Left and Right */ + snd_soc_update_bits(codec, DA7213_ADC_L_CTRL, DA7213_ADC_EN, + DA7213_ADC_EN); + snd_soc_update_bits(codec, DA7213_ADC_R_CTRL, DA7213_ADC_EN, + DA7213_ADC_EN); + + /* Enable MIC paths */ + snd_soc_update_bits(codec, DA7213_MIXIN_L_SELECT, + DA7213_MIXIN_L_MIX_SELECT_MIC_1 | + DA7213_MIXIN_L_MIX_SELECT_MIC_2, + DA7213_MIXIN_L_MIX_SELECT_MIC_1 | + DA7213_MIXIN_L_MIX_SELECT_MIC_2); + snd_soc_update_bits(codec, DA7213_MIXIN_R_SELECT, + DA7213_MIXIN_R_MIX_SELECT_MIC_2 | + DA7213_MIXIN_R_MIX_SELECT_MIC_1, + DA7213_MIXIN_R_MIX_SELECT_MIC_2 | + DA7213_MIXIN_R_MIX_SELECT_MIC_1); + + /* Mute MIC PGAs */ + snd_soc_update_bits(codec, DA7213_MIC_1_CTRL, DA7213_MUTE_EN, + DA7213_MUTE_EN); + snd_soc_update_bits(codec, DA7213_MIC_2_CTRL, DA7213_MUTE_EN, + DA7213_MUTE_EN); + + /* Perform calibration */ + if (da7213->alc_calib_auto) + da7213_alc_calib_auto(codec); + else + da7213_alc_calib_man(codec); + + /* Restore MIXIN_L/R_SELECT registers to their original states */ + snd_soc_write(codec, DA7213_MIXIN_L_SELECT, mixin_l_sel); + snd_soc_write(codec, DA7213_MIXIN_R_SELECT, mixin_r_sel); + + /* Restore ADC control registers to their original states */ + snd_soc_write(codec, DA7213_ADC_L_CTRL, adc_l_ctrl); + snd_soc_write(codec, DA7213_ADC_R_CTRL, adc_r_ctrl); + + /* Restore original values of MIC control registers */ + snd_soc_write(codec, DA7213_MIC_1_CTRL, mic_1_ctrl); + snd_soc_write(codec, DA7213_MIC_2_CTRL, mic_2_ctrl); +} + +static int da7213_put_mixin_gain(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct da7213_priv *da7213 = snd_soc_codec_get_drvdata(codec); + int ret; + + ret = snd_soc_put_volsw_2r(kcontrol, ucontrol); + + /* If ALC in operation, make sure calibrated offsets are updated */ + if ((!ret) && (da7213->alc_en)) + da7213_alc_calib(codec); + + return ret; +} + +static int da7213_put_alc_sw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct da7213_priv *da7213 = snd_soc_codec_get_drvdata(codec); + + /* Force ALC offset calibration if enabling ALC */ + if (ucontrol->value.integer.value[0] || + ucontrol->value.integer.value[1]) { + if (!da7213->alc_en) { + da7213_alc_calib(codec); + da7213->alc_en = true; + } + } else { + da7213->alc_en = false; + } + + return snd_soc_put_volsw(kcontrol, ucontrol); +} + + +/* + * KControls + */ + +static const struct snd_kcontrol_new da7213_snd_controls[] = { + + /* Volume controls */ + SOC_SINGLE_TLV("Mic 1 Volume", DA7213_MIC_1_GAIN, + DA7213_MIC_AMP_GAIN_SHIFT, DA7213_MIC_AMP_GAIN_MAX, + DA7213_NO_INVERT, mic_vol_tlv), + SOC_SINGLE_TLV("Mic 2 Volume", DA7213_MIC_2_GAIN, + DA7213_MIC_AMP_GAIN_SHIFT, DA7213_MIC_AMP_GAIN_MAX, + DA7213_NO_INVERT, mic_vol_tlv), + SOC_DOUBLE_R_TLV("Aux Volume", DA7213_AUX_L_GAIN, DA7213_AUX_R_GAIN, + DA7213_AUX_AMP_GAIN_SHIFT, DA7213_AUX_AMP_GAIN_MAX, + DA7213_NO_INVERT, aux_vol_tlv), + SOC_DOUBLE_R_EXT_TLV("Mixin PGA Volume", DA7213_MIXIN_L_GAIN, + DA7213_MIXIN_R_GAIN, DA7213_MIXIN_AMP_GAIN_SHIFT, + DA7213_MIXIN_AMP_GAIN_MAX, DA7213_NO_INVERT, + snd_soc_get_volsw_2r, da7213_put_mixin_gain, + mixin_gain_tlv), + SOC_DOUBLE_R_TLV("ADC Volume", DA7213_ADC_L_GAIN, DA7213_ADC_R_GAIN, + DA7213_ADC_AMP_GAIN_SHIFT, DA7213_ADC_AMP_GAIN_MAX, + DA7213_NO_INVERT, digital_gain_tlv), + SOC_DOUBLE_R_TLV("DAC Volume", DA7213_DAC_L_GAIN, DA7213_DAC_R_GAIN, + DA7213_DAC_AMP_GAIN_SHIFT, DA7213_DAC_AMP_GAIN_MAX, + DA7213_NO_INVERT, digital_gain_tlv), + SOC_DOUBLE_R_TLV("Headphone Volume", DA7213_HP_L_GAIN, DA7213_HP_R_GAIN, + DA7213_HP_AMP_GAIN_SHIFT, DA7213_HP_AMP_GAIN_MAX, + DA7213_NO_INVERT, hp_vol_tlv), + SOC_SINGLE_TLV("Lineout Volume", DA7213_LINE_GAIN, + DA7213_LINE_AMP_GAIN_SHIFT, DA7213_LINE_AMP_GAIN_MAX, + DA7213_NO_INVERT, lineout_vol_tlv), + + /* DAC Equalizer controls */ + SOC_SINGLE("DAC EQ Switch", DA7213_DAC_FILTERS4, DA7213_DAC_EQ_EN_SHIFT, + DA7213_DAC_EQ_EN_MAX, DA7213_NO_INVERT), + SOC_SINGLE_TLV("DAC EQ1 Volume", DA7213_DAC_FILTERS2, + DA7213_DAC_EQ_BAND1_SHIFT, DA7213_DAC_EQ_BAND_MAX, + DA7213_NO_INVERT, eq_gain_tlv), + SOC_SINGLE_TLV("DAC EQ2 Volume", DA7213_DAC_FILTERS2, + DA7213_DAC_EQ_BAND2_SHIFT, DA7213_DAC_EQ_BAND_MAX, + DA7213_NO_INVERT, eq_gain_tlv), + SOC_SINGLE_TLV("DAC EQ3 Volume", DA7213_DAC_FILTERS3, + DA7213_DAC_EQ_BAND3_SHIFT, DA7213_DAC_EQ_BAND_MAX, + DA7213_NO_INVERT, eq_gain_tlv), + SOC_SINGLE_TLV("DAC EQ4 Volume", DA7213_DAC_FILTERS3, + DA7213_DAC_EQ_BAND4_SHIFT, DA7213_DAC_EQ_BAND_MAX, + DA7213_NO_INVERT, eq_gain_tlv), + SOC_SINGLE_TLV("DAC EQ5 Volume", DA7213_DAC_FILTERS4, + DA7213_DAC_EQ_BAND5_SHIFT, DA7213_DAC_EQ_BAND_MAX, + DA7213_NO_INVERT, eq_gain_tlv), + + /* High Pass Filter and Voice Mode controls */ + SOC_SINGLE("ADC HPF Switch", DA7213_ADC_FILTERS1, DA7213_HPF_EN_SHIFT, + DA7213_HPF_EN_MAX, DA7213_NO_INVERT), + SOC_ENUM("ADC HPF Cutoff", da7213_adc_audio_hpf_corner), + SOC_SINGLE("ADC Voice Mode Switch", DA7213_ADC_FILTERS1, + DA7213_VOICE_EN_SHIFT, DA7213_VOICE_EN_MAX, + DA7213_NO_INVERT), + SOC_ENUM("ADC Voice Cutoff", da7213_adc_voice_hpf_corner), + + SOC_SINGLE("DAC HPF Switch", DA7213_DAC_FILTERS1, DA7213_HPF_EN_SHIFT, + DA7213_HPF_EN_MAX, DA7213_NO_INVERT), + SOC_ENUM("DAC HPF Cutoff", da7213_dac_audio_hpf_corner), + SOC_SINGLE("DAC Voice Mode Switch", DA7213_DAC_FILTERS1, + DA7213_VOICE_EN_SHIFT, DA7213_VOICE_EN_MAX, + DA7213_NO_INVERT), + SOC_ENUM("DAC Voice Cutoff", da7213_dac_voice_hpf_corner), + + /* Mute controls */ + SOC_SINGLE("Mic 1 Switch", DA7213_MIC_1_CTRL, DA7213_MUTE_EN_SHIFT, + DA7213_MUTE_EN_MAX, DA7213_INVERT), + SOC_SINGLE("Mic 2 Switch", DA7213_MIC_2_CTRL, DA7213_MUTE_EN_SHIFT, + DA7213_MUTE_EN_MAX, DA7213_INVERT), + SOC_DOUBLE_R("Aux Switch", DA7213_AUX_L_CTRL, DA7213_AUX_R_CTRL, + DA7213_MUTE_EN_SHIFT, DA7213_MUTE_EN_MAX, DA7213_INVERT), + SOC_DOUBLE_R("Mixin PGA Switch", DA7213_MIXIN_L_CTRL, + DA7213_MIXIN_R_CTRL, DA7213_MUTE_EN_SHIFT, + DA7213_MUTE_EN_MAX, DA7213_INVERT), + SOC_DOUBLE_R("ADC Switch", DA7213_ADC_L_CTRL, DA7213_ADC_R_CTRL, + DA7213_MUTE_EN_SHIFT, DA7213_MUTE_EN_MAX, DA7213_INVERT), + SOC_DOUBLE_R("Headphone Switch", DA7213_HP_L_CTRL, DA7213_HP_R_CTRL, + DA7213_MUTE_EN_SHIFT, DA7213_MUTE_EN_MAX, DA7213_INVERT), + SOC_SINGLE("Lineout Switch", DA7213_LINE_CTRL, DA7213_MUTE_EN_SHIFT, + DA7213_MUTE_EN_MAX, DA7213_INVERT), + SOC_SINGLE("DAC Soft Mute Switch", DA7213_DAC_FILTERS5, + DA7213_DAC_SOFTMUTE_EN_SHIFT, DA7213_DAC_SOFTMUTE_EN_MAX, + DA7213_NO_INVERT), + SOC_ENUM("DAC Soft Mute Rate", da7213_dac_soft_mute_rate), + + /* Zero Cross controls */ + SOC_DOUBLE_R("Aux ZC Switch", DA7213_AUX_L_CTRL, DA7213_AUX_R_CTRL, + DA7213_ZC_EN_SHIFT, DA7213_ZC_EN_MAX, DA7213_NO_INVERT), + SOC_DOUBLE_R("Mixin PGA ZC Switch", DA7213_MIXIN_L_CTRL, + DA7213_MIXIN_R_CTRL, DA7213_ZC_EN_SHIFT, DA7213_ZC_EN_MAX, + DA7213_NO_INVERT), + SOC_DOUBLE_R("Headphone ZC Switch", DA7213_HP_L_CTRL, DA7213_HP_R_CTRL, + DA7213_ZC_EN_SHIFT, DA7213_ZC_EN_MAX, DA7213_NO_INVERT), + + /* Gain Ramping controls */ + SOC_DOUBLE_R("Aux Gain Ramping Switch", DA7213_AUX_L_CTRL, + DA7213_AUX_R_CTRL, DA7213_GAIN_RAMP_EN_SHIFT, + DA7213_GAIN_RAMP_EN_MAX, DA7213_NO_INVERT), + SOC_DOUBLE_R("Mixin Gain Ramping Switch", DA7213_MIXIN_L_CTRL, + DA7213_MIXIN_R_CTRL, DA7213_GAIN_RAMP_EN_SHIFT, + DA7213_GAIN_RAMP_EN_MAX, DA7213_NO_INVERT), + SOC_DOUBLE_R("ADC Gain Ramping Switch", DA7213_ADC_L_CTRL, + DA7213_ADC_R_CTRL, DA7213_GAIN_RAMP_EN_SHIFT, + DA7213_GAIN_RAMP_EN_MAX, DA7213_NO_INVERT), + SOC_DOUBLE_R("DAC Gain Ramping Switch", DA7213_DAC_L_CTRL, + DA7213_DAC_R_CTRL, DA7213_GAIN_RAMP_EN_SHIFT, + DA7213_GAIN_RAMP_EN_MAX, DA7213_NO_INVERT), + SOC_DOUBLE_R("Headphone Gain Ramping Switch", DA7213_HP_L_CTRL, + DA7213_HP_R_CTRL, DA7213_GAIN_RAMP_EN_SHIFT, + DA7213_GAIN_RAMP_EN_MAX, DA7213_NO_INVERT), + SOC_SINGLE("Lineout Gain Ramping Switch", DA7213_LINE_CTRL, + DA7213_GAIN_RAMP_EN_SHIFT, DA7213_GAIN_RAMP_EN_MAX, + DA7213_NO_INVERT), + SOC_ENUM("Gain Ramping Rate", da7213_gain_ramp_rate), + + /* DAC Noise Gate controls */ + SOC_SINGLE("DAC NG Switch", DA7213_DAC_NG_CTRL, DA7213_DAC_NG_EN_SHIFT, + DA7213_DAC_NG_EN_MAX, DA7213_NO_INVERT), + SOC_ENUM("DAC NG Setup Time", da7213_dac_ng_setup_time), + SOC_ENUM("DAC NG Rampup Rate", da7213_dac_ng_rampup_rate), + SOC_ENUM("DAC NG Rampdown Rate", da7213_dac_ng_rampdown_rate), + SOC_SINGLE("DAC NG OFF Threshold", DA7213_DAC_NG_OFF_THRESHOLD, + DA7213_DAC_NG_THRESHOLD_SHIFT, DA7213_DAC_NG_THRESHOLD_MAX, + DA7213_NO_INVERT), + SOC_SINGLE("DAC NG ON Threshold", DA7213_DAC_NG_ON_THRESHOLD, + DA7213_DAC_NG_THRESHOLD_SHIFT, DA7213_DAC_NG_THRESHOLD_MAX, + DA7213_NO_INVERT), + + /* DAC Routing & Inversion */ + SOC_DOUBLE("DAC Mono Switch", DA7213_DIG_ROUTING_DAC, + DA7213_DAC_L_MONO_SHIFT, DA7213_DAC_R_MONO_SHIFT, + DA7213_DAC_MONO_MAX, DA7213_NO_INVERT), + SOC_DOUBLE("DAC Invert Switch", DA7213_DIG_CTRL, DA7213_DAC_L_INV_SHIFT, + DA7213_DAC_R_INV_SHIFT, DA7213_DAC_INV_MAX, + DA7213_NO_INVERT), + + /* DMIC controls */ + SOC_DOUBLE_R("DMIC Switch", DA7213_MIXIN_L_SELECT, + DA7213_MIXIN_R_SELECT, DA7213_DMIC_EN_SHIFT, + DA7213_DMIC_EN_MAX, DA7213_NO_INVERT), + + /* ALC Controls */ + SOC_DOUBLE_EXT("ALC Switch", DA7213_ALC_CTRL1, DA7213_ALC_L_EN_SHIFT, + DA7213_ALC_R_EN_SHIFT, DA7213_ALC_EN_MAX, + DA7213_NO_INVERT, snd_soc_get_volsw, da7213_put_alc_sw), + SOC_ENUM("ALC Attack Rate", da7213_alc_attack_rate), + SOC_ENUM("ALC Release Rate", da7213_alc_release_rate), + SOC_ENUM("ALC Hold Time", da7213_alc_hold_time), + /* + * Rate at which input signal envelope is tracked as the signal gets + * larger + */ + SOC_ENUM("ALC Integ Attack Rate", da7213_alc_integ_attack_rate), + /* + * Rate at which input signal envelope is tracked as the signal gets + * smaller + */ + SOC_ENUM("ALC Integ Release Rate", da7213_alc_integ_release_rate), + SOC_SINGLE_TLV("ALC Noise Threshold Volume", DA7213_ALC_NOISE, + DA7213_ALC_THRESHOLD_SHIFT, DA7213_ALC_THRESHOLD_MAX, + DA7213_INVERT, alc_threshold_tlv), + SOC_SINGLE_TLV("ALC Min Threshold Volume", DA7213_ALC_TARGET_MIN, + DA7213_ALC_THRESHOLD_SHIFT, DA7213_ALC_THRESHOLD_MAX, + DA7213_INVERT, alc_threshold_tlv), + SOC_SINGLE_TLV("ALC Max Threshold Volume", DA7213_ALC_TARGET_MAX, + DA7213_ALC_THRESHOLD_SHIFT, DA7213_ALC_THRESHOLD_MAX, + DA7213_INVERT, alc_threshold_tlv), + SOC_SINGLE_TLV("ALC Max Attenuation Volume", DA7213_ALC_GAIN_LIMITS, + DA7213_ALC_ATTEN_MAX_SHIFT, + DA7213_ALC_ATTEN_GAIN_MAX_MAX, DA7213_NO_INVERT, + alc_gain_tlv), + SOC_SINGLE_TLV("ALC Max Gain Volume", DA7213_ALC_GAIN_LIMITS, + DA7213_ALC_GAIN_MAX_SHIFT, DA7213_ALC_ATTEN_GAIN_MAX_MAX, + DA7213_NO_INVERT, alc_gain_tlv), + SOC_SINGLE_TLV("ALC Min Analog Gain Volume", DA7213_ALC_ANA_GAIN_LIMITS, + DA7213_ALC_ANA_GAIN_MIN_SHIFT, DA7213_ALC_ANA_GAIN_MAX, + DA7213_NO_INVERT, alc_analog_gain_tlv), + SOC_SINGLE_TLV("ALC Max Analog Gain Volume", DA7213_ALC_ANA_GAIN_LIMITS, + DA7213_ALC_ANA_GAIN_MAX_SHIFT, DA7213_ALC_ANA_GAIN_MAX, + DA7213_NO_INVERT, alc_analog_gain_tlv), + SOC_SINGLE("ALC Anticlip Mode Switch", DA7213_ALC_ANTICLIP_CTRL, + DA7213_ALC_ANTICLIP_EN_SHIFT, DA7213_ALC_ANTICLIP_EN_MAX, + DA7213_NO_INVERT), + SOC_SINGLE("ALC Anticlip Level", DA7213_ALC_ANTICLIP_LEVEL, + DA7213_ALC_ANTICLIP_LEVEL_SHIFT, + DA7213_ALC_ANTICLIP_LEVEL_MAX, DA7213_NO_INVERT), +}; + + +/* + * DAPM + */ + +/* + * Enums + */ + +/* MIC PGA source select */ +static const char * const da7213_mic_amp_in_sel_txt[] = { + "Differential", "MIC_P", "MIC_N" +}; + +static const struct soc_enum da7213_mic_1_amp_in_sel = + SOC_ENUM_SINGLE(DA7213_MIC_1_CTRL, DA7213_MIC_AMP_IN_SEL_SHIFT, + DA7213_MIC_AMP_IN_SEL_MAX, da7213_mic_amp_in_sel_txt); +static const struct snd_kcontrol_new da7213_mic_1_amp_in_sel_mux = + SOC_DAPM_ENUM("Mic 1 Amp Source MUX", da7213_mic_1_amp_in_sel); + +static const struct soc_enum da7213_mic_2_amp_in_sel = + SOC_ENUM_SINGLE(DA7213_MIC_2_CTRL, DA7213_MIC_AMP_IN_SEL_SHIFT, + DA7213_MIC_AMP_IN_SEL_MAX, da7213_mic_amp_in_sel_txt); +static const struct snd_kcontrol_new da7213_mic_2_amp_in_sel_mux = + SOC_DAPM_ENUM("Mic 2 Amp Source MUX", da7213_mic_2_amp_in_sel); + +/* DAI routing select */ +static const char * const da7213_dai_src_txt[] = { + "ADC Left", "ADC Right", "DAI Input Left", "DAI Input Right" +}; + +static const struct soc_enum da7213_dai_l_src = + SOC_ENUM_SINGLE(DA7213_DIG_ROUTING_DAI, DA7213_DAI_L_SRC_SHIFT, + DA7213_DAI_SRC_MAX, da7213_dai_src_txt); +static const struct snd_kcontrol_new da7213_dai_l_src_mux = + SOC_DAPM_ENUM("DAI Left Source MUX", da7213_dai_l_src); + +static const struct soc_enum da7213_dai_r_src = + SOC_ENUM_SINGLE(DA7213_DIG_ROUTING_DAI, DA7213_DAI_R_SRC_SHIFT, + DA7213_DAI_SRC_MAX, da7213_dai_src_txt); +static const struct snd_kcontrol_new da7213_dai_r_src_mux = + SOC_DAPM_ENUM("DAI Right Source MUX", da7213_dai_r_src); + +/* DAC routing select */ +static const char * const da7213_dac_src_txt[] = { + "ADC Output Left", "ADC Output Right", "DAI Input Left", + "DAI Input Right" +}; + +static const struct soc_enum da7213_dac_l_src = + SOC_ENUM_SINGLE(DA7213_DIG_ROUTING_DAC, DA7213_DAC_L_SRC_SHIFT, + DA7213_DAC_SRC_MAX, da7213_dac_src_txt); +static const struct snd_kcontrol_new da7213_dac_l_src_mux = + SOC_DAPM_ENUM("DAC Left Source MUX", da7213_dac_l_src); + +static const struct soc_enum da7213_dac_r_src = + SOC_ENUM_SINGLE(DA7213_DIG_ROUTING_DAC, DA7213_DAC_R_SRC_SHIFT, + DA7213_DAC_SRC_MAX, da7213_dac_src_txt); +static const struct snd_kcontrol_new da7213_dac_r_src_mux = + SOC_DAPM_ENUM("DAC Right Source MUX", da7213_dac_r_src); + +/* + * Mixer Controls + */ + +/* Mixin Left */ +static const struct snd_kcontrol_new da7213_dapm_mixinl_controls[] = { + SOC_DAPM_SINGLE("Aux Left Switch", DA7213_MIXIN_L_SELECT, + DA7213_MIXIN_L_MIX_SELECT_AUX_L_SHIFT, + DA7213_MIXIN_L_MIX_SELECT_MAX, DA7213_NO_INVERT), + SOC_DAPM_SINGLE("Mic 1 Switch", DA7213_MIXIN_L_SELECT, + DA7213_MIXIN_L_MIX_SELECT_MIC_1_SHIFT, + DA7213_MIXIN_L_MIX_SELECT_MAX, DA7213_NO_INVERT), + SOC_DAPM_SINGLE("Mic 2 Switch", DA7213_MIXIN_L_SELECT, + DA7213_MIXIN_L_MIX_SELECT_MIC_2_SHIFT, + DA7213_MIXIN_L_MIX_SELECT_MAX, DA7213_NO_INVERT), + SOC_DAPM_SINGLE("Mixin Right Switch", DA7213_MIXIN_L_SELECT, + DA7213_MIXIN_L_MIX_SELECT_MIXIN_R_SHIFT, + DA7213_MIXIN_L_MIX_SELECT_MAX, DA7213_NO_INVERT), +}; + +/* Mixin Right */ +static const struct snd_kcontrol_new da7213_dapm_mixinr_controls[] = { + SOC_DAPM_SINGLE("Aux Right Switch", DA7213_MIXIN_R_SELECT, + DA7213_MIXIN_R_MIX_SELECT_AUX_R_SHIFT, + DA7213_MIXIN_R_MIX_SELECT_MAX, DA7213_NO_INVERT), + SOC_DAPM_SINGLE("Mic 2 Switch", DA7213_MIXIN_R_SELECT, + DA7213_MIXIN_R_MIX_SELECT_MIC_2_SHIFT, + DA7213_MIXIN_R_MIX_SELECT_MAX, DA7213_NO_INVERT), + SOC_DAPM_SINGLE("Mic 1 Switch", DA7213_MIXIN_R_SELECT, + DA7213_MIXIN_R_MIX_SELECT_MIC_1_SHIFT, + DA7213_MIXIN_R_MIX_SELECT_MAX, DA7213_NO_INVERT), + SOC_DAPM_SINGLE("Mixin Left Switch", DA7213_MIXIN_R_SELECT, + DA7213_MIXIN_R_MIX_SELECT_MIXIN_L_SHIFT, + DA7213_MIXIN_R_MIX_SELECT_MAX, DA7213_NO_INVERT), +}; + +/* Mixout Left */ +static const struct snd_kcontrol_new da7213_dapm_mixoutl_controls[] = { + SOC_DAPM_SINGLE("Aux Left Switch", DA7213_MIXOUT_L_SELECT, + DA7213_MIXOUT_L_MIX_SELECT_AUX_L_SHIFT, + DA7213_MIXOUT_L_MIX_SELECT_MAX, DA7213_NO_INVERT), + SOC_DAPM_SINGLE("Mixin Left Switch", DA7213_MIXOUT_L_SELECT, + DA7213_MIXOUT_L_MIX_SELECT_MIXIN_L_SHIFT, + DA7213_MIXOUT_L_MIX_SELECT_MAX, DA7213_NO_INVERT), + SOC_DAPM_SINGLE("Mixin Right Switch", DA7213_MIXOUT_L_SELECT, + DA7213_MIXOUT_L_MIX_SELECT_MIXIN_R_SHIFT, + DA7213_MIXOUT_L_MIX_SELECT_MAX, DA7213_NO_INVERT), + SOC_DAPM_SINGLE("DAC Left Switch", DA7213_MIXOUT_L_SELECT, + DA7213_MIXOUT_L_MIX_SELECT_DAC_L_SHIFT, + DA7213_MIXOUT_L_MIX_SELECT_MAX, DA7213_NO_INVERT), + SOC_DAPM_SINGLE("Aux Left Invert Switch", DA7213_MIXOUT_L_SELECT, + DA7213_MIXOUT_L_MIX_SELECT_AUX_L_INVERTED_SHIFT, + DA7213_MIXOUT_L_MIX_SELECT_MAX, DA7213_NO_INVERT), + SOC_DAPM_SINGLE("Mixin Left Invert Switch", DA7213_MIXOUT_L_SELECT, + DA7213_MIXOUT_L_MIX_SELECT_MIXIN_L_INVERTED_SHIFT, + DA7213_MIXOUT_L_MIX_SELECT_MAX, DA7213_NO_INVERT), + SOC_DAPM_SINGLE("Mixin Right Invert Switch", DA7213_MIXOUT_L_SELECT, + DA7213_MIXOUT_L_MIX_SELECT_MIXIN_R_INVERTED_SHIFT, + DA7213_MIXOUT_L_MIX_SELECT_MAX, DA7213_NO_INVERT), +}; + +/* Mixout Right */ +static const struct snd_kcontrol_new da7213_dapm_mixoutr_controls[] = { + SOC_DAPM_SINGLE("Aux Right Switch", DA7213_MIXOUT_R_SELECT, + DA7213_MIXOUT_R_MIX_SELECT_AUX_R_SHIFT, + DA7213_MIXOUT_R_MIX_SELECT_MAX, DA7213_NO_INVERT), + SOC_DAPM_SINGLE("Mixin Right Switch", DA7213_MIXOUT_R_SELECT, + DA7213_MIXOUT_R_MIX_SELECT_MIXIN_R_SHIFT, + DA7213_MIXOUT_R_MIX_SELECT_MAX, DA7213_NO_INVERT), + SOC_DAPM_SINGLE("Mixin Left Switch", DA7213_MIXOUT_R_SELECT, + DA7213_MIXOUT_R_MIX_SELECT_MIXIN_L_SHIFT, + DA7213_MIXOUT_R_MIX_SELECT_MAX, DA7213_NO_INVERT), + SOC_DAPM_SINGLE("DAC Right Switch", DA7213_MIXOUT_R_SELECT, + DA7213_MIXOUT_R_MIX_SELECT_DAC_R_SHIFT, + DA7213_MIXOUT_R_MIX_SELECT_MAX, DA7213_NO_INVERT), + SOC_DAPM_SINGLE("Aux Right Invert Switch", DA7213_MIXOUT_R_SELECT, + DA7213_MIXOUT_R_MIX_SELECT_AUX_R_INVERTED_SHIFT, + DA7213_MIXOUT_R_MIX_SELECT_MAX, DA7213_NO_INVERT), + SOC_DAPM_SINGLE("Mixin Right Invert Switch", DA7213_MIXOUT_R_SELECT, + DA7213_MIXOUT_R_MIX_SELECT_MIXIN_R_INVERTED_SHIFT, + DA7213_MIXOUT_R_MIX_SELECT_MAX, DA7213_NO_INVERT), + SOC_DAPM_SINGLE("Mixin Left Invert Switch", DA7213_MIXOUT_R_SELECT, + DA7213_MIXOUT_R_MIX_SELECT_MIXIN_L_INVERTED_SHIFT, + DA7213_MIXOUT_R_MIX_SELECT_MAX, DA7213_NO_INVERT), +}; + + +/* + * DAPM widgets + */ + +static const struct snd_soc_dapm_widget da7213_dapm_widgets[] = { + /* + * Input & Output + */ + + /* Use a supply here as this controls both input & output DAIs */ + SND_SOC_DAPM_SUPPLY("DAI", DA7213_DAI_CTRL, DA7213_DAI_EN_SHIFT, + DA7213_NO_INVERT, NULL, 0), + + /* + * Input + */ + + /* Input Lines */ + SND_SOC_DAPM_INPUT("MIC1"), + SND_SOC_DAPM_INPUT("MIC2"), + SND_SOC_DAPM_INPUT("AUXL"), + SND_SOC_DAPM_INPUT("AUXR"), + + /* MUXs for Mic PGA source selection */ + SND_SOC_DAPM_MUX("Mic 1 Amp Source MUX", SND_SOC_NOPM, 0, 0, + &da7213_mic_1_amp_in_sel_mux), + SND_SOC_DAPM_MUX("Mic 2 Amp Source MUX", SND_SOC_NOPM, 0, 0, + &da7213_mic_2_amp_in_sel_mux), + + /* Input PGAs */ + SND_SOC_DAPM_PGA("Mic 1 PGA", DA7213_MIC_1_CTRL, DA7213_AMP_EN_SHIFT, + DA7213_NO_INVERT, NULL, 0), + SND_SOC_DAPM_PGA("Mic 2 PGA", DA7213_MIC_2_CTRL, DA7213_AMP_EN_SHIFT, + DA7213_NO_INVERT, NULL, 0), + SND_SOC_DAPM_PGA("Aux Left PGA", DA7213_AUX_L_CTRL, DA7213_AMP_EN_SHIFT, + DA7213_NO_INVERT, NULL, 0), + SND_SOC_DAPM_PGA("Aux Right PGA", DA7213_AUX_R_CTRL, + DA7213_AMP_EN_SHIFT, DA7213_NO_INVERT, NULL, 0), + SND_SOC_DAPM_PGA("Mixin Left PGA", DA7213_MIXIN_L_CTRL, + DA7213_AMP_EN_SHIFT, DA7213_NO_INVERT, NULL, 0), + SND_SOC_DAPM_PGA("Mixin Right PGA", DA7213_MIXIN_R_CTRL, + DA7213_AMP_EN_SHIFT, DA7213_NO_INVERT, NULL, 0), + + /* Mic Biases */ + SND_SOC_DAPM_SUPPLY("Mic Bias 1", DA7213_MICBIAS_CTRL, + DA7213_MICBIAS1_EN_SHIFT, DA7213_NO_INVERT, + NULL, 0), + SND_SOC_DAPM_SUPPLY("Mic Bias 2", DA7213_MICBIAS_CTRL, + DA7213_MICBIAS2_EN_SHIFT, DA7213_NO_INVERT, + NULL, 0), + + /* Input Mixers */ + SND_SOC_DAPM_MIXER("Mixin Left", SND_SOC_NOPM, 0, 0, + &da7213_dapm_mixinl_controls[0], + ARRAY_SIZE(da7213_dapm_mixinl_controls)), + SND_SOC_DAPM_MIXER("Mixin Right", SND_SOC_NOPM, 0, 0, + &da7213_dapm_mixinr_controls[0], + ARRAY_SIZE(da7213_dapm_mixinr_controls)), + + /* ADCs */ + SND_SOC_DAPM_ADC("ADC Left", NULL, DA7213_ADC_L_CTRL, + DA7213_ADC_EN_SHIFT, DA7213_NO_INVERT), + SND_SOC_DAPM_ADC("ADC Right", NULL, DA7213_ADC_R_CTRL, + DA7213_ADC_EN_SHIFT, DA7213_NO_INVERT), + + /* DAI */ + SND_SOC_DAPM_MUX("DAI Left Source MUX", SND_SOC_NOPM, 0, 0, + &da7213_dai_l_src_mux), + SND_SOC_DAPM_MUX("DAI Right Source MUX", SND_SOC_NOPM, 0, 0, + &da7213_dai_r_src_mux), + SND_SOC_DAPM_AIF_OUT("DAIOUTL", "Capture", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("DAIOUTR", "Capture", 1, SND_SOC_NOPM, 0, 0), + + /* + * Output + */ + + /* DAI */ + SND_SOC_DAPM_AIF_IN("DAIINL", "Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("DAIINR", "Playback", 1, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_MUX("DAC Left Source MUX", SND_SOC_NOPM, 0, 0, + &da7213_dac_l_src_mux), + SND_SOC_DAPM_MUX("DAC Right Source MUX", SND_SOC_NOPM, 0, 0, + &da7213_dac_r_src_mux), + + /* DACs */ + SND_SOC_DAPM_DAC("DAC Left", NULL, DA7213_DAC_L_CTRL, + DA7213_DAC_EN_SHIFT, DA7213_NO_INVERT), + SND_SOC_DAPM_DAC("DAC Right", NULL, DA7213_DAC_R_CTRL, + DA7213_DAC_EN_SHIFT, DA7213_NO_INVERT), + + /* Output Mixers */ + SND_SOC_DAPM_MIXER("Mixout Left", SND_SOC_NOPM, 0, 0, + &da7213_dapm_mixoutl_controls[0], + ARRAY_SIZE(da7213_dapm_mixoutl_controls)), + SND_SOC_DAPM_MIXER("Mixout Right", SND_SOC_NOPM, 0, 0, + &da7213_dapm_mixoutr_controls[0], + ARRAY_SIZE(da7213_dapm_mixoutr_controls)), + + /* Output PGAs */ + SND_SOC_DAPM_PGA("Mixout Left PGA", DA7213_MIXOUT_L_CTRL, + DA7213_AMP_EN_SHIFT, DA7213_NO_INVERT, NULL, 0), + SND_SOC_DAPM_PGA("Mixout Right PGA", DA7213_MIXOUT_R_CTRL, + DA7213_AMP_EN_SHIFT, DA7213_NO_INVERT, NULL, 0), + SND_SOC_DAPM_PGA("Lineout PGA", DA7213_LINE_CTRL, DA7213_AMP_EN_SHIFT, + DA7213_NO_INVERT, NULL, 0), + SND_SOC_DAPM_PGA("Headphone Left PGA", DA7213_HP_L_CTRL, + DA7213_AMP_EN_SHIFT, DA7213_NO_INVERT, NULL, 0), + SND_SOC_DAPM_PGA("Headphone Right PGA", DA7213_HP_R_CTRL, + DA7213_AMP_EN_SHIFT, DA7213_NO_INVERT, NULL, 0), + + /* Charge Pump */ + SND_SOC_DAPM_SUPPLY("Charge Pump", DA7213_CP_CTRL, DA7213_CP_EN_SHIFT, + DA7213_NO_INVERT, NULL, 0), + + /* Output Lines */ + SND_SOC_DAPM_OUTPUT("HPL"), + SND_SOC_DAPM_OUTPUT("HPR"), + SND_SOC_DAPM_OUTPUT("LINE"), +}; + + +/* + * DAPM audio route definition + */ + +static const struct snd_soc_dapm_route da7213_audio_map[] = { + /* Dest Connecting Widget source */ + + /* Input path */ + {"MIC1", NULL, "Mic Bias 1"}, + {"MIC2", NULL, "Mic Bias 2"}, + + {"Mic 1 Amp Source MUX", "Differential", "MIC1"}, + {"Mic 1 Amp Source MUX", "MIC_P", "MIC1"}, + {"Mic 1 Amp Source MUX", "MIC_N", "MIC1"}, + + {"Mic 2 Amp Source MUX", "Differential", "MIC2"}, + {"Mic 2 Amp Source MUX", "MIC_P", "MIC2"}, + {"Mic 2 Amp Source MUX", "MIC_N", "MIC2"}, + + {"Mic 1 PGA", NULL, "Mic 1 Amp Source MUX"}, + {"Mic 2 PGA", NULL, "Mic 2 Amp Source MUX"}, + + {"Aux Left PGA", NULL, "AUXL"}, + {"Aux Right PGA", NULL, "AUXR"}, + + {"Mixin Left", "Aux Left Switch", "Aux Left PGA"}, + {"Mixin Left", "Mic 1 Switch", "Mic 1 PGA"}, + {"Mixin Left", "Mic 2 Switch", "Mic 2 PGA"}, + {"Mixin Left", "Mixin Right Switch", "Mixin Right PGA"}, + + {"Mixin Right", "Aux Right Switch", "Aux Right PGA"}, + {"Mixin Right", "Mic 2 Switch", "Mic 2 PGA"}, + {"Mixin Right", "Mic 1 Switch", "Mic 1 PGA"}, + {"Mixin Right", "Mixin Left Switch", "Mixin Left PGA"}, + + {"Mixin Left PGA", NULL, "Mixin Left"}, + {"ADC Left", NULL, "Mixin Left PGA"}, + + {"Mixin Right PGA", NULL, "Mixin Right"}, + {"ADC Right", NULL, "Mixin Right PGA"}, + + {"DAI Left Source MUX", "ADC Left", "ADC Left"}, + {"DAI Left Source MUX", "ADC Right", "ADC Right"}, + {"DAI Left Source MUX", "DAI Input Left", "DAIINL"}, + {"DAI Left Source MUX", "DAI Input Right", "DAIINR"}, + + {"DAI Right Source MUX", "ADC Left", "ADC Left"}, + {"DAI Right Source MUX", "ADC Right", "ADC Right"}, + {"DAI Right Source MUX", "DAI Input Left", "DAIINL"}, + {"DAI Right Source MUX", "DAI Input Right", "DAIINR"}, + + {"DAIOUTL", NULL, "DAI Left Source MUX"}, + {"DAIOUTR", NULL, "DAI Right Source MUX"}, + + {"DAIOUTL", NULL, "DAI"}, + {"DAIOUTR", NULL, "DAI"}, + + /* Output path */ + {"DAIINL", NULL, "DAI"}, + {"DAIINR", NULL, "DAI"}, + + {"DAC Left Source MUX", "ADC Output Left", "ADC Left"}, + {"DAC Left Source MUX", "ADC Output Right", "ADC Right"}, + {"DAC Left Source MUX", "DAI Input Left", "DAIINL"}, + {"DAC Left Source MUX", "DAI Input Right", "DAIINR"}, + + {"DAC Right Source MUX", "ADC Output Left", "ADC Left"}, + {"DAC Right Source MUX", "ADC Output Right", "ADC Right"}, + {"DAC Right Source MUX", "DAI Input Left", "DAIINL"}, + {"DAC Right Source MUX", "DAI Input Right", "DAIINR"}, + + {"DAC Left", NULL, "DAC Left Source MUX"}, + {"DAC Right", NULL, "DAC Right Source MUX"}, + + {"Mixout Left", "Aux Left Switch", "Aux Left PGA"}, + {"Mixout Left", "Mixin Left Switch", "Mixin Left PGA"}, + {"Mixout Left", "Mixin Right Switch", "Mixin Right PGA"}, + {"Mixout Left", "DAC Left Switch", "DAC Left"}, + {"Mixout Left", "Aux Left Invert Switch", "Aux Left PGA"}, + {"Mixout Left", "Mixin Left Invert Switch", "Mixin Left PGA"}, + {"Mixout Left", "Mixin Right Invert Switch", "Mixin Right PGA"}, + + {"Mixout Right", "Aux Right Switch", "Aux Right PGA"}, + {"Mixout Right", "Mixin Right Switch", "Mixin Right PGA"}, + {"Mixout Right", "Mixin Left Switch", "Mixin Left PGA"}, + {"Mixout Right", "DAC Right Switch", "DAC Right"}, + {"Mixout Right", "Aux Right Invert Switch", "Aux Right PGA"}, + {"Mixout Right", "Mixin Right Invert Switch", "Mixin Right PGA"}, + {"Mixout Right", "Mixin Left Invert Switch", "Mixin Left PGA"}, + + {"Mixout Left PGA", NULL, "Mixout Left"}, + {"Mixout Right PGA", NULL, "Mixout Right"}, + + {"Headphone Left PGA", NULL, "Mixout Left PGA"}, + {"Headphone Left PGA", NULL, "Charge Pump"}, + {"HPL", NULL, "Headphone Left PGA"}, + + {"Headphone Right PGA", NULL, "Mixout Right PGA"}, + {"Headphone Right PGA", NULL, "Charge Pump"}, + {"HPR", NULL, "Headphone Right PGA"}, + + {"Lineout PGA", NULL, "Mixout Right PGA"}, + {"LINE", NULL, "Lineout PGA"}, +}; + +static struct reg_default da7213_reg_defaults[] = { + { DA7213_DIG_ROUTING_DAI, 0x10 }, + { DA7213_SR, 0x0A }, + { DA7213_REFERENCES, 0x80 }, + { DA7213_PLL_FRAC_TOP, 0x00 }, + { DA7213_PLL_FRAC_BOT, 0x00 }, + { DA7213_PLL_INTEGER, 0x20 }, + { DA7213_PLL_CTRL, 0x0C }, + { DA7213_DAI_CLK_MODE, 0x01 }, + { DA7213_DAI_CTRL, 0x08 }, + { DA7213_DIG_ROUTING_DAC, 0x32 }, + { DA7213_AUX_L_GAIN, 0x35 }, + { DA7213_AUX_R_GAIN, 0x35 }, + { DA7213_MIXIN_L_SELECT, 0x00 }, + { DA7213_MIXIN_R_SELECT, 0x00 }, + { DA7213_MIXIN_L_GAIN, 0x03 }, + { DA7213_MIXIN_R_GAIN, 0x03 }, + { DA7213_ADC_L_GAIN, 0x6F }, + { DA7213_ADC_R_GAIN, 0x6F }, + { DA7213_ADC_FILTERS1, 0x80 }, + { DA7213_MIC_1_GAIN, 0x01 }, + { DA7213_MIC_2_GAIN, 0x01 }, + { DA7213_DAC_FILTERS5, 0x00 }, + { DA7213_DAC_FILTERS2, 0x88 }, + { DA7213_DAC_FILTERS3, 0x88 }, + { DA7213_DAC_FILTERS4, 0x08 }, + { DA7213_DAC_FILTERS1, 0x80 }, + { DA7213_DAC_L_GAIN, 0x6F }, + { DA7213_DAC_R_GAIN, 0x6F }, + { DA7213_CP_CTRL, 0x61 }, + { DA7213_HP_L_GAIN, 0x39 }, + { DA7213_HP_R_GAIN, 0x39 }, + { DA7213_LINE_GAIN, 0x30 }, + { DA7213_MIXOUT_L_SELECT, 0x00 }, + { DA7213_MIXOUT_R_SELECT, 0x00 }, + { DA7213_SYSTEM_MODES_INPUT, 0x00 }, + { DA7213_SYSTEM_MODES_OUTPUT, 0x00 }, + { DA7213_AUX_L_CTRL, 0x44 }, + { DA7213_AUX_R_CTRL, 0x44 }, + { DA7213_MICBIAS_CTRL, 0x11 }, + { DA7213_MIC_1_CTRL, 0x40 }, + { DA7213_MIC_2_CTRL, 0x40 }, + { DA7213_MIXIN_L_CTRL, 0x40 }, + { DA7213_MIXIN_R_CTRL, 0x40 }, + { DA7213_ADC_L_CTRL, 0x40 }, + { DA7213_ADC_R_CTRL, 0x40 }, + { DA7213_DAC_L_CTRL, 0x48 }, + { DA7213_DAC_R_CTRL, 0x40 }, + { DA7213_HP_L_CTRL, 0x41 }, + { DA7213_HP_R_CTRL, 0x40 }, + { DA7213_LINE_CTRL, 0x40 }, + { DA7213_MIXOUT_L_CTRL, 0x10 }, + { DA7213_MIXOUT_R_CTRL, 0x10 }, + { DA7213_LDO_CTRL, 0x00 }, + { DA7213_IO_CTRL, 0x00 }, + { DA7213_GAIN_RAMP_CTRL, 0x00}, + { DA7213_MIC_CONFIG, 0x00 }, + { DA7213_PC_COUNT, 0x00 }, + { DA7213_CP_VOL_THRESHOLD1, 0x32 }, + { DA7213_CP_DELAY, 0x95 }, + { DA7213_CP_DETECTOR, 0x00 }, + { DA7213_DAI_OFFSET, 0x00 }, + { DA7213_DIG_CTRL, 0x00 }, + { DA7213_ALC_CTRL2, 0x00 }, + { DA7213_ALC_CTRL3, 0x00 }, + { DA7213_ALC_NOISE, 0x3F }, + { DA7213_ALC_TARGET_MIN, 0x3F }, + { DA7213_ALC_TARGET_MAX, 0x00 }, + { DA7213_ALC_GAIN_LIMITS, 0xFF }, + { DA7213_ALC_ANA_GAIN_LIMITS, 0x71 }, + { DA7213_ALC_ANTICLIP_CTRL, 0x00 }, + { DA7213_ALC_ANTICLIP_LEVEL, 0x00 }, + { DA7213_ALC_OFFSET_MAN_M_L, 0x00 }, + { DA7213_ALC_OFFSET_MAN_U_L, 0x00 }, + { DA7213_ALC_OFFSET_MAN_M_R, 0x00 }, + { DA7213_ALC_OFFSET_MAN_U_R, 0x00 }, + { DA7213_ALC_CIC_OP_LVL_CTRL, 0x00 }, + { DA7213_DAC_NG_SETUP_TIME, 0x00 }, + { DA7213_DAC_NG_OFF_THRESHOLD, 0x00 }, + { DA7213_DAC_NG_ON_THRESHOLD, 0x00 }, + { DA7213_DAC_NG_CTRL, 0x00 }, +}; + +static bool da7213_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case DA7213_STATUS1: + case DA7213_PLL_STATUS: + case DA7213_AUX_L_GAIN_STATUS: + case DA7213_AUX_R_GAIN_STATUS: + case DA7213_MIC_1_GAIN_STATUS: + case DA7213_MIC_2_GAIN_STATUS: + case DA7213_MIXIN_L_GAIN_STATUS: + case DA7213_MIXIN_R_GAIN_STATUS: + case DA7213_ADC_L_GAIN_STATUS: + case DA7213_ADC_R_GAIN_STATUS: + case DA7213_DAC_L_GAIN_STATUS: + case DA7213_DAC_R_GAIN_STATUS: + case DA7213_HP_L_GAIN_STATUS: + case DA7213_HP_R_GAIN_STATUS: + case DA7213_LINE_GAIN_STATUS: + case DA7213_ALC_CTRL1: + case DA7213_ALC_OFFSET_AUTO_M_L: + case DA7213_ALC_OFFSET_AUTO_U_L: + case DA7213_ALC_OFFSET_AUTO_M_R: + case DA7213_ALC_OFFSET_AUTO_U_R: + case DA7213_ALC_CIC_OP_LVL_DATA: + return 1; + default: + return 0; + } +} + +static int da7213_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + u8 dai_ctrl = 0; + u8 fs; + + /* Set DAI format */ + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + dai_ctrl |= DA7213_DAI_WORD_LENGTH_S16_LE; + break; + case SNDRV_PCM_FORMAT_S20_3LE: + dai_ctrl |= DA7213_DAI_WORD_LENGTH_S20_LE; + break; + case SNDRV_PCM_FORMAT_S24_LE: + dai_ctrl |= DA7213_DAI_WORD_LENGTH_S24_LE; + break; + case SNDRV_PCM_FORMAT_S32_LE: + dai_ctrl |= DA7213_DAI_WORD_LENGTH_S32_LE; + break; + default: + return -EINVAL; + } + + /* Set sampling rate */ + switch (params_rate(params)) { + case 8000: + fs = DA7213_SR_8000; + break; + case 11025: + fs = DA7213_SR_11025; + break; + case 12000: + fs = DA7213_SR_12000; + break; + case 16000: + fs = DA7213_SR_16000; + break; + case 22050: + fs = DA7213_SR_22050; + break; + case 32000: + fs = DA7213_SR_32000; + break; + case 44100: + fs = DA7213_SR_44100; + break; + case 48000: + fs = DA7213_SR_48000; + break; + case 88200: + fs = DA7213_SR_88200; + break; + case 96000: + fs = DA7213_SR_96000; + break; + default: + return -EINVAL; + } + + snd_soc_update_bits(codec, DA7213_DAI_CTRL, DA7213_DAI_WORD_LENGTH_MASK, + dai_ctrl); + snd_soc_write(codec, DA7213_SR, fs); + + return 0; +} + +static int da7213_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct da7213_priv *da7213 = snd_soc_codec_get_drvdata(codec); + u8 dai_clk_mode = 0, dai_ctrl = 0; + + /* Set master/slave mode */ + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + dai_clk_mode |= DA7213_DAI_CLK_EN_MASTER_MODE; + da7213->master = true; + break; + case SND_SOC_DAIFMT_CBS_CFS: + dai_clk_mode |= DA7213_DAI_CLK_EN_SLAVE_MODE; + da7213->master = false; + break; + default: + return -EINVAL; + } + + /* Set clock normal/inverted */ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_NB_IF: + dai_clk_mode |= DA7213_DAI_WCLK_POL_INV; + break; + case SND_SOC_DAIFMT_IB_NF: + dai_clk_mode |= DA7213_DAI_CLK_POL_INV; + break; + case SND_SOC_DAIFMT_IB_IF: + dai_clk_mode |= DA7213_DAI_WCLK_POL_INV | DA7213_DAI_CLK_POL_INV; + break; + default: + return -EINVAL; + } + + /* Only I2S is supported */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + dai_ctrl |= DA7213_DAI_FORMAT_I2S_MODE; + break; + case SND_SOC_DAIFMT_LEFT_J: + dai_ctrl |= DA7213_DAI_FORMAT_LEFT_J; + break; + case SND_SOC_DAIFMT_RIGHT_J: + dai_ctrl |= DA7213_DAI_FORMAT_RIGHT_J; + break; + default: + return -EINVAL; + } + + /* By default only 32 BCLK per WCLK is supported */ + dai_clk_mode |= DA7213_DAI_BCLKS_PER_WCLK_32; + + snd_soc_write(codec, DA7213_DAI_CLK_MODE, dai_clk_mode); + snd_soc_update_bits(codec, DA7213_DAI_CTRL, DA7213_DAI_FORMAT_MASK, + dai_ctrl); + + return 0; +} + +static int da7213_mute(struct snd_soc_dai *dai, int mute) +{ + struct snd_soc_codec *codec = dai->codec; + + if (mute) { + snd_soc_update_bits(codec, DA7213_DAC_L_CTRL, + DA7213_MUTE_EN, DA7213_MUTE_EN); + snd_soc_update_bits(codec, DA7213_DAC_R_CTRL, + DA7213_MUTE_EN, DA7213_MUTE_EN); + } else { + snd_soc_update_bits(codec, DA7213_DAC_L_CTRL, + DA7213_MUTE_EN, 0); + snd_soc_update_bits(codec, DA7213_DAC_R_CTRL, + DA7213_MUTE_EN, 0); + } + + return 0; +} + +#define DA7213_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) + +static int da7213_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct da7213_priv *da7213 = snd_soc_codec_get_drvdata(codec); + + switch (clk_id) { + case DA7213_CLKSRC_MCLK: + if ((freq == 32768) || + ((freq >= 5000000) && (freq <= 54000000))) { + da7213->mclk_rate = freq; + return 0; + } else { + dev_err(codec_dai->dev, "Unsupported MCLK value %d\n", + freq); + return -EINVAL; + } + break; + default: + dev_err(codec_dai->dev, "Unknown clock source %d\n", clk_id); + return -EINVAL; + } +} + +/* Supported PLL input frequencies are 5MHz - 54MHz. */ +static int da7213_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id, + int source, unsigned int fref, unsigned int fout) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct da7213_priv *da7213 = snd_soc_codec_get_drvdata(codec); + + u8 pll_ctrl, indiv_bits, indiv; + u8 pll_frac_top, pll_frac_bot, pll_integer; + u32 freq_ref; + u64 frac_div; + + /* Reset PLL configuration */ + snd_soc_write(codec, DA7213_PLL_CTRL, 0); + + pll_ctrl = 0; + + /* Workout input divider based on MCLK rate */ + if ((da7213->mclk_rate == 32768) && (source == DA7213_SYSCLK_PLL)) { + /* 32KHz PLL Mode */ + indiv_bits = DA7213_PLL_INDIV_10_20_MHZ; + indiv = DA7213_PLL_INDIV_10_20_MHZ_VAL; + freq_ref = 3750000; + pll_ctrl |= DA7213_PLL_32K_MODE; + } else { + /* 5 - 54MHz MCLK */ + if (da7213->mclk_rate < 5000000) { + goto pll_err; + } else if (da7213->mclk_rate <= 10000000) { + indiv_bits = DA7213_PLL_INDIV_5_10_MHZ; + indiv = DA7213_PLL_INDIV_5_10_MHZ_VAL; + } else if (da7213->mclk_rate <= 20000000) { + indiv_bits = DA7213_PLL_INDIV_10_20_MHZ; + indiv = DA7213_PLL_INDIV_10_20_MHZ_VAL; + } else if (da7213->mclk_rate <= 40000000) { + indiv_bits = DA7213_PLL_INDIV_20_40_MHZ; + indiv = DA7213_PLL_INDIV_20_40_MHZ_VAL; + } else if (da7213->mclk_rate <= 54000000) { + indiv_bits = DA7213_PLL_INDIV_40_54_MHZ; + indiv = DA7213_PLL_INDIV_40_54_MHZ_VAL; + } else { + goto pll_err; + } + freq_ref = (da7213->mclk_rate / indiv); + } + + pll_ctrl |= indiv_bits; + + /* PLL Bypass mode */ + if (source == DA7213_SYSCLK_MCLK) { + snd_soc_write(codec, DA7213_PLL_CTRL, pll_ctrl); + return 0; + } + + /* + * If Codec is slave and SRM enabled, + * freq_out is (98304000 + 90316800)/2 = 94310400 + */ + if (!da7213->master && da7213->srm_en) { + fout = DA7213_PLL_FREQ_OUT_94310400; + pll_ctrl |= DA7213_PLL_SRM_EN; + } + + /* Enable MCLK squarer if required */ + if (da7213->mclk_squarer_en) + pll_ctrl |= DA7213_PLL_MCLK_SQR_EN; + + /* Calculate dividers for PLL */ + pll_integer = fout / freq_ref; + frac_div = (u64)(fout % freq_ref) * 8192ULL; + do_div(frac_div, freq_ref); + pll_frac_top = (frac_div >> DA7213_BYTE_SHIFT) & DA7213_BYTE_MASK; + pll_frac_bot = (frac_div) & DA7213_BYTE_MASK; + + /* Write PLL dividers */ + snd_soc_write(codec, DA7213_PLL_FRAC_TOP, pll_frac_top); + snd_soc_write(codec, DA7213_PLL_FRAC_BOT, pll_frac_bot); + snd_soc_write(codec, DA7213_PLL_INTEGER, pll_integer); + + /* Enable PLL */ + pll_ctrl |= DA7213_PLL_EN; + snd_soc_write(codec, DA7213_PLL_CTRL, pll_ctrl); + + return 0; + +pll_err: + dev_err(codec_dai->dev, "Unsupported PLL input frequency %d\n", + da7213->mclk_rate); + return -EINVAL; +} + +/* DAI operations */ +static const struct snd_soc_dai_ops da7213_dai_ops = { + .hw_params = da7213_hw_params, + .set_fmt = da7213_set_dai_fmt, + .set_sysclk = da7213_set_dai_sysclk, + .set_pll = da7213_set_dai_pll, + .digital_mute = da7213_mute, +}; + +static struct snd_soc_dai_driver da7213_dai = { + .name = "da7213-hifi", + /* Playback Capabilities */ + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = DA7213_FORMATS, + }, + /* Capture Capabilities */ + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = DA7213_FORMATS, + }, + .ops = &da7213_dai_ops, + .symmetric_rates = 1, +}; + +static int da7213_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) +{ + switch (level) { + case SND_SOC_BIAS_ON: + case SND_SOC_BIAS_PREPARE: + break; + case SND_SOC_BIAS_STANDBY: + if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + /* Enable VMID reference & master bias */ + snd_soc_update_bits(codec, DA7213_REFERENCES, + DA7213_VMID_EN | DA7213_BIAS_EN, + DA7213_VMID_EN | DA7213_BIAS_EN); + } + break; + case SND_SOC_BIAS_OFF: + /* Disable VMID reference & master bias */ + snd_soc_update_bits(codec, DA7213_REFERENCES, + DA7213_VMID_EN | DA7213_BIAS_EN, 0); + break; + } + codec->dapm.bias_level = level; + return 0; +} + +static int da7213_probe(struct snd_soc_codec *codec) +{ + int ret; + struct da7213_priv *da7213 = snd_soc_codec_get_drvdata(codec); + struct da7213_platform_data *pdata = da7213->pdata; + + codec->control_data = da7213->regmap; + ret = snd_soc_codec_set_cache_io(codec, 8, 8, SND_SOC_REGMAP); + if (ret < 0) { + dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret); + return ret; + } + + /* Default to using ALC auto offset calibration mode. */ + snd_soc_update_bits(codec, DA7213_ALC_CTRL1, + DA7213_ALC_CALIB_MODE_MAN, 0); + da7213->alc_calib_auto = true; + + /* Default to using SRM for slave mode */ + da7213->srm_en = true; + + /* Enable all Gain Ramps */ + snd_soc_update_bits(codec, DA7213_AUX_L_CTRL, + DA7213_GAIN_RAMP_EN, DA7213_GAIN_RAMP_EN); + snd_soc_update_bits(codec, DA7213_AUX_R_CTRL, + DA7213_GAIN_RAMP_EN, DA7213_GAIN_RAMP_EN); + snd_soc_update_bits(codec, DA7213_MIXIN_L_CTRL, + DA7213_GAIN_RAMP_EN, DA7213_GAIN_RAMP_EN); + snd_soc_update_bits(codec, DA7213_MIXIN_R_CTRL, + DA7213_GAIN_RAMP_EN, DA7213_GAIN_RAMP_EN); + snd_soc_update_bits(codec, DA7213_ADC_L_CTRL, + DA7213_GAIN_RAMP_EN, DA7213_GAIN_RAMP_EN); + snd_soc_update_bits(codec, DA7213_ADC_R_CTRL, + DA7213_GAIN_RAMP_EN, DA7213_GAIN_RAMP_EN); + snd_soc_update_bits(codec, DA7213_DAC_L_CTRL, + DA7213_GAIN_RAMP_EN, DA7213_GAIN_RAMP_EN); + snd_soc_update_bits(codec, DA7213_DAC_R_CTRL, + DA7213_GAIN_RAMP_EN, DA7213_GAIN_RAMP_EN); + snd_soc_update_bits(codec, DA7213_HP_L_CTRL, + DA7213_GAIN_RAMP_EN, DA7213_GAIN_RAMP_EN); + snd_soc_update_bits(codec, DA7213_HP_R_CTRL, + DA7213_GAIN_RAMP_EN, DA7213_GAIN_RAMP_EN); + snd_soc_update_bits(codec, DA7213_LINE_CTRL, + DA7213_GAIN_RAMP_EN, DA7213_GAIN_RAMP_EN); + + /* + * There are two separate control bits for input and output mixers as + * well as headphone and line outs. + * One to enable corresponding amplifier and other to enable its + * output. As amplifier bits are related to power control, they are + * being managed by DAPM while other (non power related) bits are + * enabled here + */ + snd_soc_update_bits(codec, DA7213_MIXIN_L_CTRL, + DA7213_MIXIN_MIX_EN, DA7213_MIXIN_MIX_EN); + snd_soc_update_bits(codec, DA7213_MIXIN_R_CTRL, + DA7213_MIXIN_MIX_EN, DA7213_MIXIN_MIX_EN); + + snd_soc_update_bits(codec, DA7213_MIXOUT_L_CTRL, + DA7213_MIXOUT_MIX_EN, DA7213_MIXOUT_MIX_EN); + snd_soc_update_bits(codec, DA7213_MIXOUT_R_CTRL, + DA7213_MIXOUT_MIX_EN, DA7213_MIXOUT_MIX_EN); + + snd_soc_update_bits(codec, DA7213_HP_L_CTRL, + DA7213_HP_AMP_OE, DA7213_HP_AMP_OE); + snd_soc_update_bits(codec, DA7213_HP_R_CTRL, + DA7213_HP_AMP_OE, DA7213_HP_AMP_OE); + + snd_soc_update_bits(codec, DA7213_LINE_CTRL, + DA7213_LINE_AMP_OE, DA7213_LINE_AMP_OE); + + /* Set platform data values */ + if (da7213->pdata) { + u8 micbias_lvl = 0, dmic_cfg = 0; + + /* Set Mic Bias voltages */ + switch (pdata->micbias1_lvl) { + case DA7213_MICBIAS_1_6V: + case DA7213_MICBIAS_2_2V: + case DA7213_MICBIAS_2_5V: + case DA7213_MICBIAS_3_0V: + micbias_lvl |= (pdata->micbias1_lvl << + DA7213_MICBIAS1_LEVEL_SHIFT); + break; + } + switch (pdata->micbias2_lvl) { + case DA7213_MICBIAS_1_6V: + case DA7213_MICBIAS_2_2V: + case DA7213_MICBIAS_2_5V: + case DA7213_MICBIAS_3_0V: + micbias_lvl |= (pdata->micbias2_lvl << + DA7213_MICBIAS2_LEVEL_SHIFT); + break; + } + snd_soc_update_bits(codec, DA7213_MICBIAS_CTRL, + DA7213_MICBIAS1_LEVEL_MASK | + DA7213_MICBIAS2_LEVEL_MASK, micbias_lvl); + + /* Set DMIC configuration */ + switch (pdata->dmic_data_sel) { + case DA7213_DMIC_DATA_LFALL_RRISE: + case DA7213_DMIC_DATA_LRISE_RFALL: + dmic_cfg |= (pdata->dmic_data_sel << + DA7213_DMIC_DATA_SEL_SHIFT); + break; + } + switch (pdata->dmic_data_sel) { + case DA7213_DMIC_SAMPLE_ON_CLKEDGE: + case DA7213_DMIC_SAMPLE_BETWEEN_CLKEDGE: + dmic_cfg |= (pdata->dmic_data_sel << + DA7213_DMIC_SAMPLEPHASE_SHIFT); + break; + } + switch (pdata->dmic_data_sel) { + case DA7213_DMIC_CLK_3_0MHZ: + case DA7213_DMIC_CLK_1_5MHZ: + dmic_cfg |= (pdata->dmic_data_sel << + DA7213_DMIC_CLK_RATE_SHIFT); + break; + } + snd_soc_update_bits(codec, DA7213_MIC_CONFIG, + DA7213_DMIC_DATA_SEL_MASK | + DA7213_DMIC_SAMPLEPHASE_MASK | + DA7213_DMIC_CLK_RATE_MASK, dmic_cfg); + + /* Set MCLK squaring */ + da7213->mclk_squarer_en = pdata->mclk_squaring; + } + return 0; +} + +static struct snd_soc_codec_driver soc_codec_dev_da7213 = { + .probe = da7213_probe, + .set_bias_level = da7213_set_bias_level, + + .controls = da7213_snd_controls, + .num_controls = ARRAY_SIZE(da7213_snd_controls), + + .dapm_widgets = da7213_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(da7213_dapm_widgets), + .dapm_routes = da7213_audio_map, + .num_dapm_routes = ARRAY_SIZE(da7213_audio_map), +}; + +static const struct regmap_config da7213_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + + .reg_defaults = da7213_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(da7213_reg_defaults), + .volatile_reg = da7213_volatile_register, + .cache_type = REGCACHE_RBTREE, +}; + +static int da7213_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct da7213_priv *da7213; + struct da7213_platform_data *pdata = dev_get_platdata(&i2c->dev); + int ret; + + da7213 = devm_kzalloc(&i2c->dev, sizeof(struct da7213_priv), + GFP_KERNEL); + if (!da7213) + return -ENOMEM; + + if (pdata) + da7213->pdata = pdata; + + i2c_set_clientdata(i2c, da7213); + + da7213->regmap = devm_regmap_init_i2c(i2c, &da7213_regmap_config); + if (IS_ERR(da7213->regmap)) { + ret = PTR_ERR(da7213->regmap); + dev_err(&i2c->dev, "regmap_init() failed: %d\n", ret); + return ret; + } + + ret = snd_soc_register_codec(&i2c->dev, + &soc_codec_dev_da7213, &da7213_dai, 1); + if (ret < 0) { + dev_err(&i2c->dev, "Failed to register da7213 codec: %d\n", + ret); + } + return ret; +} + +static int da7213_remove(struct i2c_client *client) +{ + snd_soc_unregister_codec(&client->dev); + return 0; +} + +static const struct i2c_device_id da7213_i2c_id[] = { + { "da7213", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, da7213_i2c_id); + +/* I2C codec control layer */ +static struct i2c_driver da7213_i2c_driver = { + .driver = { + .name = "da7213", + .owner = THIS_MODULE, + }, + .probe = da7213_i2c_probe, + .remove = da7213_remove, + .id_table = da7213_i2c_id, +}; + +module_i2c_driver(da7213_i2c_driver); + +MODULE_DESCRIPTION("ASoC DA7213 Codec driver"); +MODULE_AUTHOR("Adam Thomson "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/da7213.h b/sound/soc/codecs/da7213.h new file mode 100644 index 0000000..9cb9ddd --- /dev/null +++ b/sound/soc/codecs/da7213.h @@ -0,0 +1,523 @@ +/* + * da7213.h - DA7213 ASoC Codec Driver + * + * Copyright (c) 2013 Dialog Semiconductor + * + * Author: Adam Thomson + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _DA7213_H +#define _DA7213_H + +#include +#include + +/* + * Registers + */ + +/* Status Registers */ +#define DA7213_STATUS1 0x02 +#define DA7213_PLL_STATUS 0x03 +#define DA7213_AUX_L_GAIN_STATUS 0x04 +#define DA7213_AUX_R_GAIN_STATUS 0x05 +#define DA7213_MIC_1_GAIN_STATUS 0x06 +#define DA7213_MIC_2_GAIN_STATUS 0x07 +#define DA7213_MIXIN_L_GAIN_STATUS 0x08 +#define DA7213_MIXIN_R_GAIN_STATUS 0x09 +#define DA7213_ADC_L_GAIN_STATUS 0x0A +#define DA7213_ADC_R_GAIN_STATUS 0x0B +#define DA7213_DAC_L_GAIN_STATUS 0x0C +#define DA7213_DAC_R_GAIN_STATUS 0x0D +#define DA7213_HP_L_GAIN_STATUS 0x0E +#define DA7213_HP_R_GAIN_STATUS 0x0F +#define DA7213_LINE_GAIN_STATUS 0x10 + +/* System Initialisation Registers */ +#define DA7213_DIG_ROUTING_DAI 0x21 +#define DA7213_SR 0x22 +#define DA7213_REFERENCES 0x23 +#define DA7213_PLL_FRAC_TOP 0x24 +#define DA7213_PLL_FRAC_BOT 0x25 +#define DA7213_PLL_INTEGER 0x26 +#define DA7213_PLL_CTRL 0x27 +#define DA7213_DAI_CLK_MODE 0x28 +#define DA7213_DAI_CTRL 0x29 +#define DA7213_DIG_ROUTING_DAC 0x2A +#define DA7213_ALC_CTRL1 0x2B + +/* Input - Gain, Select and Filter Registers */ +#define DA7213_AUX_L_GAIN 0x30 +#define DA7213_AUX_R_GAIN 0x31 +#define DA7213_MIXIN_L_SELECT 0x32 +#define DA7213_MIXIN_R_SELECT 0x33 +#define DA7213_MIXIN_L_GAIN 0x34 +#define DA7213_MIXIN_R_GAIN 0x35 +#define DA7213_ADC_L_GAIN 0x36 +#define DA7213_ADC_R_GAIN 0x37 +#define DA7213_ADC_FILTERS1 0x38 +#define DA7213_MIC_1_GAIN 0x39 +#define DA7213_MIC_2_GAIN 0x3A + +/* Output - Gain, Select and Filter Registers */ +#define DA7213_DAC_FILTERS5 0x40 +#define DA7213_DAC_FILTERS2 0x41 +#define DA7213_DAC_FILTERS3 0x42 +#define DA7213_DAC_FILTERS4 0x43 +#define DA7213_DAC_FILTERS1 0x44 +#define DA7213_DAC_L_GAIN 0x45 +#define DA7213_DAC_R_GAIN 0x46 +#define DA7213_CP_CTRL 0x47 +#define DA7213_HP_L_GAIN 0x48 +#define DA7213_HP_R_GAIN 0x49 +#define DA7213_LINE_GAIN 0x4A +#define DA7213_MIXOUT_L_SELECT 0x4B +#define DA7213_MIXOUT_R_SELECT 0x4C + +/* System Controller Registers */ +#define DA7213_SYSTEM_MODES_INPUT 0x50 +#define DA7213_SYSTEM_MODES_OUTPUT 0x51 + +/* Control Registers */ +#define DA7213_AUX_L_CTRL 0x60 +#define DA7213_AUX_R_CTRL 0x61 +#define DA7213_MICBIAS_CTRL 0x62 +#define DA7213_MIC_1_CTRL 0x63 +#define DA7213_MIC_2_CTRL 0x64 +#define DA7213_MIXIN_L_CTRL 0x65 +#define DA7213_MIXIN_R_CTRL 0x66 +#define DA7213_ADC_L_CTRL 0x67 +#define DA7213_ADC_R_CTRL 0x68 +#define DA7213_DAC_L_CTRL 0x69 +#define DA7213_DAC_R_CTRL 0x6A +#define DA7213_HP_L_CTRL 0x6B +#define DA7213_HP_R_CTRL 0x6C +#define DA7213_LINE_CTRL 0x6D +#define DA7213_MIXOUT_L_CTRL 0x6E +#define DA7213_MIXOUT_R_CTRL 0x6F + +/* Configuration Registers */ +#define DA7213_LDO_CTRL 0x90 +#define DA7213_IO_CTRL 0x91 +#define DA7213_GAIN_RAMP_CTRL 0x92 +#define DA7213_MIC_CONFIG 0x93 +#define DA7213_PC_COUNT 0x94 +#define DA7213_CP_VOL_THRESHOLD1 0x95 +#define DA7213_CP_DELAY 0x96 +#define DA7213_CP_DETECTOR 0x97 +#define DA7213_DAI_OFFSET 0x98 +#define DA7213_DIG_CTRL 0x99 +#define DA7213_ALC_CTRL2 0x9A +#define DA7213_ALC_CTRL3 0x9B +#define DA7213_ALC_NOISE 0x9C +#define DA7213_ALC_TARGET_MIN 0x9D +#define DA7213_ALC_TARGET_MAX 0x9E +#define DA7213_ALC_GAIN_LIMITS 0x9F +#define DA7213_ALC_ANA_GAIN_LIMITS 0xA0 +#define DA7213_ALC_ANTICLIP_CTRL 0xA1 +#define DA7213_ALC_ANTICLIP_LEVEL 0xA2 + +#define DA7213_ALC_OFFSET_AUTO_M_L 0xA3 +#define DA7213_ALC_OFFSET_AUTO_U_L 0xA4 +#define DA7213_ALC_OFFSET_MAN_M_L 0xA6 +#define DA7213_ALC_OFFSET_MAN_U_L 0xA7 +#define DA7213_ALC_OFFSET_AUTO_M_R 0xA8 +#define DA7213_ALC_OFFSET_AUTO_U_R 0xA9 +#define DA7213_ALC_OFFSET_MAN_M_R 0xAB +#define DA7213_ALC_OFFSET_MAN_U_R 0xAC +#define DA7213_ALC_CIC_OP_LVL_CTRL 0xAD +#define DA7213_ALC_CIC_OP_LVL_DATA 0xAE +#define DA7213_DAC_NG_SETUP_TIME 0xAF +#define DA7213_DAC_NG_OFF_THRESHOLD 0xB0 +#define DA7213_DAC_NG_ON_THRESHOLD 0xB1 +#define DA7213_DAC_NG_CTRL 0xB2 + + +/* + * Bit fields + */ + +/* DA7213_SR = 0x22 */ +#define DA7213_SR_8000 (0x1 << 0) +#define DA7213_SR_11025 (0x2 << 0) +#define DA7213_SR_12000 (0x3 << 0) +#define DA7213_SR_16000 (0x5 << 0) +#define DA7213_SR_22050 (0x6 << 0) +#define DA7213_SR_24000 (0x7 << 0) +#define DA7213_SR_32000 (0x9 << 0) +#define DA7213_SR_44100 (0xA << 0) +#define DA7213_SR_48000 (0xB << 0) +#define DA7213_SR_88200 (0xE << 0) +#define DA7213_SR_96000 (0xF << 0) + +/* DA7213_REFERENCES = 0x23 */ +#define DA7213_BIAS_EN (0x1 << 3) +#define DA7213_VMID_EN (0x1 << 7) + +/* DA7213_PLL_CTRL = 0x27 */ +#define DA7213_PLL_INDIV_5_10_MHZ (0x0 << 2) +#define DA7213_PLL_INDIV_10_20_MHZ (0x1 << 2) +#define DA7213_PLL_INDIV_20_40_MHZ (0x2 << 2) +#define DA7213_PLL_INDIV_40_54_MHZ (0x3 << 2) +#define DA7213_PLL_INDIV_MASK (0x3 << 2) +#define DA7213_PLL_MCLK_SQR_EN (0x1 << 4) +#define DA7213_PLL_32K_MODE (0x1 << 5) +#define DA7213_PLL_SRM_EN (0x1 << 6) +#define DA7213_PLL_EN (0x1 << 7) + +/* DA7213_DAI_CLK_MODE = 0x28 */ +#define DA7213_DAI_BCLKS_PER_WCLK_32 (0x0 << 0) +#define DA7213_DAI_BCLKS_PER_WCLK_64 (0x1 << 0) +#define DA7213_DAI_BCLKS_PER_WCLK_128 (0x2 << 0) +#define DA7213_DAI_BCLKS_PER_WCLK_256 (0x3 << 0) +#define DA7213_DAI_BCLKS_PER_WCLK_MASK (0x3 << 0) +#define DA7213_DAI_CLK_POL_INV (0x1 << 2) +#define DA7213_DAI_WCLK_POL_INV (0x1 << 3) +#define DA7213_DAI_CLK_EN_SLAVE_MODE (0x0 << 7) +#define DA7213_DAI_CLK_EN_MASTER_MODE (0x1 << 7) +#define DA7213_DAI_CLK_EN_MASK (0x1 << 7) + +/* DA7213_DAI_CTRL = 0x29 */ +#define DA7213_DAI_FORMAT_I2S_MODE (0x0 << 0) +#define DA7213_DAI_FORMAT_LEFT_J (0x1 << 0) +#define DA7213_DAI_FORMAT_RIGHT_J (0x2 << 0) +#define DA7213_DAI_FORMAT_MASK (0x3 << 0) +#define DA7213_DAI_WORD_LENGTH_S16_LE (0x0 << 2) +#define DA7213_DAI_WORD_LENGTH_S20_LE (0x1 << 2) +#define DA7213_DAI_WORD_LENGTH_S24_LE (0x2 << 2) +#define DA7213_DAI_WORD_LENGTH_S32_LE (0x3 << 2) +#define DA7213_DAI_WORD_LENGTH_MASK (0x3 << 2) +#define DA7213_DAI_EN_SHIFT 7 + +/* DA7213_DIG_ROUTING_DAI = 0x21 */ +#define DA7213_DAI_L_SRC_SHIFT 0 +#define DA7213_DAI_R_SRC_SHIFT 4 +#define DA7213_DAI_SRC_MAX 4 + +/* DA7213_DIG_ROUTING_DAC = 0x2A */ +#define DA7213_DAC_L_SRC_SHIFT 0 +#define DA7213_DAC_L_MONO_SHIFT 3 +#define DA7213_DAC_R_SRC_SHIFT 4 +#define DA7213_DAC_R_MONO_SHIFT 7 +#define DA7213_DAC_SRC_MAX 4 +#define DA7213_DAC_MONO_MAX 0x1 + +/* DA7213_ALC_CTRL1 = 0x2B */ +#define DA7213_ALC_OFFSET_EN_SHIFT 0 +#define DA7213_ALC_OFFSET_EN_MAX 0x1 +#define DA7213_ALC_OFFSET_EN (0x1 << 0) +#define DA7213_ALC_SYNC_MODE (0x1 << 1) +#define DA7213_ALC_CALIB_MODE_MAN (0x1 << 2) +#define DA7213_ALC_L_EN_SHIFT 3 +#define DA7213_ALC_AUTO_CALIB_EN (0x1 << 4) +#define DA7213_ALC_CALIB_OVERFLOW (0x1 << 5) +#define DA7213_ALC_R_EN_SHIFT 7 +#define DA7213_ALC_EN_MAX 0x1 + +/* DA7213_AUX_L/R_GAIN = 0x30/0x31 */ +#define DA7213_AUX_AMP_GAIN_SHIFT 0 +#define DA7213_AUX_AMP_GAIN_MAX 0x3F + +/* DA7213_MIXIN_L/R_SELECT = 0x32/0x33 */ +#define DA7213_DMIC_EN_SHIFT 7 +#define DA7213_DMIC_EN_MAX 0x1 + +/* DA7213_MIXIN_L_SELECT = 0x32 */ +#define DA7213_MIXIN_L_MIX_SELECT_AUX_L_SHIFT 0 +#define DA7213_MIXIN_L_MIX_SELECT_MIC_1_SHIFT 1 +#define DA7213_MIXIN_L_MIX_SELECT_MIC_1 (0x1 << 1) +#define DA7213_MIXIN_L_MIX_SELECT_MIC_2_SHIFT 2 +#define DA7213_MIXIN_L_MIX_SELECT_MIC_2 (0x1 << 2) +#define DA7213_MIXIN_L_MIX_SELECT_MIXIN_R_SHIFT 3 +#define DA7213_MIXIN_L_MIX_SELECT_MAX 0x1 + +/* DA7213_MIXIN_R_SELECT = 0x33 */ +#define DA7213_MIXIN_R_MIX_SELECT_AUX_R_SHIFT 0 +#define DA7213_MIXIN_R_MIX_SELECT_MIC_2_SHIFT 1 +#define DA7213_MIXIN_R_MIX_SELECT_MIC_2 (0x1 << 1) +#define DA7213_MIXIN_R_MIX_SELECT_MIC_1_SHIFT 2 +#define DA7213_MIXIN_R_MIX_SELECT_MIC_1 (0x1 << 2) +#define DA7213_MIXIN_R_MIX_SELECT_MIXIN_L_SHIFT 3 +#define DA7213_MIXIN_R_MIX_SELECT_MAX 0x1 +#define DA7213_MIC_BIAS_OUTPUT_SELECT_2 (0x1 << 6) + +/* DA7213_MIXIN_L/R_GAIN = 0x34/0x35 */ +#define DA7213_MIXIN_AMP_GAIN_SHIFT 0 +#define DA7213_MIXIN_AMP_GAIN_MAX 0xF + +/* DA7213_ADC_L/R_GAIN = 0x36/0x37 */ +#define DA7213_ADC_AMP_GAIN_SHIFT 0 +#define DA7213_ADC_AMP_GAIN_MAX 0x7F + +/* DA7213_ADC/DAC_FILTERS1 = 0x38/0x44 */ +#define DA7213_VOICE_HPF_CORNER_SHIFT 0 +#define DA7213_VOICE_HPF_CORNER_MAX 8 +#define DA7213_VOICE_EN_SHIFT 3 +#define DA7213_VOICE_EN_MAX 0x1 +#define DA7213_AUDIO_HPF_CORNER_SHIFT 4 +#define DA7213_AUDIO_HPF_CORNER_MAX 4 +#define DA7213_HPF_EN_SHIFT 7 +#define DA7213_HPF_EN_MAX 0x1 + +/* DA7213_MIC_1/2_GAIN = 0x39/0x3A */ +#define DA7213_MIC_AMP_GAIN_SHIFT 0 +#define DA7213_MIC_AMP_GAIN_MAX 0x7 + +/* DA7213_DAC_FILTERS5 = 0x40 */ +#define DA7213_DAC_SOFTMUTE_EN_SHIFT 7 +#define DA7213_DAC_SOFTMUTE_EN_MAX 0x1 +#define DA7213_DAC_SOFTMUTE_RATE_SHIFT 4 +#define DA7213_DAC_SOFTMUTE_RATE_MAX 7 + +/* DA7213_DAC_FILTERS2/3/4 = 0x41/0x42/0x43 */ +#define DA7213_DAC_EQ_BAND_MAX 0xF + +/* DA7213_DAC_FILTERS2 = 0x41 */ +#define DA7213_DAC_EQ_BAND1_SHIFT 0 +#define DA7213_DAC_EQ_BAND2_SHIFT 4 + +/* DA7213_DAC_FILTERS2 = 0x42 */ +#define DA7213_DAC_EQ_BAND3_SHIFT 0 +#define DA7213_DAC_EQ_BAND4_SHIFT 4 + +/* DA7213_DAC_FILTERS4 = 0x43 */ +#define DA7213_DAC_EQ_BAND5_SHIFT 0 +#define DA7213_DAC_EQ_EN_SHIFT 7 +#define DA7213_DAC_EQ_EN_MAX 0x1 + +/* DA7213_DAC_L/R_GAIN = 0x45/0x46 */ +#define DA7213_DAC_AMP_GAIN_SHIFT 0 +#define DA7213_DAC_AMP_GAIN_MAX 0x7F + +/* DA7213_HP_L/R_GAIN = 0x45/0x46 */ +#define DA7213_HP_AMP_GAIN_SHIFT 0 +#define DA7213_HP_AMP_GAIN_MAX 0x3F + +/* DA7213_CP_CTRL = 0x47 */ +#define DA7213_CP_EN_SHIFT 7 + +/* DA7213_LINE_GAIN = 0x4A */ +#define DA7213_LINE_AMP_GAIN_SHIFT 0 +#define DA7213_LINE_AMP_GAIN_MAX 0x3F + +/* DA7213_MIXOUT_L_SELECT = 0x4B */ +#define DA7213_MIXOUT_L_MIX_SELECT_AUX_L_SHIFT 0 +#define DA7213_MIXOUT_L_MIX_SELECT_MIXIN_L_SHIFT 1 +#define DA7213_MIXOUT_L_MIX_SELECT_MIXIN_R_SHIFT 2 +#define DA7213_MIXOUT_L_MIX_SELECT_DAC_L_SHIFT 3 +#define DA7213_MIXOUT_L_MIX_SELECT_AUX_L_INVERTED_SHIFT 4 +#define DA7213_MIXOUT_L_MIX_SELECT_MIXIN_L_INVERTED_SHIFT 5 +#define DA7213_MIXOUT_L_MIX_SELECT_MIXIN_R_INVERTED_SHIFT 6 +#define DA7213_MIXOUT_L_MIX_SELECT_MAX 0x1 + +/* DA7213_MIXOUT_R_SELECT = 0x4C */ +#define DA7213_MIXOUT_R_MIX_SELECT_AUX_R_SHIFT 0 +#define DA7213_MIXOUT_R_MIX_SELECT_MIXIN_R_SHIFT 1 +#define DA7213_MIXOUT_R_MIX_SELECT_MIXIN_L_SHIFT 2 +#define DA7213_MIXOUT_R_MIX_SELECT_DAC_R_SHIFT 3 +#define DA7213_MIXOUT_R_MIX_SELECT_AUX_R_INVERTED_SHIFT 4 +#define DA7213_MIXOUT_R_MIX_SELECT_MIXIN_R_INVERTED_SHIFT 5 +#define DA7213_MIXOUT_R_MIX_SELECT_MIXIN_L_INVERTED_SHIFT 6 +#define DA7213_MIXOUT_R_MIX_SELECT_MAX 0x1 + +/* + * DA7213_AUX_L/R_CTRL = 0x60/0x61, + * DA7213_MIC_1/2_CTRL = 0x63/0x64, + * DA7213_MIXIN_L/R_CTRL = 0x65/0x66, + * DA7213_ADC_L/R_CTRL = 0x65/0x66, + * DA7213_DAC_L/R_CTRL = 0x69/0x6A, + * DA7213_HP_L/R_CTRL = 0x6B/0x6C, + * DA7213_LINE_CTRL = 0x6D + */ +#define DA7213_MUTE_EN_SHIFT 6 +#define DA7213_MUTE_EN_MAX 0x1 +#define DA7213_MUTE_EN (0x1 << 6) + +/* + * DA7213_AUX_L/R_CTRL = 0x60/0x61, + * DA7213_MIXIN_L/R_CTRL = 0x65/0x66, + * DA7213_ADC_L/R_CTRL = 0x65/0x66, + * DA7213_DAC_L/R_CTRL = 0x69/0x6A, + * DA7213_HP_L/R_CTRL = 0x6B/0x6C, + * DA7213_LINE_CTRL = 0x6D + */ +#define DA7213_GAIN_RAMP_EN_SHIFT 5 +#define DA7213_GAIN_RAMP_EN_MAX 0x1 +#define DA7213_GAIN_RAMP_EN (0x1 << 5) + +/* + * DA7213_AUX_L/R_CTRL = 0x60/0x61, + * DA7213_MIXIN_L/R_CTRL = 0x65/0x66, + * DA7213_HP_L/R_CTRL = 0x6B/0x6C, + * DA7213_LINE_CTRL = 0x6D + */ +#define DA7213_ZC_EN_SHIFT 4 +#define DA7213_ZC_EN_MAX 0x1 + +/* + * DA7213_AUX_L/R_CTRL = 0x60/0x61, + * DA7213_MIC_1/2_CTRL = 0x63/0x64, + * DA7213_MIXIN_L/R_CTRL = 0x65/0x66, + * DA7213_HP_L/R_CTRL = 0x6B/0x6C, + * DA7213_MIXOUT_L/R_CTRL = 0x6E/0x6F, + * DA7213_LINE_CTRL = 0x6D + */ +#define DA7213_AMP_EN_SHIFT 7 + +/* DA7213_MIC_1/2_CTRL = 0x63/0x64 */ +#define DA7213_MIC_AMP_IN_SEL_SHIFT 2 +#define DA7213_MIC_AMP_IN_SEL_MAX 3 + +/* DA7213_MICBIAS_CTRL = 0x62 */ +#define DA7213_MICBIAS1_LEVEL_SHIFT 0 +#define DA7213_MICBIAS1_LEVEL_MASK (0x3 << 0) +#define DA7213_MICBIAS1_EN_SHIFT 3 +#define DA7213_MICBIAS2_LEVEL_SHIFT 4 +#define DA7213_MICBIAS2_LEVEL_MASK (0x3 << 4) +#define DA7213_MICBIAS2_EN_SHIFT 7 + +/* DA7213_MIXIN_L/R_CTRL = 0x65/0x66 */ +#define DA7213_MIXIN_MIX_EN (0x1 << 3) + +/* DA7213_ADC_L/R_CTRL = 0x67/0x68 */ +#define DA7213_ADC_EN_SHIFT 7 +#define DA7213_ADC_EN (0x1 << 7) + +/* DA7213_DAC_L/R_CTRL = 0x69/0x6A*/ +#define DA7213_DAC_EN_SHIFT 7 + +/* DA7213_HP_L/R_CTRL = 0x6B/0x6C */ +#define DA7213_HP_AMP_OE (0x1 << 3) + +/* DA7213_LINE_CTRL = 0x6D */ +#define DA7213_LINE_AMP_OE (0x1 << 3) + +/* DA7213_MIXOUT_L/R_CTRL = 0x6E/0x6F */ +#define DA7213_MIXOUT_MIX_EN (0x1 << 3) + +/* DA7213_GAIN_RAMP_CTRL = 0x92 */ +#define DA7213_GAIN_RAMP_RATE_SHIFT 0 +#define DA7213_GAIN_RAMP_RATE_MAX 4 + +/* DA7213_MIC_CONFIG = 0x93 */ +#define DA7213_DMIC_DATA_SEL_SHIFT 0 +#define DA7213_DMIC_DATA_SEL_MASK (0x1 << 0) +#define DA7213_DMIC_SAMPLEPHASE_SHIFT 1 +#define DA7213_DMIC_SAMPLEPHASE_MASK (0x1 << 1) +#define DA7213_DMIC_CLK_RATE_SHIFT 2 +#define DA7213_DMIC_CLK_RATE_MASK (0x1 << 2) + +/* DA7213_DIG_CTRL = 0x99 */ +#define DA7213_DAC_L_INV_SHIFT 3 +#define DA7213_DAC_R_INV_SHIFT 7 +#define DA7213_DAC_INV_MAX 0x1 + +/* DA7213_ALC_CTRL2 = 0x9A */ +#define DA7213_ALC_ATTACK_SHIFT 0 +#define DA7213_ALC_ATTACK_MAX 13 +#define DA7213_ALC_RELEASE_SHIFT 4 +#define DA7213_ALC_RELEASE_MAX 11 + +/* DA7213_ALC_CTRL3 = 0x9B */ +#define DA7213_ALC_HOLD_SHIFT 0 +#define DA7213_ALC_HOLD_MAX 16 +#define DA7213_ALC_INTEG_ATTACK_SHIFT 4 +#define DA7213_ALC_INTEG_RELEASE_SHIFT 6 +#define DA7213_ALC_INTEG_MAX 4 + +/* + * DA7213_ALC_NOISE = 0x9C, + * DA7213_ALC_TARGET_MIN/MAX = 0x9D/0x9E + */ +#define DA7213_ALC_THRESHOLD_SHIFT 0 +#define DA7213_ALC_THRESHOLD_MAX 0x3F + +/* DA7213_ALC_GAIN_LIMITS = 0x9F */ +#define DA7213_ALC_ATTEN_MAX_SHIFT 0 +#define DA7213_ALC_GAIN_MAX_SHIFT 4 +#define DA7213_ALC_ATTEN_GAIN_MAX_MAX 0xF + +/* DA7213_ALC_ANA_GAIN_LIMITS = 0xA0 */ +#define DA7213_ALC_ANA_GAIN_MIN_SHIFT 0 +#define DA7213_ALC_ANA_GAIN_MAX_SHIFT 4 +#define DA7213_ALC_ANA_GAIN_MAX 0x7 + +/* DA7213_ALC_ANTICLIP_CTRL = 0xA1 */ +#define DA7213_ALC_ANTICLIP_EN_SHIFT 7 +#define DA7213_ALC_ANTICLIP_EN_MAX 0x1 + +/* DA7213_ALC_ANTICLIP_LEVEL = 0xA2 */ +#define DA7213_ALC_ANTICLIP_LEVEL_SHIFT 0 +#define DA7213_ALC_ANTICLIP_LEVEL_MAX 0x7F + +/* DA7213_ALC_CIC_OP_LVL_CTRL = 0xAD */ +#define DA7213_ALC_DATA_MIDDLE (0x2 << 0) +#define DA7213_ALC_DATA_TOP (0x3 << 0) +#define DA7213_ALC_CIC_OP_CHANNEL_LEFT (0x0 << 7) +#define DA7213_ALC_CIC_OP_CHANNEL_RIGHT (0x1 << 7) + +/* DA7213_DAC_NG_SETUP_TIME = 0xAF */ +#define DA7213_DAC_NG_SETUP_TIME_SHIFT 0 +#define DA7213_DAC_NG_SETUP_TIME_MAX 4 +#define DA7213_DAC_NG_RAMPUP_RATE_SHIFT 2 +#define DA7213_DAC_NG_RAMPDN_RATE_SHIFT 3 +#define DA7213_DAC_NG_RAMP_RATE_MAX 2 + +/* DA7213_DAC_NG_OFF/ON_THRESH = 0xB0/0xB1 */ +#define DA7213_DAC_NG_THRESHOLD_SHIFT 0 +#define DA7213_DAC_NG_THRESHOLD_MAX 0x7 + +/* DA7213_DAC_NG_CTRL = 0xB2 */ +#define DA7213_DAC_NG_EN_SHIFT 7 +#define DA7213_DAC_NG_EN_MAX 0x1 + + +/* + * General defines + */ + +/* Register inversion */ +#define DA7213_NO_INVERT 0 +#define DA7213_INVERT 1 + +/* Byte related defines */ +#define DA7213_BYTE_SHIFT 8 +#define DA7213_BYTE_MASK 0xFF + +/* ALC related */ +#define DA7213_ALC_OFFSET_15_8 0x00FF00 +#define DA7213_ALC_OFFSET_19_16 0x0F0000 +#define DA7213_ALC_AVG_ITERATIONS 5 + +/* PLL related */ +#define DA7213_SYSCLK_MCLK 0 +#define DA7213_SYSCLK_PLL 1 +#define DA7213_PLL_FREQ_OUT_90316800 90316800 +#define DA7213_PLL_FREQ_OUT_98304000 98304000 +#define DA7213_PLL_FREQ_OUT_94310400 94310400 +#define DA7213_PLL_INDIV_5_10_MHZ_VAL 2 +#define DA7213_PLL_INDIV_10_20_MHZ_VAL 4 +#define DA7213_PLL_INDIV_20_40_MHZ_VAL 8 +#define DA7213_PLL_INDIV_40_54_MHZ_VAL 16 + +enum clk_src { + DA7213_CLKSRC_MCLK +}; + +/* Codec private data */ +struct da7213_priv { + struct regmap *regmap; + unsigned int mclk_rate; + bool master; + bool mclk_squarer_en; + bool srm_en; + bool alc_calib_auto; + bool alc_en; + struct da7213_platform_data *pdata; +}; + +#endif /* _DA7213_H */ -- cgit v0.10.2 From 4909a0caabb8b4352efcea223e58b86f8bc1f98b Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Sun, 17 Feb 2013 14:33:04 +0100 Subject: ALSA: usb/quirks, fix out-of-bounds access bootresponse in snd_usb_mbox2_boot_quirk is only 12 (decimal) u8's long, but i9s passed to snd_usb_ctl_msg as it would be 0x12 (hexa) long. Fix that by having proper size of the array, i.e. 0x12. Signed-off-by: Jiri Slaby Cc: Signed-off-by: Takashi Iwai diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index 7d7ad0b..5325a38 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -533,7 +533,7 @@ static int snd_usb_mbox2_boot_quirk(struct usb_device *dev) { struct usb_host_config *config = dev->actconfig; int err; - u8 bootresponse[12]; + u8 bootresponse[0x12]; int fwsize; int count; -- cgit v0.10.2 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 diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 48c9d10..e575685 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 v0.10.2 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 diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index e575685..61478fd 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 v0.10.2 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 diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index b9af281b..32adaa6 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 v0.10.2 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 diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 32adaa6..6bcdd66 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_eld.c b/sound/pci/hda/hda_eld.c index 4c054f4..16066d7 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 05f1d59..363cd48 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 6bcdd66..1e38191 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 v0.10.2 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 diff --git a/sound/pci/hda/hda_eld.c b/sound/pci/hda/hda_eld.c index 16066d7..7dd8463 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 363cd48..83b7486 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 1e38191..e77735d 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 v0.10.2 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 diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index e77735d..21425fb 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 v0.10.2 From b531f81b0d70ffbe8d70500512483227cc532608 Mon Sep 17 00:00:00 2001 From: Pawel Moll Date: Thu, 21 Feb 2013 01:55:50 +0000 Subject: ALSA: usb: Fix Processing Unit Descriptor parsers Commit 99fc86450c439039d2ef88d06b222fd51a779176 "ALSA: usb-mixer: parse descriptors with structs" introduced a set of useful parsers for descriptors. Unfortunately the parses for the Processing Unit Descriptor came with a very subtle bug... Functions uac_processing_unit_iProcessing() and uac_processing_unit_specific() were indexing the baSourceID array forgetting the fields before the iProcessing and process-specific descriptors. The problem was observed with Sound Blaster Extigy mixer, where nNrModes in Up/Down-mix Processing Unit Descriptor was accessed at offset 10 of the descriptor (value 0) instead of offset 15 (value 7). In result the resulting control had interesting limit values: Simple mixer control 'Channel Routing Mode Select',0 Capabilities: volume volume-joined penum Playback channels: Mono Capture channels: Mono Limits: 0 - -1 Mono: -1 [100%] Fixed by starting from the bmControls, which was calculated correctly, instead of baSourceID. Now the mentioned control is fine: Simple mixer control 'Channel Routing Mode Select',0 Capabilities: volume volume-joined penum Playback channels: Mono Capture channels: Mono Limits: 0 - 6 Mono: 0 [0%] Signed-off-by: Pawel Moll Cc: Signed-off-by: Takashi Iwai diff --git a/include/uapi/linux/usb/audio.h b/include/uapi/linux/usb/audio.h index ac90037..d2314be 100644 --- a/include/uapi/linux/usb/audio.h +++ b/include/uapi/linux/usb/audio.h @@ -384,14 +384,16 @@ static inline __u8 uac_processing_unit_iProcessing(struct uac_processing_unit_de int protocol) { __u8 control_size = uac_processing_unit_bControlSize(desc, protocol); - return desc->baSourceID[desc->bNrInPins + control_size]; + return *(uac_processing_unit_bmControls(desc, protocol) + + control_size); } static inline __u8 *uac_processing_unit_specific(struct uac_processing_unit_descriptor *desc, int protocol) { __u8 control_size = uac_processing_unit_bControlSize(desc, protocol); - return &desc->baSourceID[desc->bNrInPins + control_size + 1]; + return uac_processing_unit_bmControls(desc, protocol) + + control_size + 1; } /* 4.5.2 Class-Specific AS Interface Descriptor */ -- cgit v0.10.2