diff options
Diffstat (limited to 'sound/pci')
135 files changed, 7693 insertions, 2244 deletions
diff --git a/sound/pci/Makefile b/sound/pci/Makefile index 04cac7469139..18b673018dfd 100644 --- a/sound/pci/Makefile +++ b/sound/pci/Makefile @@ -4,30 +4,30 @@ # Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz> # -snd-ad1889-objs := ad1889.o -snd-als300-objs := als300.o -snd-als4000-objs := als4000.o -snd-atiixp-objs := atiixp.o -snd-atiixp-modem-objs := atiixp_modem.o -snd-azt3328-objs := azt3328.o -snd-bt87x-objs := bt87x.o -snd-cmipci-objs := cmipci.o -snd-cs4281-objs := cs4281.o -snd-cs5530-objs := cs5530.o -snd-ens1370-objs := ens1370.o ak4531_codec.o -snd-ens1371-objs := ens1371.o -snd-es1938-objs := es1938.o -snd-es1968-objs := es1968.o -snd-fm801-objs := fm801.o -snd-intel8x0-objs := intel8x0.o -snd-intel8x0m-objs := intel8x0m.o -snd-maestro3-objs := maestro3.o -snd-rme32-objs := rme32.o -snd-rme96-objs := rme96.o -snd-sis7019-objs := sis7019.o -snd-sonicvibes-objs := sonicvibes.o -snd-via82xx-objs := via82xx.o -snd-via82xx-modem-objs := via82xx_modem.o +snd-ad1889-y := ad1889.o +snd-als300-y := als300.o +snd-als4000-y := als4000.o +snd-atiixp-y := atiixp.o +snd-atiixp-modem-y := atiixp_modem.o +snd-azt3328-y := azt3328.o +snd-bt87x-y := bt87x.o +snd-cmipci-y := cmipci.o +snd-cs4281-y := cs4281.o +snd-cs5530-y := cs5530.o +snd-ens1370-y := ens1370.o ak4531_codec.o +snd-ens1371-y := ens1371.o +snd-es1938-y := es1938.o +snd-es1968-y := es1968.o +snd-fm801-y := fm801.o +snd-intel8x0-y := intel8x0.o +snd-intel8x0m-y := intel8x0m.o +snd-maestro3-y := maestro3.o +snd-rme32-y := rme32.o +snd-rme96-y := rme96.o +snd-sis7019-y := sis7019.o +snd-sonicvibes-y := sonicvibes.o +snd-via82xx-y := via82xx.o +snd-via82xx-modem-y := via82xx_modem.o # Toplevel Module Dependency obj-$(CONFIG_SND_AD1889) += snd-ad1889.o diff --git a/sound/pci/ac97/ac97_codec.c b/sound/pci/ac97/ac97_codec.c index 25f93e56cfc7..6e710dce5c60 100644 --- a/sound/pci/ac97/ac97_codec.c +++ b/sound/pci/ac97/ac97_codec.c @@ -1864,7 +1864,7 @@ void snd_ac97_get_name(struct snd_ac97 *ac97, unsigned int id, char *name, int m strcat(name, " "); strcat(name, pid->name); if (pid->mask != 0xffffffff) - sprintf(name + strlen(name), " rev %d", id & ~pid->mask); + sprintf(name + strlen(name), " rev %u", id & ~pid->mask); if (ac97 && pid->patch) { if ((modem && (pid->flags & AC97_MODEM_PATCH)) || (! modem && ! (pid->flags & AC97_MODEM_PATCH))) diff --git a/sound/pci/ac97/ac97_patch.c b/sound/pci/ac97/ac97_patch.c index 1d786bd5ce3e..cd83aa864ea3 100644 --- a/sound/pci/ac97/ac97_patch.c +++ b/sound/pci/ac97/ac97_patch.c @@ -41,12 +41,9 @@ static int patch_build_controls(struct snd_ac97 * ac97, const struct snd_kcontro static void reset_tlv(struct snd_ac97 *ac97, const char *name, const unsigned int *tlv) { - struct snd_ctl_elem_id sid; struct snd_kcontrol *kctl; - memset(&sid, 0, sizeof(sid)); - strcpy(sid.name, name); - sid.iface = SNDRV_CTL_ELEM_IFACE_MIXER; - kctl = snd_ctl_find_id(ac97->bus->card, &sid); + + kctl = snd_ctl_find_id_mixer(ac97->bus->card, name); if (kctl && kctl->tlv.p) kctl->tlv.p = tlv; } diff --git a/sound/pci/ac97/ac97_proc.c b/sound/pci/ac97/ac97_proc.c index 5426f7bc9884..518834964b7b 100644 --- a/sound/pci/ac97/ac97_proc.c +++ b/sound/pci/ac97/ac97_proc.c @@ -161,12 +161,12 @@ static void snd_ac97_proc_read_main(struct snd_ac97 *ac97, struct snd_info_buffe "Mic select : %s\n" "ADC/DAC loopback : %s\n", val & 0x8000 ? "post" : "pre", - val & 0x4000 ? "on" : "off", - val & 0x2000 ? "on" : "off", - val & 0x1000 ? "on" : "off", + str_on_off(val & 0x4000), + str_on_off(val & 0x2000), + str_on_off(val & 0x1000), val & 0x0200 ? "Mic" : "MIX", val & 0x0100 ? "Mic2" : "Mic1", - val & 0x0080 ? "on" : "off"); + str_on_off(val & 0x0080)); if (ac97->ext_id & AC97_EI_DRA) snd_iprintf(buffer, "Double rate slots: %s\n", double_rate_slots[(val >> 10) & 3]); diff --git a/sound/pci/ad1889.c b/sound/pci/ad1889.c index 50e30704bf6f..9ed778b6b03c 100644 --- a/sound/pci/ad1889.c +++ b/sound/pci/ad1889.c @@ -626,7 +626,7 @@ snd_ad1889_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffe reg = ad1889_readw(chip, AD_DS_WSMC); snd_iprintf(buffer, "Wave output: %s\n", - (reg & AD_DS_WSMC_WAEN) ? "enabled" : "disabled"); + str_enabled_disabled(reg & AD_DS_WSMC_WAEN)); snd_iprintf(buffer, "Wave Channels: %s\n", (reg & AD_DS_WSMC_WAST) ? "stereo" : "mono"); snd_iprintf(buffer, "Wave Quality: %d-bit linear\n", @@ -642,7 +642,7 @@ snd_ad1889_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffe snd_iprintf(buffer, "Synthesis output: %s\n", - reg & AD_DS_WSMC_SYEN ? "enabled" : "disabled"); + str_enabled_disabled(reg & AD_DS_WSMC_SYEN)); /* SYRQ is at offset 4 */ tmp = (reg & AD_DS_WSMC_SYRQ) ? @@ -654,7 +654,7 @@ snd_ad1889_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffe reg = ad1889_readw(chip, AD_DS_RAMC); snd_iprintf(buffer, "ADC input: %s\n", - (reg & AD_DS_RAMC_ADEN) ? "enabled" : "disabled"); + str_enabled_disabled(reg & AD_DS_RAMC_ADEN)); snd_iprintf(buffer, "ADC Channels: %s\n", (reg & AD_DS_RAMC_ADST) ? "stereo" : "mono"); snd_iprintf(buffer, "ADC Quality: %d-bit linear\n", @@ -669,7 +669,7 @@ snd_ad1889_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffe (reg & AD_DS_RAMC_ADST) ? "stereo" : "mono"); snd_iprintf(buffer, "Resampler input: %s\n", - reg & AD_DS_RAMC_REEN ? "enabled" : "disabled"); + str_enabled_disabled(reg & AD_DS_RAMC_REEN)); /* RERQ is at offset 12 */ tmp = (reg & AD_DS_RAMC_RERQ) ? diff --git a/sound/pci/ali5451/Makefile b/sound/pci/ali5451/Makefile index 8156198fbaeb..e319a4c1d6b2 100644 --- a/sound/pci/ali5451/Makefile +++ b/sound/pci/ali5451/Makefile @@ -4,7 +4,7 @@ # Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz> # -snd-ali5451-objs := ali5451.o +snd-ali5451-y := ali5451.o # Toplevel Module Dependency obj-$(CONFIG_SND_ALI5451) += snd-ali5451.o diff --git a/sound/pci/ali5451/ali5451.c b/sound/pci/ali5451/ali5451.c index 2378a39abaeb..793d2f13267e 100644 --- a/sound/pci/ali5451/ali5451.c +++ b/sound/pci/ali5451/ali5451.c @@ -243,9 +243,7 @@ struct snd_ali { spinlock_t reg_lock; spinlock_t voice_alloc; -#ifdef CONFIG_PM_SLEEP - struct snd_ali_image *image; -#endif + struct snd_ali_image image; }; static const struct pci_device_id snd_ali_ids[] = { @@ -294,7 +292,7 @@ static int snd_ali_codec_ready(struct snd_ali *codec, } snd_ali_5451_poke(codec, port, res & ~0x8000); - dev_dbg(codec->card->dev, "ali_codec_ready: codec is not ready.\n "); + dev_dbg(codec->card->dev, "ali_codec_ready: codec is not ready.\n"); return -EIO; } @@ -1824,18 +1822,13 @@ static int snd_ali_mixer(struct snd_ali *codec) return 0; } -#ifdef CONFIG_PM_SLEEP static int ali_suspend(struct device *dev) { struct snd_card *card = dev_get_drvdata(dev); struct snd_ali *chip = card->private_data; - struct snd_ali_image *im; + struct snd_ali_image *im = &chip->image; int i, j; - im = chip->image; - if (!im) - return 0; - snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); for (i = 0; i < chip->num_of_codecs; i++) snd_ac97_suspend(chip->ac97[i]); @@ -1872,13 +1865,9 @@ static int ali_resume(struct device *dev) { struct snd_card *card = dev_get_drvdata(dev); struct snd_ali *chip = card->private_data; - struct snd_ali_image *im; + struct snd_ali_image *im = &chip->image; int i, j; - im = chip->image; - if (!im) - return 0; - spin_lock_irq(&chip->reg_lock); for (i = 0; i < ALI_CHANNELS; i++) { @@ -1908,11 +1897,7 @@ static int ali_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(ali_pm, ali_suspend, ali_resume); -#define ALI_PM_OPS &ali_pm -#else -#define ALI_PM_OPS NULL -#endif /* CONFIG_PM_SLEEP */ +static DEFINE_SIMPLE_DEV_PM_OPS(ali_pm, ali_suspend, ali_resume); static void snd_ali_free(struct snd_card *card) { @@ -2112,13 +2097,6 @@ static int snd_ali_create(struct snd_card *card, return err; } -#ifdef CONFIG_PM_SLEEP - codec->image = devm_kmalloc(&pci->dev, sizeof(*codec->image), - GFP_KERNEL); - if (!codec->image) - dev_warn(card->dev, "can't allocate apm buffer\n"); -#endif - snd_ali_enable_address_interrupt(codec); codec->hw_initialized = 1; return 0; @@ -2181,7 +2159,7 @@ static struct pci_driver ali5451_driver = { .id_table = snd_ali_ids, .probe = snd_ali_probe, .driver = { - .pm = ALI_PM_OPS, + .pm = &ali_pm, }, }; diff --git a/sound/pci/als300.c b/sound/pci/als300.c index c70aff060120..c7c481203ef8 100644 --- a/sound/pci/als300.c +++ b/sound/pci/als300.c @@ -654,7 +654,6 @@ static int snd_als300_create(struct snd_card *card, return 0; } -#ifdef CONFIG_PM_SLEEP static int snd_als300_suspend(struct device *dev) { struct snd_card *card = dev_get_drvdata(dev); @@ -677,11 +676,7 @@ static int snd_als300_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(snd_als300_pm, snd_als300_suspend, snd_als300_resume); -#define SND_ALS300_PM_OPS &snd_als300_pm -#else -#define SND_ALS300_PM_OPS NULL -#endif +static DEFINE_SIMPLE_DEV_PM_OPS(snd_als300_pm, snd_als300_suspend, snd_als300_resume); static int snd_als300_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) @@ -739,7 +734,7 @@ static struct pci_driver als300_driver = { .id_table = snd_als300_ids, .probe = snd_als300_probe, .driver = { - .pm = SND_ALS300_PM_OPS, + .pm = &snd_als300_pm, }, }; diff --git a/sound/pci/als4000.c b/sound/pci/als4000.c index f33aeb692a11..022473594c73 100644 --- a/sound/pci/als4000.c +++ b/sound/pci/als4000.c @@ -936,7 +936,6 @@ static int snd_card_als4000_probe(struct pci_dev *pci, return snd_card_free_on_error(&pci->dev, __snd_card_als4000_probe(pci, pci_id)); } -#ifdef CONFIG_PM_SLEEP static int snd_als4000_suspend(struct device *dev) { struct snd_card *card = dev_get_drvdata(dev); @@ -968,18 +967,14 @@ static int snd_als4000_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(snd_als4000_pm, snd_als4000_suspend, snd_als4000_resume); -#define SND_ALS4000_PM_OPS &snd_als4000_pm -#else -#define SND_ALS4000_PM_OPS NULL -#endif /* CONFIG_PM_SLEEP */ +static DEFINE_SIMPLE_DEV_PM_OPS(snd_als4000_pm, snd_als4000_suspend, snd_als4000_resume); static struct pci_driver als4000_driver = { .name = KBUILD_MODNAME, .id_table = snd_als4000_ids, .probe = snd_card_als4000_probe, .driver = { - .pm = SND_ALS4000_PM_OPS, + .pm = &snd_als4000_pm, }, }; diff --git a/sound/pci/asihpi/Makefile b/sound/pci/asihpi/Makefile index 8351f8f5b523..d558a974fa7e 100644 --- a/sound/pci/asihpi/Makefile +++ b/sound/pci/asihpi/Makefile @@ -1,5 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-only -snd-asihpi-objs := asihpi.o hpioctl.o hpimsginit.o\ +snd-asihpi-y := asihpi.o hpioctl.o hpimsginit.o\ hpicmn.o hpifunc.o hpidebug.o hpidspcd.o\ hpios.o hpi6000.o hpi6205.o hpimsgx.o diff --git a/sound/pci/asihpi/asihpi.c b/sound/pci/asihpi/asihpi.c index 001786e2aba1..5a84591b13fc 100644 --- a/sound/pci/asihpi/asihpi.c +++ b/sound/pci/asihpi/asihpi.c @@ -36,19 +36,10 @@ MODULE_AUTHOR("AudioScience inc. <support@audioscience.com>"); MODULE_DESCRIPTION("AudioScience ALSA ASI5xxx ASI6xxx ASI87xx ASI89xx " HPI_VER_STRING); -#if defined CONFIG_SND_DEBUG_VERBOSE -/** - * snd_printddd - very verbose debug printk - * @format: format string - * - * Works like snd_printk() for debugging purposes. - * Ignored when CONFIG_SND_DEBUG_VERBOSE is not set. - * Must set snd module debug parameter to 3 to enable at runtime. - */ -#define snd_printddd(format, args...) \ - __snd_printk(3, __FILE__, __LINE__, format, ##args) +#ifdef ASIHPI_VERBOSE_DEBUG +#define asihpi_dbg(format, args...) pr_debug(format, ##args) #else -#define snd_printddd(format, args...) do { } while (0) +#define asihpi_dbg(format, args...) do { } while (0) #endif static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* index 0-MAX */ @@ -260,8 +251,7 @@ static inline u16 hpi_stream_group_reset(u32 h_stream) static u16 handle_error(u16 err, int line, char *filename) { if (err) - printk(KERN_WARNING - "in file %s, line %d: HPI error %d\n", + pr_warn("in file %s, line %d: HPI error %d\n", filename, line, err); return err; } @@ -273,16 +263,18 @@ static u16 handle_error(u16 err, int line, char *filename) static void print_hwparams(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *p) { + struct device *dev = substream->pcm->card->dev; char name[16]; + snd_pcm_debug_name(substream, name, sizeof(name)); - snd_printdd("%s HWPARAMS\n", name); - snd_printdd(" samplerate=%dHz channels=%d format=%d subformat=%d\n", + dev_dbg(dev, "%s HWPARAMS\n", name); + dev_dbg(dev, " samplerate=%dHz channels=%d format=%d subformat=%d\n", params_rate(p), params_channels(p), params_format(p), params_subformat(p)); - snd_printdd(" buffer=%dB period=%dB period_size=%dB periods=%d\n", + dev_dbg(dev, " buffer=%dB period=%dB period_size=%dB periods=%d\n", params_buffer_bytes(p), params_period_bytes(p), params_period_size(p), params_periods(p)); - snd_printdd(" buffer_size=%d access=%d data_rate=%dB/s\n", + dev_dbg(dev, " buffer_size=%d access=%d data_rate=%dB/s\n", params_buffer_size(p), params_access(p), params_rate(p) * params_channels(p) * snd_pcm_format_width(params_format(p)) / 8); @@ -317,7 +309,8 @@ static const snd_pcm_format_t hpi_to_alsa_formats[] = { }; -static int snd_card_asihpi_format_alsa2hpi(snd_pcm_format_t alsa_format, +static int snd_card_asihpi_format_alsa2hpi(struct snd_card_asihpi *asihpi, + snd_pcm_format_t alsa_format, u16 *hpi_format) { u16 format; @@ -330,8 +323,8 @@ static int snd_card_asihpi_format_alsa2hpi(snd_pcm_format_t alsa_format, } } - snd_printd(KERN_WARNING "failed match for alsa format %d\n", - alsa_format); + dev_dbg(asihpi->card->dev, "failed match for alsa format %d\n", + alsa_format); *hpi_format = 0; return -EINVAL; } @@ -439,7 +432,7 @@ static int snd_card_asihpi_pcm_hw_params(struct snd_pcm_substream *substream, unsigned int bytes_per_sec; print_hwparams(substream, params); - err = snd_card_asihpi_format_alsa2hpi(params_format(params), &format); + err = snd_card_asihpi_format_alsa2hpi(card, params_format(params), &format); if (err) return err; @@ -461,17 +454,17 @@ static int snd_card_asihpi_pcm_hw_params(struct snd_pcm_substream *substream, err = hpi_stream_host_buffer_attach(dpcm->h_stream, params_buffer_bytes(params), runtime->dma_addr); if (err == 0) { - snd_printdd( + dev_dbg(card->card->dev, "stream_host_buffer_attach success %u %lu\n", params_buffer_bytes(params), (unsigned long)runtime->dma_addr); } else { - snd_printd("stream_host_buffer_attach error %d\n", - err); + dev_dbg(card->card->dev, + "stream_host_buffer_attach error %d\n", err); return -ENOMEM; } - err = hpi_stream_get_info_ex(dpcm->h_stream, NULL, + hpi_stream_get_info_ex(dpcm->h_stream, NULL, &dpcm->hpi_buffer_attached, NULL, NULL, NULL); } bytes_per_sec = params_rate(params) * params_channels(params); @@ -569,7 +562,7 @@ static int snd_card_asihpi_trigger(struct snd_pcm_substream *substream, switch (cmd) { case SNDRV_PCM_TRIGGER_START: - snd_printdd("%s trigger start\n", name); + dev_dbg(card->card->dev, "%s trigger start\n", name); snd_pcm_group_for_each_entry(s, substream) { struct snd_pcm_runtime *runtime = s->runtime; struct snd_card_asihpi_pcm *ds = runtime->private_data; @@ -590,7 +583,7 @@ static int snd_card_asihpi_trigger(struct snd_pcm_substream *substream, * data?? */ unsigned int preload = ds->period_bytes * 1; - snd_printddd("%d preload %d\n", s->number, preload); + asihpi_dbg("%d preload %d\n", s->number, preload); hpi_handle_error(hpi_outstream_write_buf( ds->h_stream, &runtime->dma_area[0], @@ -600,7 +593,7 @@ static int snd_card_asihpi_trigger(struct snd_pcm_substream *substream, } if (card->support_grouping) { - snd_printdd("%d group\n", s->number); + dev_dbg(card->card->dev, "%d group\n", s->number); e = hpi_stream_group_add( dpcm->h_stream, ds->h_stream); @@ -621,7 +614,7 @@ static int snd_card_asihpi_trigger(struct snd_pcm_substream *substream, break; case SNDRV_PCM_TRIGGER_STOP: - snd_printdd("%s trigger stop\n", name); + dev_dbg(card->card->dev, "%s trigger stop\n", name); card->pcm_stop(substream); snd_pcm_group_for_each_entry(s, substream) { if (snd_pcm_substream_chip(s) != card) @@ -635,7 +628,7 @@ static int snd_card_asihpi_trigger(struct snd_pcm_substream *substream, __snd_pcm_set_state(s->runtime, SNDRV_PCM_STATE_SETUP); if (card->support_grouping) { - snd_printdd("%d group\n", s->number); + dev_dbg(card->card->dev, "%d group\n", s->number); snd_pcm_trigger_done(s, substream); } else break; @@ -652,17 +645,17 @@ static int snd_card_asihpi_trigger(struct snd_pcm_substream *substream, break; case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - snd_printdd("%s trigger pause release\n", name); + dev_dbg(card->card->dev, "%s trigger pause release\n", name); card->pcm_start(substream); hpi_handle_error(hpi_stream_start(dpcm->h_stream)); break; case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - snd_printdd("%s trigger pause push\n", name); + dev_dbg(card->card->dev, "%s trigger pause push\n", name); card->pcm_stop(substream); hpi_handle_error(hpi_stream_stop(dpcm->h_stream)); break; default: - snd_printd(KERN_ERR "\tINVALID\n"); + dev_dbg(card->card->dev, "\tINVALID\n"); return -EINVAL; } @@ -760,12 +753,13 @@ static void snd_card_asihpi_timer_function(struct timer_list *t) if (state == HPI_STATE_STOPPED) { if (bytes_avail == 0) { hpi_handle_error(hpi_stream_start(ds->h_stream)); - snd_printdd("P%d start\n", s->number); + dev_dbg(card->card->dev, + "P%d start\n", s->number); ds->drained_count = 0; } } else if (state == HPI_STATE_DRAINED) { - snd_printd(KERN_WARNING "P%d drained\n", - s->number); + dev_dbg(card->card->dev, + "P%d drained\n", s->number); ds->drained_count++; if (ds->drained_count > 20) { snd_pcm_stop_xrun(s); @@ -790,7 +784,7 @@ static void snd_card_asihpi_timer_function(struct timer_list *t) newdata); } - snd_printddd( + asihpi_dbg( "timer1, %s, %d, S=%d, elap=%d, rw=%d, dsp=%d, left=%d, aux=%d, space=%d, hw_ptr=%ld, appl_ptr=%ld\n", name, s->number, state, ds->pcm_buf_elapsed_dma_ofs, @@ -821,7 +815,7 @@ static void snd_card_asihpi_timer_function(struct timer_list *t) next_jiffies = max(next_jiffies, 1U); dpcm->timer.expires = jiffies + next_jiffies; - snd_printddd("timer2, jif=%d, buf_pos=%d, newdata=%d, xfer=%d\n", + asihpi_dbg("timer2, jif=%d, buf_pos=%d, newdata=%d, xfer=%d\n", next_jiffies, pcm_buf_dma_ofs, newdata, xfercount); snd_pcm_group_for_each_entry(s, substream) { @@ -854,7 +848,7 @@ static void snd_card_asihpi_timer_function(struct timer_list *t) } if (s->stream == SNDRV_PCM_STREAM_PLAYBACK) { - snd_printddd("write1, P=%d, xfer=%d, buf_ofs=%d\n", + asihpi_dbg("write1, P=%d, xfer=%d, buf_ofs=%d\n", s->number, xfer1, buf_ofs); hpi_handle_error( hpi_outstream_write_buf( @@ -864,7 +858,7 @@ static void snd_card_asihpi_timer_function(struct timer_list *t) if (xfer2) { pd = s->runtime->dma_area; - snd_printddd("write2, P=%d, xfer=%d, buf_ofs=%d\n", + asihpi_dbg("write2, P=%d, xfer=%d, buf_ofs=%d\n", s->number, xfercount - xfer1, buf_ofs); hpi_handle_error( @@ -874,7 +868,7 @@ static void snd_card_asihpi_timer_function(struct timer_list *t) &ds->format)); } } else { - snd_printddd("read1, C=%d, xfer=%d\n", + asihpi_dbg("read1, C=%d, xfer=%d\n", s->number, xfer1); hpi_handle_error( hpi_instream_read_buf( @@ -882,7 +876,7 @@ static void snd_card_asihpi_timer_function(struct timer_list *t) pd, xfer1)); if (xfer2) { pd = s->runtime->dma_area; - snd_printddd("read2, C=%d, xfer=%d\n", + asihpi_dbg("read2, C=%d, xfer=%d\n", s->number, xfer2); hpi_handle_error( hpi_instream_read_buf( @@ -919,8 +913,6 @@ static int snd_card_asihpi_playback_prepare(struct snd_pcm_substream * struct snd_pcm_runtime *runtime = substream->runtime; struct snd_card_asihpi_pcm *dpcm = runtime->private_data; - snd_printdd("P%d prepare\n", substream->number); - hpi_handle_error(hpi_outstream_reset(dpcm->h_stream)); dpcm->pcm_buf_host_rw_ofs = 0; dpcm->pcm_buf_dma_ofs = 0; @@ -938,7 +930,7 @@ snd_card_asihpi_playback_pointer(struct snd_pcm_substream *substream) snd_pcm_debug_name(substream, name, sizeof(name)); ptr = bytes_to_frames(runtime, dpcm->pcm_buf_dma_ofs % dpcm->buffer_bytes); - snd_printddd("%s, pointer=%ld\n", name, (unsigned long)ptr); + asihpi_dbg("%s, pointer=%ld\n", name, (unsigned long)ptr); return ptr; } @@ -1060,8 +1052,6 @@ static int snd_card_asihpi_playback_open(struct snd_pcm_substream *substream) snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, card->update_interval_frames, UINT_MAX); - snd_printdd("playback open\n"); - return 0; } @@ -1071,8 +1061,6 @@ static int snd_card_asihpi_playback_close(struct snd_pcm_substream *substream) struct snd_card_asihpi_pcm *dpcm = runtime->private_data; hpi_handle_error(hpi_outstream_close(dpcm->h_stream)); - snd_printdd("playback close\n"); - return 0; } @@ -1095,7 +1083,7 @@ snd_card_asihpi_capture_pointer(struct snd_pcm_substream *substream) char name[16]; snd_pcm_debug_name(substream, name, sizeof(name)); - snd_printddd("%s, pointer=%d\n", name, dpcm->pcm_buf_dma_ofs); + asihpi_dbg("%s, pointer=%d\n", name, dpcm->pcm_buf_dma_ofs); /* NOTE Unlike playback can't use actual samples_played for the capture position, because those samples aren't yet in the local buffer available for reading. @@ -1113,7 +1101,6 @@ static int snd_card_asihpi_capture_prepare(struct snd_pcm_substream *substream) dpcm->pcm_buf_dma_ofs = 0; dpcm->pcm_buf_elapsed_dma_ofs = 0; - snd_printdd("Capture Prepare %d\n", substream->number); return 0; } @@ -1162,8 +1149,9 @@ static int snd_card_asihpi_capture_open(struct snd_pcm_substream *substream) if (dpcm == NULL) return -ENOMEM; - snd_printdd("capture open adapter %d stream %d\n", - card->hpi->adapter->index, substream->number); + + dev_dbg(card->card->dev, "capture open adapter %d stream %d\n", + card->hpi->adapter->index, substream->number); err = hpi_handle_error( hpi_instream_open(card->hpi->adapter->index, @@ -1413,8 +1401,6 @@ static void asihpi_ctl_init(struct snd_kcontrol_new *snd_control, hpi_ctl->src_node_index, dir, name); } - /* printk(KERN_INFO "Adding %s %d to %d ", hpi_ctl->name, - hpi_ctl->wSrcNodeType, hpi_ctl->wDstNodeType); */ } /*------------------------------------------------------------ @@ -2175,9 +2161,8 @@ static int snd_asihpi_mux_get(struct snd_kcontrol *kcontrol, return 0; } } - snd_printd(KERN_WARNING - "Control %x failed to match mux source %hu %hu\n", - h_control, source_type, source_index); + pr_warn("%s: Control %x failed to match mux source %hu %hu\n", + __func__, h_control, source_type, source_index); ucontrol->value.enumerated.item[0] = 0; return 0; } diff --git a/sound/pci/asihpi/hpimsgx.c b/sound/pci/asihpi/hpimsgx.c index d0caef299481..b68e6bfbbfba 100644 --- a/sound/pci/asihpi/hpimsgx.c +++ b/sound/pci/asihpi/hpimsgx.c @@ -708,7 +708,7 @@ static u16 HPIMSGX__init(struct hpi_message *phm, phr->error = HPI_ERROR_PROCESSING_MESSAGE; return phr->error; } - if (hr.error == 0) { + if (hr.error == 0 && hr.u.s.adapter_index < HPI_MAX_ADAPTERS) { /* the adapter was created successfully save the mapping for future use */ hpi_entry_points[hr.u.s.adapter_index] = entry_point_func; diff --git a/sound/pci/asihpi/hpioctl.c b/sound/pci/asihpi/hpioctl.c index 477a5b4b50bc..9fb0c8e503df 100644 --- a/sound/pci/asihpi/hpioctl.c +++ b/sound/pci/asihpi/hpioctl.c @@ -356,7 +356,7 @@ int asihpi_adapter_probe(struct pci_dev *pci_dev, memset(&adapter, 0, sizeof(adapter)); - dev_printk(KERN_DEBUG, &pci_dev->dev, + dev_dbg(&pci_dev->dev, "probe %04x:%04x,%04x:%04x,%04x\n", pci_dev->vendor, pci_dev->device, pci_dev->subsystem_vendor, pci_dev->subsystem_device, pci_dev->devfn); diff --git a/sound/pci/atiixp.c b/sound/pci/atiixp.c index 43d01f1847ed..df2fef726d60 100644 --- a/sound/pci/atiixp.c +++ b/sound/pci/atiixp.c @@ -520,7 +520,6 @@ static int snd_atiixp_aclink_reset(struct atiixp *chip) return 0; } -#ifdef CONFIG_PM_SLEEP static int snd_atiixp_aclink_down(struct atiixp *chip) { // if (atiixp_read(chip, MODEM_MIRROR) & 0x1) /* modem running, too? */ @@ -530,7 +529,6 @@ static int snd_atiixp_aclink_down(struct atiixp *chip) ATI_REG_CMD_POWERDOWN); return 0; } -#endif /* * auto-detection of codecs @@ -1454,7 +1452,6 @@ static int snd_atiixp_mixer_new(struct atiixp *chip, int clock, } -#ifdef CONFIG_PM_SLEEP /* * power management */ @@ -1499,12 +1496,7 @@ static int snd_atiixp_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(snd_atiixp_pm, snd_atiixp_suspend, snd_atiixp_resume); -#define SND_ATIIXP_PM_OPS &snd_atiixp_pm -#else -#define SND_ATIIXP_PM_OPS NULL -#endif /* CONFIG_PM_SLEEP */ - +static DEFINE_SIMPLE_DEV_PM_OPS(snd_atiixp_pm, snd_atiixp_suspend, snd_atiixp_resume); /* * proc interface for register dump @@ -1634,7 +1626,7 @@ static struct pci_driver atiixp_driver = { .id_table = snd_atiixp_ids, .probe = snd_atiixp_probe, .driver = { - .pm = SND_ATIIXP_PM_OPS, + .pm = &snd_atiixp_pm, }, }; diff --git a/sound/pci/atiixp_modem.c b/sound/pci/atiixp_modem.c index 8864c4c3c7e1..eb569539f322 100644 --- a/sound/pci/atiixp_modem.c +++ b/sound/pci/atiixp_modem.c @@ -496,7 +496,6 @@ static int snd_atiixp_aclink_reset(struct atiixp_modem *chip) return 0; } -#ifdef CONFIG_PM_SLEEP static int snd_atiixp_aclink_down(struct atiixp_modem *chip) { // if (atiixp_read(chip, MODEM_MIRROR) & 0x1) /* modem running, too? */ @@ -506,7 +505,6 @@ static int snd_atiixp_aclink_down(struct atiixp_modem *chip) ATI_REG_CMD_POWERDOWN); return 0; } -#endif /* * auto-detection of codecs @@ -1094,7 +1092,6 @@ static int snd_atiixp_mixer_new(struct atiixp_modem *chip, int clock) } -#ifdef CONFIG_PM_SLEEP /* * power management */ @@ -1128,11 +1125,7 @@ static int snd_atiixp_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(snd_atiixp_pm, snd_atiixp_suspend, snd_atiixp_resume); -#define SND_ATIIXP_PM_OPS &snd_atiixp_pm -#else -#define SND_ATIIXP_PM_OPS NULL -#endif /* CONFIG_PM_SLEEP */ +static DEFINE_SIMPLE_DEV_PM_OPS(snd_atiixp_pm, snd_atiixp_suspend, snd_atiixp_resume); /* * proc interface for register dump @@ -1258,7 +1251,7 @@ static struct pci_driver atiixp_modem_driver = { .id_table = snd_atiixp_ids, .probe = snd_atiixp_probe, .driver = { - .pm = SND_ATIIXP_PM_OPS, + .pm = &snd_atiixp_pm, }, }; diff --git a/sound/pci/au88x0/Makefile b/sound/pci/au88x0/Makefile index 78ab11562f4d..5ec5abdee28d 100644 --- a/sound/pci/au88x0/Makefile +++ b/sound/pci/au88x0/Makefile @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 -snd-au8810-objs := au8810.o -snd-au8820-objs := au8820.o -snd-au8830-objs := au8830.o +snd-au8810-y := au8810.o +snd-au8820-y := au8820.o +snd-au8830-y := au8830.o obj-$(CONFIG_SND_AU8810) += snd-au8810.o obj-$(CONFIG_SND_AU8820) += snd-au8820.o diff --git a/sound/pci/aw2/Makefile b/sound/pci/aw2/Makefile index f9045afb4cda..c246f7c7f2bf 100644 --- a/sound/pci/aw2/Makefile +++ b/sound/pci/aw2/Makefile @@ -1,4 +1,4 @@ # SPDX-License-Identifier: GPL-2.0-only -snd-aw2-objs := aw2-alsa.o aw2-saa7146.o +snd-aw2-y := aw2-alsa.o aw2-saa7146.o obj-$(CONFIG_SND_AW2) += snd-aw2.o diff --git a/sound/pci/aw2/aw2-saa7146.h b/sound/pci/aw2/aw2-saa7146.h index b5c5a71c0ac3..3a3de56b9b07 100644 --- a/sound/pci/aw2/aw2-saa7146.h +++ b/sound/pci/aw2/aw2-saa7146.h @@ -19,11 +19,12 @@ #define NUM_STREAM_CAPTURE_ANA 0 -typedef void (*snd_aw2_saa7146_it_cb) (void *); +struct snd_pcm_substream; +typedef void (*snd_aw2_saa7146_it_cb) (struct snd_pcm_substream *); struct snd_aw2_saa7146_cb_param { snd_aw2_saa7146_it_cb p_it_callback; - void *p_callback_param; + struct snd_pcm_substream *p_callback_param; }; /* definition of the chip-specific record */ diff --git a/sound/pci/azt3328.c b/sound/pci/azt3328.c index 431f0026b507..8a895d838005 100644 --- a/sound/pci/azt3328.c +++ b/sound/pci/azt3328.c @@ -295,7 +295,6 @@ struct snd_azf3328 { * CONFIG_PM register storage below, but that's slightly difficult. */ u16 shadow_reg_ctrl_6AH; -#ifdef CONFIG_PM_SLEEP /* register value containers for power management * Note: not always full I/O range preserved (similar to Win driver!) */ u32 saved_regs_ctrl[AZF_ALIGN(AZF_IO_SIZE_CTRL_PM) / 4]; @@ -303,7 +302,6 @@ struct snd_azf3328 { u32 saved_regs_mpu[AZF_ALIGN(AZF_IO_SIZE_MPU_PM) / 4]; u32 saved_regs_opl3[AZF_ALIGN(AZF_IO_SIZE_OPL3_PM) / 4]; u32 saved_regs_mixer[AZF_ALIGN(AZF_IO_SIZE_MIXER_PM) / 4]; -#endif }; static const struct pci_device_id snd_azf3328_ids[] = { @@ -1222,7 +1220,7 @@ snd_azf3328_codec_setfmt(struct snd_azf3328_codec_data *codec, case AZF_FREQ_22050: freq = SOUNDFORMAT_FREQ_22050; break; case AZF_FREQ_32000: freq = SOUNDFORMAT_FREQ_32000; break; default: - snd_printk(KERN_WARNING "unknown bitrate %d, assuming 44.1kHz!\n", bitrate); + pr_warn("azf3328: unknown bitrate %d, assuming 44.1kHz!\n", bitrate); fallthrough; case AZF_FREQ_44100: freq = SOUNDFORMAT_FREQ_44100; break; case AZF_FREQ_48000: freq = SOUNDFORMAT_FREQ_48000; break; @@ -2517,7 +2515,6 @@ snd_azf3328_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) return snd_card_free_on_error(&pci->dev, __snd_azf3328_probe(pci, pci_id)); } -#ifdef CONFIG_PM_SLEEP static inline void snd_azf3328_suspend_regs(const struct snd_azf3328 *chip, unsigned long io_addr, unsigned count, u32 *saved_regs) @@ -2633,18 +2630,14 @@ snd_azf3328_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(snd_azf3328_pm, snd_azf3328_suspend, snd_azf3328_resume); -#define SND_AZF3328_PM_OPS &snd_azf3328_pm -#else -#define SND_AZF3328_PM_OPS NULL -#endif /* CONFIG_PM_SLEEP */ +static DEFINE_SIMPLE_DEV_PM_OPS(snd_azf3328_pm, snd_azf3328_suspend, snd_azf3328_resume); static struct pci_driver azf3328_driver = { .name = KBUILD_MODNAME, .id_table = snd_azf3328_ids, .probe = snd_azf3328_probe, .driver = { - .pm = SND_AZF3328_PM_OPS, + .pm = &snd_azf3328_pm, }, }; diff --git a/sound/pci/ca0106/Makefile b/sound/pci/ca0106/Makefile index 9e51d3df3ee8..693dc4d80925 100644 --- a/sound/pci/ca0106/Makefile +++ b/sound/pci/ca0106/Makefile @@ -1,5 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-only -snd-ca0106-objs := ca0106_main.o ca0106_mixer.o ca_midi.o +snd-ca0106-y := ca0106_main.o ca0106_mixer.o ca_midi.o snd-ca0106-$(CONFIG_SND_PROC_FS) += ca0106_proc.o obj-$(CONFIG_SND_CA0106) += snd-ca0106.o diff --git a/sound/pci/cmipci.c b/sound/pci/cmipci.c index 08e34b184780..cb8593c376ee 100644 --- a/sound/pci/cmipci.c +++ b/sound/pci/cmipci.c @@ -486,10 +486,8 @@ struct cmipci { spinlock_t reg_lock; -#ifdef CONFIG_PM_SLEEP unsigned int saved_regs[0x20]; unsigned char saved_mixers[0x20]; -#endif }; @@ -1572,14 +1570,6 @@ static const struct snd_pcm_hardware snd_cmipci_capture_spdif = .fifo_size = 0, }; -static const unsigned int rate_constraints[] = { 5512, 8000, 11025, 16000, 22050, - 32000, 44100, 48000, 88200, 96000, 128000 }; -static const struct snd_pcm_hw_constraint_list hw_constraints_rates = { - .count = ARRAY_SIZE(rate_constraints), - .list = rate_constraints, - .mask = 0, -}; - /* * check device open/close */ @@ -1651,11 +1641,9 @@ static int snd_cmipci_playback_open(struct snd_pcm_substream *substream) SNDRV_PCM_RATE_96000; runtime->hw.rate_max = 96000; } else if (cm->chip_version == 55) { - err = snd_pcm_hw_constraint_list(runtime, 0, - SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates); - if (err < 0) - return err; - runtime->hw.rates |= SNDRV_PCM_RATE_KNOT; + runtime->hw.rates |= SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_128000; runtime->hw.rate_max = 128000; } snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 0x10000); @@ -1677,11 +1665,9 @@ static int snd_cmipci_capture_open(struct snd_pcm_substream *substream) runtime->hw.rate_min = 41000; runtime->hw.rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000; } else if (cm->chip_version == 55) { - err = snd_pcm_hw_constraint_list(runtime, 0, - SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates); - if (err < 0) - return err; - runtime->hw.rates |= SNDRV_PCM_RATE_KNOT; + runtime->hw.rates |= SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_128000; runtime->hw.rate_max = 128000; } snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 0x10000); @@ -1717,11 +1703,9 @@ static int snd_cmipci_playback2_open(struct snd_pcm_substream *substream) SNDRV_PCM_RATE_96000; runtime->hw.rate_max = 96000; } else if (cm->chip_version == 55) { - err = snd_pcm_hw_constraint_list(runtime, 0, - SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates); - if (err < 0) - return err; - runtime->hw.rates |= SNDRV_PCM_RATE_KNOT; + runtime->hw.rates |= SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_128000; runtime->hw.rate_max = 128000; } snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 0x10000); @@ -3100,7 +3084,7 @@ static int snd_cmipci_create(struct snd_card *card, struct pci_dev *pci, } } } - sprintf(card->shortname, "C-Media CMI%d", val); + sprintf(card->shortname, "C-Media CMI%u", val); if (cm->chip_version < 68) scnprintf(modelstr, sizeof(modelstr), " (model %d)", cm->chip_version); @@ -3260,7 +3244,6 @@ static int snd_cmipci_probe(struct pci_dev *pci, return err; } -#ifdef CONFIG_PM_SLEEP /* * power management */ @@ -3324,18 +3307,14 @@ static int snd_cmipci_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(snd_cmipci_pm, snd_cmipci_suspend, snd_cmipci_resume); -#define SND_CMIPCI_PM_OPS &snd_cmipci_pm -#else -#define SND_CMIPCI_PM_OPS NULL -#endif /* CONFIG_PM_SLEEP */ +static DEFINE_SIMPLE_DEV_PM_OPS(snd_cmipci_pm, snd_cmipci_suspend, snd_cmipci_resume); static struct pci_driver cmipci_driver = { .name = KBUILD_MODNAME, .id_table = snd_cmipci_ids, .probe = snd_cmipci_probe, .driver = { - .pm = SND_CMIPCI_PM_OPS, + .pm = &snd_cmipci_pm, }, }; diff --git a/sound/pci/cs4281.c b/sound/pci/cs4281.c index 0c9cadf7b3b8..0cc86e73cc62 100644 --- a/sound/pci/cs4281.c +++ b/sound/pci/cs4281.c @@ -470,10 +470,7 @@ struct cs4281 { struct gameport *gameport; -#ifdef CONFIG_PM_SLEEP u32 suspend_regs[SUSPEND_REGISTERS]; -#endif - }; static irqreturn_t snd_cs4281_interrupt(int irq, void *dev_id); @@ -1897,8 +1894,6 @@ static int snd_cs4281_probe(struct pci_dev *pci, /* * Power Management */ -#ifdef CONFIG_PM_SLEEP - static const int saved_regs[SUSPEND_REGISTERS] = { BA0_JSCTL, BA0_GPIOR, @@ -1987,18 +1982,14 @@ static int cs4281_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(cs4281_pm, cs4281_suspend, cs4281_resume); -#define CS4281_PM_OPS &cs4281_pm -#else -#define CS4281_PM_OPS NULL -#endif /* CONFIG_PM_SLEEP */ +static DEFINE_SIMPLE_DEV_PM_OPS(cs4281_pm, cs4281_suspend, cs4281_resume); static struct pci_driver cs4281_driver = { .name = KBUILD_MODNAME, .id_table = snd_cs4281_ids, .probe = snd_cs4281_probe, .driver = { - .pm = CS4281_PM_OPS, + .pm = &cs4281_pm, }, }; diff --git a/sound/pci/ctxfi/Makefile b/sound/pci/ctxfi/Makefile index 70888706a0af..ff2b1cba3a3c 100644 --- a/sound/pci/ctxfi/Makefile +++ b/sound/pci/ctxfi/Makefile @@ -1,5 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-only -snd-ctxfi-objs := xfi.o ctatc.o ctvmem.o ctpcm.o ctmixer.o ctresource.o \ +snd-ctxfi-y := xfi.o ctatc.o ctvmem.o ctpcm.o ctmixer.o ctresource.o \ ctsrc.o ctamixer.o ctdaio.o ctimap.o cthardware.o cttimer.o \ cthw20k2.o cthw20k1.o diff --git a/sound/pci/ctxfi/ctamixer.c b/sound/pci/ctxfi/ctamixer.c index d074727c3e21..397900929aa6 100644 --- a/sound/pci/ctxfi/ctamixer.c +++ b/sound/pci/ctxfi/ctamixer.c @@ -292,7 +292,7 @@ static int put_amixer_rsc(struct amixer_mgr *mgr, struct amixer *amixer) return 0; } -int amixer_mgr_create(struct hw *hw, struct amixer_mgr **ramixer_mgr) +int amixer_mgr_create(struct hw *hw, void **ramixer_mgr) { int err; struct amixer_mgr *amixer_mgr; @@ -321,8 +321,9 @@ error: return err; } -int amixer_mgr_destroy(struct amixer_mgr *amixer_mgr) +int amixer_mgr_destroy(void *ptr) { + struct amixer_mgr *amixer_mgr = ptr; rsc_mgr_uninit(&amixer_mgr->mgr); kfree(amixer_mgr); return 0; @@ -446,7 +447,7 @@ static int put_sum_rsc(struct sum_mgr *mgr, struct sum *sum) return 0; } -int sum_mgr_create(struct hw *hw, struct sum_mgr **rsum_mgr) +int sum_mgr_create(struct hw *hw, void **rsum_mgr) { int err; struct sum_mgr *sum_mgr; @@ -475,8 +476,9 @@ error: return err; } -int sum_mgr_destroy(struct sum_mgr *sum_mgr) +int sum_mgr_destroy(void *ptr) { + struct sum_mgr *sum_mgr = ptr; rsc_mgr_uninit(&sum_mgr->mgr); kfree(sum_mgr); return 0; diff --git a/sound/pci/ctxfi/ctamixer.h b/sound/pci/ctxfi/ctamixer.h index 4498e6139d0e..8fc017da6bda 100644 --- a/sound/pci/ctxfi/ctamixer.h +++ b/sound/pci/ctxfi/ctamixer.h @@ -43,8 +43,8 @@ struct sum_mgr { }; /* Constructor and destructor of daio resource manager */ -int sum_mgr_create(struct hw *hw, struct sum_mgr **rsum_mgr); -int sum_mgr_destroy(struct sum_mgr *sum_mgr); +int sum_mgr_create(struct hw *hw, void **ptr); +int sum_mgr_destroy(void *ptr); /* Define the descriptor of a amixer resource */ struct amixer_rsc_ops; @@ -89,7 +89,7 @@ struct amixer_mgr { }; /* Constructor and destructor of amixer resource manager */ -int amixer_mgr_create(struct hw *hw, struct amixer_mgr **ramixer_mgr); -int amixer_mgr_destroy(struct amixer_mgr *amixer_mgr); +int amixer_mgr_create(struct hw *hw, void **ramixer_mgr); +int amixer_mgr_destroy(void *amixer_mgr); #endif /* CTAMIXER_H */ diff --git a/sound/pci/ctxfi/ctatc.c b/sound/pci/ctxfi/ctatc.c index fbdb8a3d5b8e..2a3e9d8ba7db 100644 --- a/sound/pci/ctxfi/ctatc.c +++ b/sound/pci/ctxfi/ctatc.c @@ -105,23 +105,20 @@ static struct { .public_name = "Mixer"} }; -typedef int (*create_t)(struct hw *, void **); -typedef int (*destroy_t)(void *); - static struct { int (*create)(struct hw *hw, void **rmgr); int (*destroy)(void *mgr); } rsc_mgr_funcs[NUM_RSCTYP] = { - [SRC] = { .create = (create_t)src_mgr_create, - .destroy = (destroy_t)src_mgr_destroy }, - [SRCIMP] = { .create = (create_t)srcimp_mgr_create, - .destroy = (destroy_t)srcimp_mgr_destroy }, - [AMIXER] = { .create = (create_t)amixer_mgr_create, - .destroy = (destroy_t)amixer_mgr_destroy }, - [SUM] = { .create = (create_t)sum_mgr_create, - .destroy = (destroy_t)sum_mgr_destroy }, - [DAIO] = { .create = (create_t)daio_mgr_create, - .destroy = (destroy_t)daio_mgr_destroy } + [SRC] = { .create = src_mgr_create, + .destroy = src_mgr_destroy }, + [SRCIMP] = { .create = srcimp_mgr_create, + .destroy = srcimp_mgr_destroy }, + [AMIXER] = { .create = amixer_mgr_create, + .destroy = amixer_mgr_destroy }, + [SUM] = { .create = sum_mgr_create, + .destroy = sum_mgr_destroy }, + [DAIO] = { .create = daio_mgr_create, + .destroy = daio_mgr_destroy } }; static int diff --git a/sound/pci/ctxfi/ctdaio.c b/sound/pci/ctxfi/ctdaio.c index 7fc720046ce2..9993b02d2968 100644 --- a/sound/pci/ctxfi/ctdaio.c +++ b/sound/pci/ctxfi/ctdaio.c @@ -211,52 +211,30 @@ static int dao_set_right_input(struct dao *dao, struct rsc *input) return 0; } -static int dao_clear_left_input(struct dao *dao) +static int dao_clear_input(struct dao *dao, unsigned int start, unsigned int end) { - struct imapper *entry; - struct daio *daio = &dao->daio; - int i; + unsigned int i; - if (!dao->imappers[0]) + if (!dao->imappers[start]) return 0; - - entry = dao->imappers[0]; - dao->mgr->imap_delete(dao->mgr, entry); - /* Program conjugate resources */ - for (i = 1; i < daio->rscl.msr; i++) { - entry = dao->imappers[i]; - dao->mgr->imap_delete(dao->mgr, entry); + for (i = start; i < end; i++) { + dao->mgr->imap_delete(dao->mgr, dao->imappers[i]); dao->imappers[i] = NULL; } - kfree(dao->imappers[0]); - dao->imappers[0] = NULL; - return 0; } -static int dao_clear_right_input(struct dao *dao) -{ - struct imapper *entry; - struct daio *daio = &dao->daio; - int i; - if (!dao->imappers[daio->rscl.msr]) - return 0; - - entry = dao->imappers[daio->rscl.msr]; - dao->mgr->imap_delete(dao->mgr, entry); - /* Program conjugate resources */ - for (i = 1; i < daio->rscr.msr; i++) { - entry = dao->imappers[daio->rscl.msr + i]; - dao->mgr->imap_delete(dao->mgr, entry); - dao->imappers[daio->rscl.msr + i] = NULL; - } - - kfree(dao->imappers[daio->rscl.msr]); - dao->imappers[daio->rscl.msr] = NULL; +static int dao_clear_left_input(struct dao *dao) +{ + return dao_clear_input(dao, 0, dao->daio.rscl.msr); +} - return 0; +static int dao_clear_right_input(struct dao *dao) +{ + return dao_clear_input(dao, dao->daio.rscl.msr, + dao->daio.rscl.msr + dao->daio.rscr.msr); } static const struct dao_rsc_ops dao_ops = { @@ -684,7 +662,7 @@ static int daio_mgr_commit_write(struct daio_mgr *mgr) return 0; } -int daio_mgr_create(struct hw *hw, struct daio_mgr **rdaio_mgr) +int daio_mgr_create(struct hw *hw, void **rdaio_mgr) { int err, i; struct daio_mgr *daio_mgr; @@ -738,8 +716,9 @@ error1: return err; } -int daio_mgr_destroy(struct daio_mgr *daio_mgr) +int daio_mgr_destroy(void *ptr) { + struct daio_mgr *daio_mgr = ptr; unsigned long flags; /* free daio input mapper list */ diff --git a/sound/pci/ctxfi/ctdaio.h b/sound/pci/ctxfi/ctdaio.h index bd6310f48013..15147fe5f74a 100644 --- a/sound/pci/ctxfi/ctdaio.h +++ b/sound/pci/ctxfi/ctdaio.h @@ -115,7 +115,7 @@ struct daio_mgr { }; /* Constructor and destructor of daio resource manager */ -int daio_mgr_create(struct hw *hw, struct daio_mgr **rdaio_mgr); -int daio_mgr_destroy(struct daio_mgr *daio_mgr); +int daio_mgr_create(struct hw *hw, void **ptr); +int daio_mgr_destroy(void *ptr); #endif /* CTDAIO_H */ diff --git a/sound/pci/ctxfi/ctsrc.c b/sound/pci/ctxfi/ctsrc.c index 4a94b4708a77..159bd4008069 100644 --- a/sound/pci/ctxfi/ctsrc.c +++ b/sound/pci/ctxfi/ctsrc.c @@ -540,7 +540,7 @@ static int src_mgr_commit_write(struct src_mgr *mgr) return 0; } -int src_mgr_create(struct hw *hw, struct src_mgr **rsrc_mgr) +int src_mgr_create(struct hw *hw, void **rsrc_mgr) { int err, i; struct src_mgr *src_mgr; @@ -580,8 +580,9 @@ error1: return err; } -int src_mgr_destroy(struct src_mgr *src_mgr) +int src_mgr_destroy(void *ptr) { + struct src_mgr *src_mgr = ptr; rsc_mgr_uninit(&src_mgr->mgr); kfree(src_mgr); @@ -821,7 +822,7 @@ static int srcimp_imap_delete(struct srcimp_mgr *mgr, struct imapper *entry) return err; } -int srcimp_mgr_create(struct hw *hw, struct srcimp_mgr **rsrcimp_mgr) +int srcimp_mgr_create(struct hw *hw, void **rsrcimp_mgr) { int err; struct srcimp_mgr *srcimp_mgr; @@ -866,8 +867,9 @@ error1: return err; } -int srcimp_mgr_destroy(struct srcimp_mgr *srcimp_mgr) +int srcimp_mgr_destroy(void *ptr) { + struct srcimp_mgr *srcimp_mgr = ptr; unsigned long flags; /* free src input mapper list */ diff --git a/sound/pci/ctxfi/ctsrc.h b/sound/pci/ctxfi/ctsrc.h index 1124daf50c9b..e6366cc6a7ae 100644 --- a/sound/pci/ctxfi/ctsrc.h +++ b/sound/pci/ctxfi/ctsrc.h @@ -139,10 +139,10 @@ struct srcimp_mgr { }; /* Constructor and destructor of SRC resource manager */ -int src_mgr_create(struct hw *hw, struct src_mgr **rsrc_mgr); -int src_mgr_destroy(struct src_mgr *src_mgr); +int src_mgr_create(struct hw *hw, void **ptr); +int src_mgr_destroy(void *ptr); /* Constructor and destructor of SRCIMP resource manager */ -int srcimp_mgr_create(struct hw *hw, struct srcimp_mgr **rsrc_mgr); -int srcimp_mgr_destroy(struct srcimp_mgr *srcimp_mgr); +int srcimp_mgr_create(struct hw *hw, void **ptr); +int srcimp_mgr_destroy(void *ptr); #endif /* CTSRC_H */ diff --git a/sound/pci/echoaudio/Makefile b/sound/pci/echoaudio/Makefile index 4865b8fe7434..96667641c7cf 100644 --- a/sound/pci/echoaudio/Makefile +++ b/sound/pci/echoaudio/Makefile @@ -4,20 +4,20 @@ # Copyright (c) 2003 by Giuliano Pochini <pochini@shiny.it> # -snd-darla20-objs := darla20.o -snd-gina20-objs := gina20.o -snd-layla20-objs := layla20.o -snd-darla24-objs := darla24.o -snd-gina24-objs := gina24.o -snd-layla24-objs := layla24.o -snd-mona-objs := mona.o -snd-mia-objs := mia.o -snd-echo3g-objs := echo3g.o -snd-indigo-objs := indigo.o -snd-indigoio-objs := indigoio.o -snd-indigodj-objs := indigodj.o -snd-indigoiox-objs := indigoiox.o -snd-indigodjx-objs := indigodjx.o +snd-darla20-y := darla20.o +snd-gina20-y := gina20.o +snd-layla20-y := layla20.o +snd-darla24-y := darla24.o +snd-gina24-y := gina24.o +snd-layla24-y := layla24.o +snd-mona-y := mona.o +snd-mia-y := mia.o +snd-echo3g-y := echo3g.o +snd-indigo-y := indigo.o +snd-indigoio-y := indigoio.o +snd-indigodj-y := indigodj.o +snd-indigoiox-y := indigoiox.o +snd-indigodjx-y := indigodjx.o obj-$(CONFIG_SND_DARLA20) += snd-darla20.o obj-$(CONFIG_SND_GINA20) += snd-gina20.o diff --git a/sound/pci/echoaudio/echoaudio.c b/sound/pci/echoaudio/echoaudio.c index c70c3ac4e99a..7484de255a3e 100644 --- a/sound/pci/echoaudio/echoaudio.c +++ b/sound/pci/echoaudio/echoaudio.c @@ -34,7 +34,6 @@ static int get_firmware(const struct firmware **fw_entry, int err; char name[30]; -#ifdef CONFIG_PM_SLEEP if (chip->fw_cache[fw_index]) { dev_dbg(chip->card->dev, "firmware requested: %s is cached\n", @@ -42,7 +41,6 @@ static int get_firmware(const struct firmware **fw_entry, *fw_entry = chip->fw_cache[fw_index]; return 0; } -#endif dev_dbg(chip->card->dev, "firmware requested: %s\n", card_fw[fw_index].data); @@ -51,10 +49,8 @@ static int get_firmware(const struct firmware **fw_entry, if (err < 0) dev_err(chip->card->dev, "get_firmware(): Firmware not available (%d)\n", err); -#ifdef CONFIG_PM_SLEEP else chip->fw_cache[fw_index] = *fw_entry; -#endif return err; } @@ -63,18 +59,13 @@ static int get_firmware(const struct firmware **fw_entry, static void free_firmware(const struct firmware *fw_entry, struct echoaudio *chip) { -#ifdef CONFIG_PM_SLEEP dev_dbg(chip->card->dev, "firmware not released (kept in cache)\n"); -#else - release_firmware(fw_entry); -#endif } static void free_firmware_cache(struct echoaudio *chip) { -#ifdef CONFIG_PM_SLEEP int i; for (i = 0; i < 8 ; i++) @@ -82,8 +73,6 @@ static void free_firmware_cache(struct echoaudio *chip) release_firmware(chip->fw_cache[i]); dev_dbg(chip->card->dev, "release_firmware(%d)\n", i); } - -#endif } @@ -2146,8 +2135,6 @@ static int snd_echo_probe(struct pci_dev *pci, } -#if defined(CONFIG_PM_SLEEP) - static int snd_echo_suspend(struct device *dev) { struct echoaudio *chip = dev_get_drvdata(dev); @@ -2237,11 +2224,7 @@ static int snd_echo_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(snd_echo_pm, snd_echo_suspend, snd_echo_resume); -#define SND_ECHO_PM_OPS &snd_echo_pm -#else -#define SND_ECHO_PM_OPS NULL -#endif /* CONFIG_PM_SLEEP */ +static DEFINE_SIMPLE_DEV_PM_OPS(snd_echo_pm, snd_echo_suspend, snd_echo_resume); /****************************************************************************** Everything starts and ends here @@ -2253,7 +2236,7 @@ static struct pci_driver echo_driver = { .id_table = snd_echo_ids, .probe = snd_echo_probe, .driver = { - .pm = SND_ECHO_PM_OPS, + .pm = &snd_echo_pm, }, }; diff --git a/sound/pci/echoaudio/echoaudio.h b/sound/pci/echoaudio/echoaudio.h index d51de3e4edfa..511f2fcc0fb9 100644 --- a/sound/pci/echoaudio/echoaudio.h +++ b/sound/pci/echoaudio/echoaudio.h @@ -422,9 +422,7 @@ struct echoaudio { u32 __iomem *dsp_registers; /* DSP's register base */ u32 active_mask; /* Chs. active mask or * punks out */ -#ifdef CONFIG_PM_SLEEP const struct firmware *fw_cache[8]; /* Cached firmwares */ -#endif #ifdef ECHOCARD_HAS_MIDI u16 mtc_state; /* State for MIDI input parsing state machine */ diff --git a/sound/pci/echoaudio/echoaudio_3g.c b/sound/pci/echoaudio/echoaudio_3g.c index cc3c79387194..18b4d4b4d38d 100644 --- a/sound/pci/echoaudio/echoaudio_3g.c +++ b/sound/pci/echoaudio/echoaudio_3g.c @@ -274,7 +274,6 @@ static int set_sample_rate(struct echoaudio *chip, u32 rate) chip->digital_mode == DIGITAL_MODE_ADAT)) return -EINVAL; - clock = 0; control_reg = le32_to_cpu(chip->comm_page->control_register); control_reg &= E3G_CLOCK_CLEAR_MASK; diff --git a/sound/pci/emu10k1/Makefile b/sound/pci/emu10k1/Makefile index 17d5527be319..1f325abcb3ef 100644 --- a/sound/pci/emu10k1/Makefile +++ b/sound/pci/emu10k1/Makefile @@ -4,12 +4,12 @@ # Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz> # -snd-emu10k1-objs := emu10k1.o emu10k1_main.o \ +snd-emu10k1-y := emu10k1.o emu10k1_main.o \ irq.o memory.o voice.o emumpu401.o emupcm.o io.o \ emumixer.o emufx.o timer.o p16v.o snd-emu10k1-$(CONFIG_SND_PROC_FS) += emuproc.o -snd-emu10k1-synth-objs := emu10k1_synth.o emu10k1_callback.o emu10k1_patch.o -snd-emu10k1x-objs := emu10k1x.o +snd-emu10k1-synth-y := emu10k1_synth.o emu10k1_callback.o emu10k1_patch.o +snd-emu10k1x-y := emu10k1x.o # Toplevel Module Dependency obj-$(CONFIG_SND_EMU10K1) += snd-emu10k1.o diff --git a/sound/pci/emu10k1/emu10k1.c b/sound/pci/emu10k1/emu10k1.c index fe72e7d77241..dadeda7758ce 100644 --- a/sound/pci/emu10k1/emu10k1.c +++ b/sound/pci/emu10k1/emu10k1.c @@ -189,8 +189,7 @@ static int snd_emu10k1_suspend(struct device *dev) emu->suspend = 1; - cancel_work_sync(&emu->emu1010.firmware_work); - cancel_work_sync(&emu->emu1010.clock_work); + cancel_work_sync(&emu->emu1010.work); snd_ac97_suspend(emu->ac97); diff --git a/sound/pci/emu10k1/emu10k1_callback.c b/sound/pci/emu10k1/emu10k1_callback.c index d36234b88fb4..ef26e4d3e2a3 100644 --- a/sound/pci/emu10k1/emu10k1_callback.c +++ b/sound/pci/emu10k1/emu10k1_callback.c @@ -255,7 +255,7 @@ lookup_voices(struct snd_emux *emu, struct snd_emu10k1 *hw, /* check if sample is finished playing (non-looping only) */ if (bp != best + V_OFF && bp != best + V_FREE && (vp->reg.sample_mode & SNDRV_SFNT_SAMPLE_SINGLESHOT)) { - val = snd_emu10k1_ptr_read(hw, CCCA_CURRADDR, vp->ch) - 64; + val = snd_emu10k1_ptr_read(hw, CCCA_CURRADDR, vp->ch) - 64 + 3; if (val >= vp->reg.loopstart) bp = best + V_OFF; } @@ -310,6 +310,7 @@ start_voice(struct snd_emux_voice *vp) { unsigned int temp; int ch; + bool w_16; u32 psst, dsl, map, ccca, vtarget; unsigned int addr, mapped_offset; struct snd_midi_channel *chan; @@ -321,6 +322,7 @@ start_voice(struct snd_emux_voice *vp) if (snd_BUG_ON(ch < 0)) return -EINVAL; chan = vp->chan; + w_16 = !(vp->reg.sample_mode & SNDRV_SFNT_SAMPLE_8BITS); emem = (struct snd_emu10k1_memblk *)vp->block; if (emem == NULL) @@ -330,7 +332,7 @@ start_voice(struct snd_emux_voice *vp) /* dev_err(hw->card->devK, "emu: cannot map!\n"); */ return -ENOMEM; } - mapped_offset = snd_emu10k1_memblk_offset(emem) >> 1; + mapped_offset = snd_emu10k1_memblk_offset(emem) >> w_16; vp->reg.start += mapped_offset; vp->reg.end += mapped_offset; vp->reg.loopstart += mapped_offset; @@ -362,7 +364,7 @@ start_voice(struct snd_emux_voice *vp) map = (hw->silent_page.addr << hw->address_mode) | (hw->address_mode ? MAP_PTI_MASK1 : MAP_PTI_MASK0); - addr = vp->reg.start + 64; + addr = vp->reg.start + 64 - 3; temp = vp->reg.parm.filterQ; ccca = (temp << 28) | addr; if (vp->apitch < 0xe400) @@ -371,7 +373,7 @@ start_voice(struct snd_emux_voice *vp) unsigned int shift = (vp->apitch - 0xe000) >> 10; ccca |= shift << 25; } - if (vp->reg.sample_mode & SNDRV_SFNT_SAMPLE_8BITS) + if (!w_16) ccca |= CCCA_8BITSELECT; vtarget = (unsigned int)vp->vtarget << 16; diff --git a/sound/pci/emu10k1/emu10k1_main.c b/sound/pci/emu10k1/emu10k1_main.c index de5c41e578e1..5b8a5ba825bd 100644 --- a/sound/pci/emu10k1/emu10k1_main.c +++ b/sound/pci/emu10k1/emu10k1_main.c @@ -652,52 +652,6 @@ static int snd_emu10k1_cardbus_init(struct snd_emu10k1 *emu) return 0; } -static int snd_emu1010_load_firmware_entry(struct snd_emu10k1 *emu, - const struct firmware *fw_entry) -{ - int n, i; - u16 reg; - u8 value; - __always_unused u16 write_post; - - if (!fw_entry) - return -EIO; - - /* The FPGA is a Xilinx Spartan IIE XC2S50E */ - /* On E-MU 0404b it is a Xilinx Spartan III XC3S50 */ - /* GPIO7 -> FPGA PGMN - * GPIO6 -> FPGA CCLK - * GPIO5 -> FPGA DIN - * FPGA CONFIG OFF -> FPGA PGMN - */ - spin_lock_irq(&emu->emu_lock); - outw(0x00, emu->port + A_GPIO); /* Set PGMN low for 100uS. */ - write_post = inw(emu->port + A_GPIO); - udelay(100); - outw(0x80, emu->port + A_GPIO); /* Leave bit 7 set during netlist setup. */ - write_post = inw(emu->port + A_GPIO); - udelay(100); /* Allow FPGA memory to clean */ - for (n = 0; n < fw_entry->size; n++) { - value = fw_entry->data[n]; - for (i = 0; i < 8; i++) { - reg = 0x80; - if (value & 0x1) - reg = reg | 0x20; - value = value >> 1; - outw(reg, emu->port + A_GPIO); - write_post = inw(emu->port + A_GPIO); - outw(reg | 0x40, emu->port + A_GPIO); - write_post = inw(emu->port + A_GPIO); - } - } - /* After programming, set GPIO bit 4 high again. */ - outw(0x10, emu->port + A_GPIO); - write_post = inw(emu->port + A_GPIO); - spin_unlock_irq(&emu->emu_lock); - - return 0; -} - /* firmware file names, per model, init-fw and dock-fw (optional) */ static const char * const firmware_names[5][2] = { [EMU_MODEL_EMU1010] = { @@ -729,72 +683,68 @@ static int snd_emu1010_load_firmware(struct snd_emu10k1 *emu, int dock, return err; } - return snd_emu1010_load_firmware_entry(emu, *fw); + snd_emu1010_load_firmware_entry(emu, dock, *fw); + return 0; } -static void emu1010_firmware_work(struct work_struct *work) +static void snd_emu1010_load_dock_firmware(struct snd_emu10k1 *emu) { - struct snd_emu10k1 *emu; - u32 tmp, tmp2, reg; + u32 tmp, tmp2; int err; - emu = container_of(work, struct snd_emu10k1, - emu1010.firmware_work); - if (emu->card->shutdown) + // The docking events clearly arrive prematurely - while the + // Dock's FPGA seems to be successfully programmed, the Dock + // fails to initialize subsequently if we don't give it some + // time to "warm up" here. + msleep(200); + + dev_info(emu->card->dev, "emu1010: Loading Audio Dock Firmware\n"); + err = snd_emu1010_load_firmware(emu, 1, &emu->dock_fw); + if (err < 0) return; -#ifdef CONFIG_PM_SLEEP - if (emu->suspend) + snd_emu1010_fpga_write(emu, EMU_HANA_FPGA_CONFIG, 0); + + snd_emu1010_fpga_read(emu, EMU_HANA_ID, &tmp); + dev_dbg(emu->card->dev, "emu1010: EMU_HANA+DOCK_ID = 0x%x\n", tmp); + if ((tmp & 0x1f) != 0x15) { + /* FPGA failed to be programmed */ + dev_err(emu->card->dev, + "emu1010: Loading Audio Dock Firmware failed, reg = 0x%x\n", + tmp); return; -#endif + } + dev_info(emu->card->dev, "emu1010: Audio Dock Firmware loaded\n"); + + snd_emu1010_fpga_read(emu, EMU_DOCK_MAJOR_REV, &tmp); + snd_emu1010_fpga_read(emu, EMU_DOCK_MINOR_REV, &tmp2); + dev_info(emu->card->dev, "Audio Dock ver: %u.%u\n", tmp, tmp2); + + /* Allow DLL to settle, to sync clocking between 1010 and Dock */ + msleep(10); +} + +static void emu1010_dock_event(struct snd_emu10k1 *emu) +{ + u32 reg; + snd_emu1010_fpga_read(emu, EMU_HANA_OPTION_CARDS, ®); /* OPTIONS: Which cards are attached to the EMU */ if (reg & EMU_HANA_OPTION_DOCK_OFFLINE) { /* Audio Dock attached */ - /* Return to Audio Dock programming mode */ - dev_info(emu->card->dev, - "emu1010: Loading Audio Dock Firmware\n"); - snd_emu1010_fpga_write(emu, EMU_HANA_FPGA_CONFIG, - EMU_HANA_FPGA_CONFIG_AUDIODOCK); - err = snd_emu1010_load_firmware(emu, 1, &emu->dock_fw); - if (err < 0) - return; - snd_emu1010_fpga_write(emu, EMU_HANA_FPGA_CONFIG, 0); - snd_emu1010_fpga_read(emu, EMU_HANA_ID, &tmp); - dev_info(emu->card->dev, - "emu1010: EMU_HANA+DOCK_ID = 0x%x\n", tmp); - if ((tmp & 0x1f) != 0x15) { - /* FPGA failed to be programmed */ - dev_info(emu->card->dev, - "emu1010: Loading Audio Dock Firmware file failed, reg = 0x%x\n", - tmp); - return; - } - dev_info(emu->card->dev, - "emu1010: Audio Dock Firmware loaded\n"); - snd_emu1010_fpga_read(emu, EMU_DOCK_MAJOR_REV, &tmp); - snd_emu1010_fpga_read(emu, EMU_DOCK_MINOR_REV, &tmp2); - dev_info(emu->card->dev, "Audio Dock ver: %u.%u\n", tmp, tmp2); - /* Sync clocking between 1010 and Dock */ - /* Allow DLL to settle */ - msleep(10); + snd_emu1010_load_dock_firmware(emu); /* Unmute all. Default is muted after a firmware load */ snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_UNMUTE); + } else if (!(reg & EMU_HANA_OPTION_DOCK_ONLINE)) { + /* Audio Dock removed */ + dev_info(emu->card->dev, "emu1010: Audio Dock detached\n"); + /* The hardware auto-mutes all, so we unmute again */ + snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_UNMUTE); } } -static void emu1010_clock_work(struct work_struct *work) +static void emu1010_clock_event(struct snd_emu10k1 *emu) { - struct snd_emu10k1 *emu; struct snd_ctl_elem_id id; - emu = container_of(work, struct snd_emu10k1, - emu1010.clock_work); - if (emu->card->shutdown) - return; -#ifdef CONFIG_PM_SLEEP - if (emu->suspend) - return; -#endif - spin_lock_irq(&emu->reg_lock); // This is the only thing that can actually happen. emu->emu1010.clock_source = emu->emu1010.clock_fallback; @@ -805,21 +755,44 @@ static void emu1010_clock_work(struct work_struct *work) snd_ctl_notify(emu->card, SNDRV_CTL_EVENT_MASK_VALUE, &id); } -static void emu1010_interrupt(struct snd_emu10k1 *emu) +static void emu1010_work(struct work_struct *work) { + struct snd_emu10k1 *emu; u32 sts; + emu = container_of(work, struct snd_emu10k1, emu1010.work); + if (emu->card->shutdown) + return; +#ifdef CONFIG_PM_SLEEP + if (emu->suspend) + return; +#endif + + snd_emu1010_fpga_lock(emu); + snd_emu1010_fpga_read(emu, EMU_HANA_IRQ_STATUS, &sts); - if (sts & EMU_HANA_IRQ_DOCK_LOST) { - /* Audio Dock removed */ - dev_info(emu->card->dev, "emu1010: Audio Dock detached\n"); - /* The hardware auto-mutes all, so we unmute again */ - snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_UNMUTE); - } else if (sts & EMU_HANA_IRQ_DOCK) { - schedule_work(&emu->emu1010.firmware_work); - } + + // The distinction of the IRQ status bits is unreliable, + // so we dispatch later based on option card status. + if (sts & (EMU_HANA_IRQ_DOCK | EMU_HANA_IRQ_DOCK_LOST)) + emu1010_dock_event(emu); + if (sts & EMU_HANA_IRQ_WCLK_CHANGED) - schedule_work(&emu->emu1010.clock_work); + emu1010_clock_event(emu); + + snd_emu1010_fpga_unlock(emu); +} + +static void emu1010_interrupt(struct snd_emu10k1 *emu) +{ + // We get an interrupt on each GPIO input pin change, but we + // care only about the ones triggered by the dedicated pin. + u16 sts = inw(emu->port + A_GPIO); + u16 bit = emu->card_capabilities->ca0108_chip ? 0x2000 : 0x8000; + if (!(sts & bit)) + return; + + schedule_work(&emu->emu1010.work); } /* @@ -841,32 +814,13 @@ static int snd_emu10k1_emu1010_init(struct snd_emu10k1 *emu) * Proper init follows in snd_emu10k1_init(). */ outl(HCFG_LOCKSOUNDCACHE | HCFG_LOCKTANKCACHE_MASK, emu->port + HCFG); - /* Disable 48Volt power to Audio Dock */ - snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_PWR, 0); - - /* ID, should read & 0x7f = 0x55. (Bit 7 is the IRQ bit) */ - snd_emu1010_fpga_read(emu, EMU_HANA_ID, ®); - dev_dbg(emu->card->dev, "reg1 = 0x%x\n", reg); - if ((reg & 0x3f) == 0x15) { - /* FPGA netlist already present so clear it */ - /* Return to programming mode */ - - snd_emu1010_fpga_write(emu, EMU_HANA_FPGA_CONFIG, EMU_HANA_FPGA_CONFIG_HANA); - } - snd_emu1010_fpga_read(emu, EMU_HANA_ID, ®); - dev_dbg(emu->card->dev, "reg2 = 0x%x\n", reg); - if ((reg & 0x3f) == 0x15) { - /* FPGA failed to return to programming mode */ - dev_info(emu->card->dev, - "emu1010: FPGA failed to return to programming mode\n"); - return -ENODEV; - } - dev_info(emu->card->dev, "emu1010: EMU_HANA_ID = 0x%x\n", reg); + snd_emu1010_fpga_lock(emu); + dev_info(emu->card->dev, "emu1010: Loading Hana Firmware\n"); err = snd_emu1010_load_firmware(emu, 0, &emu->firmware); if (err < 0) { dev_info(emu->card->dev, "emu1010: Loading Firmware failed\n"); - return err; + goto fail; } /* ID, should read & 0x7f = 0x55 when FPGA programmed. */ @@ -876,7 +830,8 @@ static int snd_emu10k1_emu1010_init(struct snd_emu10k1 *emu) dev_info(emu->card->dev, "emu1010: Loading Hana Firmware file failed, reg = 0x%x\n", reg); - return -ENODEV; + err = -ENODEV; + goto fail; } dev_info(emu->card->dev, "emu1010: Hana Firmware loaded\n"); @@ -889,7 +844,7 @@ static int snd_emu10k1_emu1010_init(struct snd_emu10k1 *emu) snd_emu1010_fpga_read(emu, EMU_HANA_OPTION_CARDS, ®); dev_info(emu->card->dev, "emu1010: Card options = 0x%x\n", reg); if (reg & EMU_HANA_OPTION_DOCK_OFFLINE) - schedule_work(&emu->emu1010.firmware_work); + snd_emu1010_load_dock_firmware(emu); if (emu->card_capabilities->no_adat) { emu->emu1010.optical_in = 0; /* IN_SPDIF */ emu->emu1010.optical_out = 0; /* OUT_SPDIF */ @@ -936,7 +891,9 @@ static int snd_emu10k1_emu1010_init(struct snd_emu10k1 *emu) // so it is safe to simply enable the outputs. snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_UNMUTE); - return 0; +fail: + snd_emu1010_fpga_unlock(emu); + return err; } /* * Create the EMU10K1 instance @@ -958,10 +915,10 @@ static void snd_emu10k1_free(struct snd_card *card) } if (emu->card_capabilities->emu_model == EMU_MODEL_EMU1010) { /* Disable 48Volt power to Audio Dock */ - snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_PWR, 0); + snd_emu1010_fpga_write_lock(emu, EMU_HANA_DOCK_PWR, 0); } - cancel_work_sync(&emu->emu1010.firmware_work); - cancel_work_sync(&emu->emu1010.clock_work); + cancel_work_sync(&emu->emu1010.work); + mutex_destroy(&emu->emu1010.lock); release_firmware(emu->firmware); release_firmware(emu->dock_fw); snd_util_memhdr_free(emu->memhdr); @@ -1540,8 +1497,8 @@ int snd_emu10k1_create(struct snd_card *card, emu->irq = -1; emu->synth = NULL; emu->get_synth_voice = NULL; - INIT_WORK(&emu->emu1010.firmware_work, emu1010_firmware_work); - INIT_WORK(&emu->emu1010.clock_work, emu1010_clock_work); + INIT_WORK(&emu->emu1010.work, emu1010_work); + mutex_init(&emu->emu1010.lock); /* read revision & serial */ emu->revision = pci->revision; pci_read_config_dword(pci, PCI_SUBSYSTEM_VENDOR_ID, &emu->serial); diff --git a/sound/pci/emu10k1/emu10k1_patch.c b/sound/pci/emu10k1/emu10k1_patch.c index 89890f24509f..806b4f95cad1 100644 --- a/sound/pci/emu10k1/emu10k1_patch.c +++ b/sound/pci/emu10k1/emu10k1_patch.c @@ -16,7 +16,7 @@ #define BLANK_LOOP_START 4 #define BLANK_LOOP_END 8 #define BLANK_LOOP_SIZE 12 -#define BLANK_HEAD_SIZE 32 +#define BLANK_HEAD_SIZE 3 /* * allocate a sample block and copy data from userspace @@ -26,56 +26,77 @@ snd_emu10k1_sample_new(struct snd_emux *rec, struct snd_sf_sample *sp, struct snd_util_memhdr *hdr, const void __user *data, long count) { + u8 fill; + u32 xor; + int shift; int offset; int truesize, size, blocksize; - __maybe_unused int loopsize; - int loopend, sampleend; - unsigned int start_addr; + int loop_start, loop_end, loop_size, data_end, unroll; struct snd_emu10k1 *emu; emu = rec->hw; if (snd_BUG_ON(!sp || !hdr)) return -EINVAL; - if (sp->v.size == 0) { - dev_dbg(emu->card->dev, - "emu: rom font for sample %d\n", sp->v.sample); - return 0; + if (sp->v.mode_flags & (SNDRV_SFNT_SAMPLE_BIDIR_LOOP | SNDRV_SFNT_SAMPLE_REVERSE_LOOP)) { + /* should instead return -ENOTSUPP; but compatibility */ + dev_warn(emu->card->dev, + "Emu10k1 wavetable patch %d with unsupported loop feature\n", + sp->v.sample); } - /* recalculate address offset */ - sp->v.end -= sp->v.start; - sp->v.loopstart -= sp->v.start; - sp->v.loopend -= sp->v.start; - sp->v.start = 0; - - /* some samples have invalid data. the addresses are corrected in voice info */ - sampleend = sp->v.end; - if (sampleend > sp->v.size) - sampleend = sp->v.size; - loopend = sp->v.loopend; - if (loopend > sampleend) - loopend = sampleend; - - /* be sure loop points start < end */ - if (sp->v.loopstart >= sp->v.loopend) - swap(sp->v.loopstart, sp->v.loopend); + if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS) { + shift = 0; + fill = 0x80; + xor = (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_UNSIGNED) ? 0 : 0x80808080; + } else { + shift = 1; + fill = 0; + xor = (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_UNSIGNED) ? 0x80008000 : 0; + } /* compute true data size to be loaded */ truesize = sp->v.size + BLANK_HEAD_SIZE; - loopsize = 0; -#if 0 /* not supported */ - if (sp->v.mode_flags & (SNDRV_SFNT_SAMPLE_BIDIR_LOOP|SNDRV_SFNT_SAMPLE_REVERSE_LOOP)) - loopsize = sp->v.loopend - sp->v.loopstart; - truesize += loopsize; -#endif - if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_NO_BLANK) + if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_NO_BLANK) { truesize += BLANK_LOOP_SIZE; + /* if no blank loop is attached in the sample, add it */ + if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_SINGLESHOT) { + sp->v.loopstart = sp->v.end + BLANK_LOOP_START; + sp->v.loopend = sp->v.end + BLANK_LOOP_END; + } + } + + loop_start = sp->v.loopstart; + loop_end = sp->v.loopend; + loop_size = loop_end - loop_start; + if (!loop_size) + return -EINVAL; + data_end = sp->v.end; + + /* recalculate offset */ + sp->v.start += BLANK_HEAD_SIZE; + sp->v.end += BLANK_HEAD_SIZE; + sp->v.loopstart += BLANK_HEAD_SIZE; + sp->v.loopend += BLANK_HEAD_SIZE; + + // Automatic pre-filling of the cache does not work in the presence + // of loops (*), and we don't want to fill it manually, as that is + // fiddly and slow. So we unroll the loop until the loop end is + // beyond the cache size. + // (*) Strictly speaking, a single iteration is supported (that's + // how it works when the playback engine runs), but handling this + // special case is not worth it. + unroll = 0; + while (sp->v.loopend < 64) { + truesize += loop_size; + sp->v.loopstart += loop_size; + sp->v.loopend += loop_size; + sp->v.end += loop_size; + unroll++; + } /* try to allocate a memory block */ - blocksize = truesize; - if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS)) - blocksize *= 2; + blocksize = truesize << shift; sp->block = snd_emu10k1_synth_alloc(emu, blocksize); if (sp->block == NULL) { dev_dbg(emu->card->dev, @@ -88,110 +109,43 @@ snd_emu10k1_sample_new(struct snd_emux *rec, struct snd_sf_sample *sp, /* write blank samples at head */ offset = 0; - size = BLANK_HEAD_SIZE; - if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS)) - size *= 2; - if (offset + size > blocksize) - return -EINVAL; - snd_emu10k1_synth_bzero(emu, sp->block, offset, size); - offset += size; - - /* copy start->loopend */ - size = loopend; - if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS)) - size *= 2; - if (offset + size > blocksize) - return -EINVAL; - if (snd_emu10k1_synth_copy_from_user(emu, sp->block, offset, data, size)) { - snd_emu10k1_synth_free(emu, sp->block); - sp->block = NULL; - return -EFAULT; - } + size = BLANK_HEAD_SIZE << shift; + snd_emu10k1_synth_memset(emu, sp->block, offset, size, fill); offset += size; - data += size; - -#if 0 /* not supported yet */ - /* handle reverse (or bidirectional) loop */ - if (sp->v.mode_flags & (SNDRV_SFNT_SAMPLE_BIDIR_LOOP|SNDRV_SFNT_SAMPLE_REVERSE_LOOP)) { - /* copy loop in reverse */ - if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS)) { - int woffset; - unsigned short *wblock = (unsigned short*)block; - woffset = offset / 2; - if (offset + loopsize * 2 > blocksize) - return -EINVAL; - for (i = 0; i < loopsize; i++) - wblock[woffset + i] = wblock[woffset - i -1]; - offset += loopsize * 2; - } else { - if (offset + loopsize > blocksize) - return -EINVAL; - for (i = 0; i < loopsize; i++) - block[offset + i] = block[offset - i -1]; - offset += loopsize; - } - /* modify loop pointers */ - if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_BIDIR_LOOP) { - sp->v.loopend += loopsize; - } else { - sp->v.loopstart += loopsize; - sp->v.loopend += loopsize; + /* copy provided samples */ + if (unroll && loop_end <= data_end) { + size = loop_end << shift; + if (snd_emu10k1_synth_copy_from_user(emu, sp->block, offset, data, size, xor)) + goto faulty; + offset += size; + + data += loop_start << shift; + while (--unroll > 0) { + size = loop_size << shift; + if (snd_emu10k1_synth_copy_from_user(emu, sp->block, offset, data, size, xor)) + goto faulty; + offset += size; } - /* add sample pointer */ - sp->v.end += loopsize; - } -#endif - /* loopend -> sample end */ - size = sp->v.size - loopend; - if (size < 0) - return -EINVAL; - if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS)) - size *= 2; - if (snd_emu10k1_synth_copy_from_user(emu, sp->block, offset, data, size)) { - snd_emu10k1_synth_free(emu, sp->block); - sp->block = NULL; - return -EFAULT; + size = (data_end - loop_start) << shift; + } else { + size = data_end << shift; } + if (snd_emu10k1_synth_copy_from_user(emu, sp->block, offset, data, size, xor)) + goto faulty; offset += size; /* clear rest of samples (if any) */ if (offset < blocksize) - snd_emu10k1_synth_bzero(emu, sp->block, offset, blocksize - offset); - - if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_NO_BLANK) { - /* if no blank loop is attached in the sample, add it */ - if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_SINGLESHOT) { - sp->v.loopstart = sp->v.end + BLANK_LOOP_START; - sp->v.loopend = sp->v.end + BLANK_LOOP_END; - } - } - -#if 0 /* not supported yet */ - if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_UNSIGNED) { - /* unsigned -> signed */ - if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS)) { - unsigned short *wblock = (unsigned short*)block; - for (i = 0; i < truesize; i++) - wblock[i] ^= 0x8000; - } else { - for (i = 0; i < truesize; i++) - block[i] ^= 0x80; - } - } -#endif - - /* recalculate offset */ - start_addr = BLANK_HEAD_SIZE * 2; - if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS)) - start_addr >>= 1; - sp->v.start += start_addr; - sp->v.end += start_addr; - sp->v.loopstart += start_addr; - sp->v.loopend += start_addr; + snd_emu10k1_synth_memset(emu, sp->block, offset, blocksize - offset, fill); return 0; + +faulty: + snd_emu10k1_synth_free(emu, sp->block); + sp->block = NULL; + return -EFAULT; } /* diff --git a/sound/pci/emu10k1/emumixer.c b/sound/pci/emu10k1/emumixer.c index 0a32ea53d8c6..05b98d9b547b 100644 --- a/sound/pci/emu10k1/emumixer.c +++ b/sound/pci/emu10k1/emumixer.c @@ -661,7 +661,9 @@ static int snd_emu1010_output_source_put(struct snd_kcontrol *kcontrol, change = (emu->emu1010.output_source[channel] != val); if (change) { emu->emu1010.output_source[channel] = val; + snd_emu1010_fpga_lock(emu); snd_emu1010_output_source_apply(emu, channel, val); + snd_emu1010_fpga_unlock(emu); } return change; } @@ -705,7 +707,9 @@ static int snd_emu1010_input_source_put(struct snd_kcontrol *kcontrol, change = (emu->emu1010.input_source[channel] != val); if (change) { emu->emu1010.input_source[channel] = val; + snd_emu1010_fpga_lock(emu); snd_emu1010_input_source_apply(emu, channel, val); + snd_emu1010_fpga_unlock(emu); } return change; } @@ -774,7 +778,7 @@ static int snd_emu1010_adc_pads_put(struct snd_kcontrol *kcontrol, struct snd_ct cache = cache & ~mask; change = (cache != emu->emu1010.adc_pads); if (change) { - snd_emu1010_fpga_write(emu, EMU_HANA_ADC_PADS, cache ); + snd_emu1010_fpga_write_lock(emu, EMU_HANA_ADC_PADS, cache ); emu->emu1010.adc_pads = cache; } @@ -832,7 +836,7 @@ static int snd_emu1010_dac_pads_put(struct snd_kcontrol *kcontrol, struct snd_ct cache = cache & ~mask; change = (cache != emu->emu1010.dac_pads); if (change) { - snd_emu1010_fpga_write(emu, EMU_HANA_DAC_PADS, cache ); + snd_emu1010_fpga_write_lock(emu, EMU_HANA_DAC_PADS, cache ); emu->emu1010.dac_pads = cache; } @@ -980,6 +984,7 @@ static int snd_emu1010_clock_source_put(struct snd_kcontrol *kcontrol, val = ucontrol->value.enumerated.item[0] ; if (val >= emu_ci->num) return -EINVAL; + snd_emu1010_fpga_lock(emu); spin_lock_irq(&emu->reg_lock); change = (emu->emu1010.clock_source != val); if (change) { @@ -996,6 +1001,7 @@ static int snd_emu1010_clock_source_put(struct snd_kcontrol *kcontrol, } else { spin_unlock_irq(&emu->reg_lock); } + snd_emu1010_fpga_unlock(emu); return change; } @@ -1041,7 +1047,7 @@ static int snd_emu1010_clock_fallback_put(struct snd_kcontrol *kcontrol, change = (emu->emu1010.clock_fallback != val); if (change) { emu->emu1010.clock_fallback = val; - snd_emu1010_fpga_write(emu, EMU_HANA_DEFCLOCK, 1 - val); + snd_emu1010_fpga_write_lock(emu, EMU_HANA_DEFCLOCK, 1 - val); } return change; } @@ -1093,7 +1099,7 @@ static int snd_emu1010_optical_out_put(struct snd_kcontrol *kcontrol, emu->emu1010.optical_out = val; tmp = (emu->emu1010.optical_in ? EMU_HANA_OPTICAL_IN_ADAT : EMU_HANA_OPTICAL_IN_SPDIF) | (emu->emu1010.optical_out ? EMU_HANA_OPTICAL_OUT_ADAT : EMU_HANA_OPTICAL_OUT_SPDIF); - snd_emu1010_fpga_write(emu, EMU_HANA_OPTICAL_TYPE, tmp); + snd_emu1010_fpga_write_lock(emu, EMU_HANA_OPTICAL_TYPE, tmp); } return change; } @@ -1144,7 +1150,7 @@ static int snd_emu1010_optical_in_put(struct snd_kcontrol *kcontrol, emu->emu1010.optical_in = val; tmp = (emu->emu1010.optical_in ? EMU_HANA_OPTICAL_IN_ADAT : EMU_HANA_OPTICAL_IN_SPDIF) | (emu->emu1010.optical_out ? EMU_HANA_OPTICAL_OUT_ADAT : EMU_HANA_OPTICAL_OUT_SPDIF); - snd_emu1010_fpga_write(emu, EMU_HANA_OPTICAL_TYPE, tmp); + snd_emu1010_fpga_write_lock(emu, EMU_HANA_OPTICAL_TYPE, tmp); } return change; } @@ -2323,7 +2329,9 @@ int snd_emu10k1_mixer(struct snd_emu10k1 *emu, for (i = 0; i < emu_ri->n_outs; i++) emu->emu1010.output_source[i] = emu1010_map_source(emu_ri, emu_ri->out_dflts[i]); + snd_emu1010_fpga_lock(emu); snd_emu1010_apply_sources(emu); + snd_emu1010_fpga_unlock(emu); kctl = emu->ctl_clock_source = snd_ctl_new1(&snd_emu1010_clock_source, emu); err = snd_ctl_add(card, kctl); diff --git a/sound/pci/emu10k1/emupcm.c b/sound/pci/emu10k1/emupcm.c index 7f4c1b38d6ec..1bf6e3d652f8 100644 --- a/sound/pci/emu10k1/emupcm.c +++ b/sound/pci/emu10k1/emupcm.c @@ -147,16 +147,6 @@ static const struct snd_pcm_hw_constraint_list hw_constraints_capture_buffer_siz .mask = 0 }; -static const unsigned int capture_rates[8] = { - 8000, 11025, 16000, 22050, 24000, 32000, 44100, 48000 -}; - -static const struct snd_pcm_hw_constraint_list hw_constraints_capture_rates = { - .count = 8, - .list = capture_rates, - .mask = 0 -}; - static unsigned int snd_emu10k1_capture_rate_reg(unsigned int rate) { switch (rate) { @@ -174,16 +164,6 @@ static unsigned int snd_emu10k1_capture_rate_reg(unsigned int rate) } } -static const unsigned int audigy_capture_rates[9] = { - 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000 -}; - -static const struct snd_pcm_hw_constraint_list hw_constraints_audigy_capture_rates = { - .count = 9, - .list = audigy_capture_rates, - .mask = 0 -}; - static unsigned int snd_emu10k1_audigy_capture_rate_reg(unsigned int rate) { switch (rate) { @@ -207,17 +187,16 @@ static void snd_emu10k1_constrain_capture_rates(struct snd_emu10k1 *emu, { if (emu->card_capabilities->emu_model && emu->emu1010.word_clock == 44100) { - // This also sets the rate constraint by deleting SNDRV_PCM_RATE_KNOT runtime->hw.rates = SNDRV_PCM_RATE_11025 | \ SNDRV_PCM_RATE_22050 | \ SNDRV_PCM_RATE_44100; runtime->hw.rate_min = 11025; runtime->hw.rate_max = 44100; - return; + } else if (emu->audigy) { + runtime->hw.rates = SNDRV_PCM_RATE_8000_48000 | + SNDRV_PCM_RATE_12000 | + SNDRV_PCM_RATE_24000; } - snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, - emu->audigy ? &hw_constraints_audigy_capture_rates : - &hw_constraints_capture_rates); } static void snd_emu1010_constrain_efx_rate(struct snd_emu10k1 *emu, @@ -1053,7 +1032,7 @@ static const struct snd_pcm_hardware snd_emu10k1_capture = SNDRV_PCM_INFO_RESUME | SNDRV_PCM_INFO_MMAP_VALID), .formats = SNDRV_PCM_FMTBIT_S16_LE, - .rates = SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_KNOT, + .rates = SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_24000, .rate_min = 8000, .rate_max = 48000, .channels_min = 1, diff --git a/sound/pci/emu10k1/emuproc.c b/sound/pci/emu10k1/emuproc.c index 2f80fd91017c..bd4734dc04cd 100644 --- a/sound/pci/emu10k1/emuproc.c +++ b/sound/pci/emu10k1/emuproc.c @@ -11,6 +11,7 @@ #include <linux/slab.h> #include <linux/init.h> +#include <linux/string_choices.h> #include <sound/core.h> #include <sound/emu10k1.h> #include "p16v.h" @@ -32,9 +33,9 @@ static void snd_emu10k1_proc_spdif_status(struct snd_emu10k1 * emu, snd_iprintf(buffer, "\n%s\n", title); if (status != 0xffffffff) { - snd_iprintf(buffer, "Professional Mode : %s\n", (status & SPCS_PROFESSIONAL) ? "yes" : "no"); - snd_iprintf(buffer, "Not Audio Data : %s\n", (status & SPCS_NOTAUDIODATA) ? "yes" : "no"); - snd_iprintf(buffer, "Copyright : %s\n", (status & SPCS_COPYRIGHT) ? "yes" : "no"); + snd_iprintf(buffer, "Professional Mode : %s\n", str_yes_no(status & SPCS_PROFESSIONAL)); + snd_iprintf(buffer, "Not Audio Data : %s\n", str_yes_no(status & SPCS_NOTAUDIODATA)); + snd_iprintf(buffer, "Copyright : %s\n", str_yes_no(status & SPCS_COPYRIGHT)); snd_iprintf(buffer, "Emphasis : %s\n", emphasis[(status & SPCS_EMPHASISMASK) >> 3]); snd_iprintf(buffer, "Mode : %i\n", (status & SPCS_MODEMASK) >> 6); snd_iprintf(buffer, "Category Code : 0x%x\n", (status & SPCS_CATEGORYCODEMASK) >> 8); @@ -46,9 +47,9 @@ static void snd_emu10k1_proc_spdif_status(struct snd_emu10k1 * emu, if (rate_reg > 0) { rate = snd_emu10k1_ptr_read(emu, rate_reg, 0); - snd_iprintf(buffer, "S/PDIF Valid : %s\n", rate & SRCS_SPDIFVALID ? "on" : "off"); - snd_iprintf(buffer, "S/PDIF Locked : %s\n", rate & SRCS_SPDIFLOCKED ? "on" : "off"); - snd_iprintf(buffer, "Rate Locked : %s\n", rate & SRCS_RATELOCKED ? "on" : "off"); + snd_iprintf(buffer, "S/PDIF Valid : %s\n", str_on_off(rate & SRCS_SPDIFVALID)); + snd_iprintf(buffer, "S/PDIF Locked : %s\n", str_on_off(rate & SRCS_SPDIFLOCKED)); + snd_iprintf(buffer, "Rate Locked : %s\n", str_on_off(rate & SRCS_RATELOCKED)); /* From ((Rate * 48000 ) / 262144); */ snd_iprintf(buffer, "Estimated Sample Rate : %d\n", ((rate & 0xFFFFF ) * 375) >> 11); } @@ -165,6 +166,8 @@ static void snd_emu10k1_proc_spdif_read(struct snd_info_entry *entry, u32 value2; if (emu->card_capabilities->emu_model) { + snd_emu1010_fpga_lock(emu); + // This represents the S/PDIF lock status on 0404b, which is // kinda weird and unhelpful, because monitoring it via IRQ is // impractical (one gets an IRQ flood as long as it is desynced). @@ -197,6 +200,8 @@ static void snd_emu10k1_proc_spdif_read(struct snd_info_entry *entry, snd_iprintf(buffer, "\nS/PDIF mode: %s%s\n", value & EMU_HANA_SPDIF_MODE_RX_PRO ? "professional" : "consumer", value & EMU_HANA_SPDIF_MODE_RX_NOCOPY ? ", no copy" : ""); + + snd_emu1010_fpga_unlock(emu); } else { snd_emu10k1_proc_spdif_status(emu, buffer, "CD-ROM S/PDIF In", CDCS, CDSRCS); snd_emu10k1_proc_spdif_status(emu, buffer, "Optical or Coax S/PDIF In", GPSCS, GPSRCS); @@ -204,7 +209,7 @@ static void snd_emu10k1_proc_spdif_read(struct snd_info_entry *entry, #if 0 val = snd_emu10k1_ptr_read(emu, ZVSRCS, 0); snd_iprintf(buffer, "\nZoomed Video\n"); - snd_iprintf(buffer, "Rate Locked : %s\n", val & SRCS_RATELOCKED ? "on" : "off"); + snd_iprintf(buffer, "Rate Locked : %s\n", str_on_off(val & SRCS_RATELOCKED)); snd_iprintf(buffer, "Estimated Sample Rate : 0x%x\n", val & SRCS_ESTSAMPLERATE); #endif } @@ -458,6 +463,9 @@ static void snd_emu_proc_emu1010_reg_read(struct snd_info_entry *entry, struct snd_emu10k1 *emu = entry->private_data; u32 value; int i; + + snd_emu1010_fpga_lock(emu); + snd_iprintf(buffer, "EMU1010 Registers:\n\n"); for(i = 0; i < 0x40; i+=1) { @@ -496,6 +504,8 @@ static void snd_emu_proc_emu1010_reg_read(struct snd_info_entry *entry, snd_emu_proc_emu1010_link_read(emu, buffer, 0x701); } } + + snd_emu1010_fpga_unlock(emu); } static void snd_emu_proc_io_reg_read(struct snd_info_entry *entry, diff --git a/sound/pci/emu10k1/io.c b/sound/pci/emu10k1/io.c index 74df2330015f..b60ab5671e00 100644 --- a/sound/pci/emu10k1/io.c +++ b/sound/pci/emu10k1/io.c @@ -285,24 +285,33 @@ static void snd_emu1010_fpga_write_locked(struct snd_emu10k1 *emu, u32 reg, u32 outw(value, emu->port + A_GPIO); udelay(10); outw(value | 0x80 , emu->port + A_GPIO); /* High bit clocks the value into the fpga. */ + udelay(10); } void snd_emu1010_fpga_write(struct snd_emu10k1 *emu, u32 reg, u32 value) { - unsigned long flags; + if (snd_BUG_ON(!mutex_is_locked(&emu->emu1010.lock))) + return; + snd_emu1010_fpga_write_locked(emu, reg, value); +} - spin_lock_irqsave(&emu->emu_lock, flags); +void snd_emu1010_fpga_write_lock(struct snd_emu10k1 *emu, u32 reg, u32 value) +{ + snd_emu1010_fpga_lock(emu); snd_emu1010_fpga_write_locked(emu, reg, value); - spin_unlock_irqrestore(&emu->emu_lock, flags); + snd_emu1010_fpga_unlock(emu); } -static void snd_emu1010_fpga_read_locked(struct snd_emu10k1 *emu, u32 reg, u32 *value) +void snd_emu1010_fpga_read(struct snd_emu10k1 *emu, u32 reg, u32 *value) { // The higest input pin is used as the designated interrupt trigger, // so it needs to be masked out. // But note that any other input pin change will also cause an IRQ, // so using this function often causes an IRQ as a side effect. u32 mask = emu->card_capabilities->ca0108_chip ? 0x1f : 0x7f; + + if (snd_BUG_ON(!mutex_is_locked(&emu->emu1010.lock))) + return; if (snd_BUG_ON(reg > 0x3f)) return; reg += 0x40; /* 0x40 upwards are registers. */ @@ -313,47 +322,31 @@ static void snd_emu1010_fpga_read_locked(struct snd_emu10k1 *emu, u32 reg, u32 * *value = ((inw(emu->port + A_GPIO) >> 8) & mask); } -void snd_emu1010_fpga_read(struct snd_emu10k1 *emu, u32 reg, u32 *value) -{ - unsigned long flags; - - spin_lock_irqsave(&emu->emu_lock, flags); - snd_emu1010_fpga_read_locked(emu, reg, value); - spin_unlock_irqrestore(&emu->emu_lock, flags); -} - /* Each Destination has one and only one Source, * but one Source can feed any number of Destinations simultaneously. */ void snd_emu1010_fpga_link_dst_src_write(struct snd_emu10k1 *emu, u32 dst, u32 src) { - unsigned long flags; - if (snd_BUG_ON(dst & ~0x71f)) return; if (snd_BUG_ON(src & ~0x71f)) return; - spin_lock_irqsave(&emu->emu_lock, flags); - snd_emu1010_fpga_write_locked(emu, EMU_HANA_DESTHI, dst >> 8); - snd_emu1010_fpga_write_locked(emu, EMU_HANA_DESTLO, dst & 0x1f); - snd_emu1010_fpga_write_locked(emu, EMU_HANA_SRCHI, src >> 8); - snd_emu1010_fpga_write_locked(emu, EMU_HANA_SRCLO, src & 0x1f); - spin_unlock_irqrestore(&emu->emu_lock, flags); + snd_emu1010_fpga_write(emu, EMU_HANA_DESTHI, dst >> 8); + snd_emu1010_fpga_write(emu, EMU_HANA_DESTLO, dst & 0x1f); + snd_emu1010_fpga_write(emu, EMU_HANA_SRCHI, src >> 8); + snd_emu1010_fpga_write(emu, EMU_HANA_SRCLO, src & 0x1f); } u32 snd_emu1010_fpga_link_dst_src_read(struct snd_emu10k1 *emu, u32 dst) { - unsigned long flags; u32 hi, lo; if (snd_BUG_ON(dst & ~0x71f)) return 0; - spin_lock_irqsave(&emu->emu_lock, flags); - snd_emu1010_fpga_write_locked(emu, EMU_HANA_DESTHI, dst >> 8); - snd_emu1010_fpga_write_locked(emu, EMU_HANA_DESTLO, dst & 0x1f); - snd_emu1010_fpga_read_locked(emu, EMU_HANA_SRCHI, &hi); - snd_emu1010_fpga_read_locked(emu, EMU_HANA_SRCLO, &lo); - spin_unlock_irqrestore(&emu->emu_lock, flags); + snd_emu1010_fpga_write(emu, EMU_HANA_DESTHI, dst >> 8); + snd_emu1010_fpga_write(emu, EMU_HANA_DESTLO, dst & 0x1f); + snd_emu1010_fpga_read(emu, EMU_HANA_SRCHI, &hi); + snd_emu1010_fpga_read(emu, EMU_HANA_SRCLO, &lo); return (hi << 8) | lo; } @@ -429,6 +422,59 @@ void snd_emu1010_update_clock(struct snd_emu10k1 *emu) snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_LEDS_2, leds); } +void snd_emu1010_load_firmware_entry(struct snd_emu10k1 *emu, int dock, + const struct firmware *fw_entry) +{ + __always_unused u16 write_post; + + // On E-MU 1010 rev1 the FPGA is a Xilinx Spartan IIE XC2S50E. + // On E-MU 0404b it is a Xilinx Spartan III XC3S50. + // The wiring is as follows: + // GPO7 -> FPGA input & 1K resistor -> FPGA /PGMN <- FPGA output + // In normal operation, the active low reset line is held up by + // an FPGA output, while the GPO pin performs its duty as control + // register access strobe signal. Writing the respective bit to + // EMU_HANA_FPGA_CONFIG puts the FPGA output into high-Z mode, at + // which point the GPO pin can control the reset line through the + // resistor. + // GPO6 -> FPGA CCLK & FPGA input + // GPO5 -> FPGA DIN (dual function) + + // If the FPGA is already programmed, return it to programming mode + snd_emu1010_fpga_write(emu, EMU_HANA_FPGA_CONFIG, + dock ? EMU_HANA_FPGA_CONFIG_AUDIODOCK : + EMU_HANA_FPGA_CONFIG_HANA); + + // Assert reset line for 100uS + outw(0x00, emu->port + A_GPIO); + write_post = inw(emu->port + A_GPIO); + udelay(100); + outw(0x80, emu->port + A_GPIO); + write_post = inw(emu->port + A_GPIO); + udelay(100); // Allow FPGA memory to clean + + // Upload the netlist. Keep reset line high! + for (int n = 0; n < fw_entry->size; n++) { + u8 value = fw_entry->data[n]; + for (int i = 0; i < 8; i++) { + u16 reg = 0x80; + if (value & 1) + reg |= 0x20; + value >>= 1; + outw(reg, emu->port + A_GPIO); + write_post = inw(emu->port + A_GPIO); + outw(reg | 0x40, emu->port + A_GPIO); + write_post = inw(emu->port + A_GPIO); + } + } + + // After programming, set GPIO bit 4 high again. + // This appears to be a config word that the rev1 Hana + // firmware reads; weird things happen without this. + outw(0x10, emu->port + A_GPIO); + write_post = inw(emu->port + A_GPIO); +} + void snd_emu10k1_intr_enable(struct snd_emu10k1 *emu, unsigned int intrenb) { unsigned long flags; diff --git a/sound/pci/emu10k1/memory.c b/sound/pci/emu10k1/memory.c index 20b07117574b..d29711777161 100644 --- a/sound/pci/emu10k1/memory.c +++ b/sound/pci/emu10k1/memory.c @@ -565,15 +565,18 @@ static inline void *offset_ptr(struct snd_emu10k1 *emu, int page, int offset) } /* - * bzero(blk + offset, size) + * memset(blk + offset, value, size) */ -int snd_emu10k1_synth_bzero(struct snd_emu10k1 *emu, struct snd_util_memblk *blk, - int offset, int size) +int snd_emu10k1_synth_memset(struct snd_emu10k1 *emu, struct snd_util_memblk *blk, + int offset, int size, u8 value) { int page, nextofs, end_offset, temp, temp1; void *ptr; struct snd_emu10k1_memblk *p = (struct snd_emu10k1_memblk *)blk; + if (snd_BUG_ON(offset + size > p->mem.size)) + return -EFAULT; + offset += blk->offset & (PAGE_SIZE - 1); end_offset = offset + size; page = get_aligned_page(offset); @@ -585,25 +588,55 @@ int snd_emu10k1_synth_bzero(struct snd_emu10k1 *emu, struct snd_util_memblk *blk temp = temp1; ptr = offset_ptr(emu, page + p->first_page, offset); if (ptr) - memset(ptr, 0, temp); + memset(ptr, value, temp); offset = nextofs; page++; } while (offset < end_offset); return 0; } -EXPORT_SYMBOL(snd_emu10k1_synth_bzero); +EXPORT_SYMBOL(snd_emu10k1_synth_memset); + +// Note that the value is assumed to be suitably repetitive. +static void xor_range(void *ptr, int size, u32 value) +{ + if ((long)ptr & 1) { + *(u8 *)ptr ^= (u8)value; + ptr++; + size--; + } + if (size > 1 && ((long)ptr & 2)) { + *(u16 *)ptr ^= (u16)value; + ptr += 2; + size -= 2; + } + while (size > 3) { + *(u32 *)ptr ^= value; + ptr += 4; + size -= 4; + } + if (size > 1) { + *(u16 *)ptr ^= (u16)value; + ptr += 2; + size -= 2; + } + if (size > 0) + *(u8 *)ptr ^= (u8)value; +} /* - * copy_from_user(blk + offset, data, size) + * copy_from_user(blk + offset, data, size) ^ xor */ int snd_emu10k1_synth_copy_from_user(struct snd_emu10k1 *emu, struct snd_util_memblk *blk, - int offset, const char __user *data, int size) + int offset, const char __user *data, int size, u32 xor) { int page, nextofs, end_offset, temp, temp1; void *ptr; struct snd_emu10k1_memblk *p = (struct snd_emu10k1_memblk *)blk; + if (snd_BUG_ON(offset + size > p->mem.size)) + return -EFAULT; + offset += blk->offset & (PAGE_SIZE - 1); end_offset = offset + size; page = get_aligned_page(offset); @@ -614,8 +647,12 @@ int snd_emu10k1_synth_copy_from_user(struct snd_emu10k1 *emu, struct snd_util_me if (temp1 < temp) temp = temp1; ptr = offset_ptr(emu, page + p->first_page, offset); - if (ptr && copy_from_user(ptr, data, temp)) - return -EFAULT; + if (ptr) { + if (copy_from_user(ptr, data, temp)) + return -EFAULT; + if (xor) + xor_range(ptr, temp, xor); + } offset = nextofs; data += temp; page++; diff --git a/sound/pci/emu10k1/p16v.c b/sound/pci/emu10k1/p16v.c index e7f097cae574..a9a75891f1da 100644 --- a/sound/pci/emu10k1/p16v.c +++ b/sound/pci/emu10k1/p16v.c @@ -174,11 +174,6 @@ static int snd_p16v_pcm_open_playback_channel(struct snd_pcm_substream *substrea if (err < 0) return err; - runtime->sync.id32[0] = substream->pcm->card->number; - runtime->sync.id32[1] = 'P'; - runtime->sync.id32[2] = 16; - runtime->sync.id32[3] = 'V'; - return 0; } @@ -226,6 +221,17 @@ static int snd_p16v_pcm_open_capture(struct snd_pcm_substream *substream) return snd_p16v_pcm_open_capture_channel(substream, 0); } +static int snd_p16v_pcm_ioctl_playback(struct snd_pcm_substream *substream, + unsigned int cmd, void *arg) +{ + if (cmd == SNDRV_PCM_IOCTL1_SYNC_ID) { + static const unsigned char id[4] = { 'P', '1', '6', 'V' }; + snd_pcm_set_sync_per_card(substream, arg, id, 4); + return 0; + } + return snd_pcm_lib_ioctl(substream, cmd, arg); +} + /* prepare playback callback */ static int snd_p16v_pcm_prepare_playback(struct snd_pcm_substream *substream) { @@ -531,6 +537,7 @@ snd_p16v_pcm_pointer_capture(struct snd_pcm_substream *substream) static const struct snd_pcm_ops snd_p16v_playback_front_ops = { .open = snd_p16v_pcm_open_playback_front, .close = snd_p16v_pcm_close_playback, + .ioctl = snd_p16v_pcm_ioctl_playback, .prepare = snd_p16v_pcm_prepare_playback, .trigger = snd_p16v_pcm_trigger_playback, .pointer = snd_p16v_pcm_pointer_playback, diff --git a/sound/pci/ens1370.c b/sound/pci/ens1370.c index 89210b2c7342..71c89e4e3090 100644 --- a/sound/pci/ens1370.c +++ b/sound/pci/ens1370.c @@ -1850,12 +1850,12 @@ static void snd_ensoniq_proc_read(struct snd_info_entry *entry, snd_iprintf(buffer, "Ensoniq AudioPCI " CHIP_NAME "\n\n"); snd_iprintf(buffer, "Joystick enable : %s\n", - ensoniq->ctrl & ES_JYSTK_EN ? "on" : "off"); + str_on_off(ensoniq->ctrl & ES_JYSTK_EN)); #ifdef CHIP1370 snd_iprintf(buffer, "MIC +5V bias : %s\n", - ensoniq->ctrl & ES_1370_XCTL1 ? "on" : "off"); + str_on_off(ensoniq->ctrl & ES_1370_XCTL1)); snd_iprintf(buffer, "Line In to AOUT : %s\n", - ensoniq->ctrl & ES_1370_XCTL0 ? "on" : "off"); + str_on_off(ensoniq->ctrl & ES_1370_XCTL0)); #else snd_iprintf(buffer, "Joystick port : 0x%x\n", (ES_1371_JOY_ASELI(ensoniq->ctrl) * 8) + 0x200); @@ -1968,7 +1968,6 @@ static void snd_ensoniq_chip_init(struct ensoniq *ensoniq) outl(ensoniq->cssr, ES_REG(ensoniq, STATUS)); } -#ifdef CONFIG_PM_SLEEP static int snd_ensoniq_suspend(struct device *dev) { struct snd_card *card = dev_get_drvdata(dev); @@ -2007,11 +2006,7 @@ static int snd_ensoniq_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(snd_ensoniq_pm, snd_ensoniq_suspend, snd_ensoniq_resume); -#define SND_ENSONIQ_PM_OPS &snd_ensoniq_pm -#else -#define SND_ENSONIQ_PM_OPS NULL -#endif /* CONFIG_PM_SLEEP */ +static DEFINE_SIMPLE_DEV_PM_OPS(snd_ensoniq_pm, snd_ensoniq_suspend, snd_ensoniq_resume); static int snd_ensoniq_create(struct snd_card *card, struct pci_dev *pci) @@ -2380,7 +2375,7 @@ static struct pci_driver ens137x_driver = { .id_table = snd_audiopci_ids, .probe = snd_audiopci_probe, .driver = { - .pm = SND_ENSONIQ_PM_OPS, + .pm = &snd_ensoniq_pm, }, }; diff --git a/sound/pci/es1938.c b/sound/pci/es1938.c index ec598ba1a883..018a8d53ca53 100644 --- a/sound/pci/es1938.c +++ b/sound/pci/es1938.c @@ -216,9 +216,7 @@ struct es1938 { #ifdef SUPPORT_JOYSTICK struct gameport *gameport; #endif -#ifdef CONFIG_PM_SLEEP unsigned char saved_regs[SAVED_REG_SIZE]; -#endif }; static irqreturn_t snd_es1938_interrupt(int irq, void *dev_id); @@ -1395,7 +1393,6 @@ static void snd_es1938_chip_init(struct es1938 *chip) outb(0, SLDM_REG(chip, DMACLEAR)); } -#ifdef CONFIG_PM_SLEEP /* * PM support */ @@ -1461,11 +1458,7 @@ static int es1938_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(es1938_pm, es1938_suspend, es1938_resume); -#define ES1938_PM_OPS &es1938_pm -#else -#define ES1938_PM_OPS NULL -#endif /* CONFIG_PM_SLEEP */ +static DEFINE_SIMPLE_DEV_PM_OPS(es1938_pm, es1938_suspend, es1938_resume); #ifdef SUPPORT_JOYSTICK static int snd_es1938_create_gameport(struct es1938 *chip) @@ -1787,7 +1780,7 @@ static struct pci_driver es1938_driver = { .id_table = snd_es1938_ids, .probe = snd_es1938_probe, .driver = { - .pm = ES1938_PM_OPS, + .pm = &es1938_pm, }, }; diff --git a/sound/pci/es1968.c b/sound/pci/es1968.c index 4bc0f53c223b..c6c018b40c69 100644 --- a/sound/pci/es1968.c +++ b/sound/pci/es1968.c @@ -473,9 +473,7 @@ struct esschan { /* linked list */ struct list_head list; -#ifdef CONFIG_PM_SLEEP u16 wc_map[4]; -#endif }; struct es1968 { @@ -526,9 +524,7 @@ struct es1968 { struct list_head substream_list; spinlock_t substream_lock; -#ifdef CONFIG_PM_SLEEP u16 apu_map[NR_APUS][NR_APU_REGS]; -#endif #ifdef SUPPORT_JOYSTICK struct gameport *gameport; @@ -689,9 +685,7 @@ static void __apu_set_register(struct es1968 *chip, u16 channel, u8 reg, u16 dat { if (snd_BUG_ON(channel >= NR_APUS)) return; -#ifdef CONFIG_PM_SLEEP chip->apu_map[channel][reg] = data; -#endif reg |= (channel << 4); apu_index_set(chip, reg); apu_data_set(chip, data); @@ -976,9 +970,7 @@ static void snd_es1968_program_wavecache(struct es1968 *chip, struct esschan *es /* set the wavecache control reg */ wave_set_register(chip, es->apu[channel] << 3, tmpval); -#ifdef CONFIG_PM_SLEEP es->wc_map[channel] = tmpval; -#endif } @@ -2356,7 +2348,6 @@ static void snd_es1968_start_irq(struct es1968 *chip) outw(w, chip->io_port + ESM_PORT_HOST_IRQ); } -#ifdef CONFIG_PM_SLEEP /* * PM support */ @@ -2418,11 +2409,7 @@ static int es1968_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(es1968_pm, es1968_suspend, es1968_resume); -#define ES1968_PM_OPS &es1968_pm -#else -#define ES1968_PM_OPS NULL -#endif /* CONFIG_PM_SLEEP */ +static DEFINE_SIMPLE_DEV_PM_OPS(es1968_pm, es1968_suspend, es1968_resume); #ifdef SUPPORT_JOYSTICK #define JOYSTICK_ADDR 0x200 @@ -2852,7 +2839,7 @@ static struct pci_driver es1968_driver = { .id_table = snd_es1968_ids, .probe = snd_es1968_probe, .driver = { - .pm = ES1968_PM_OPS, + .pm = &es1968_pm, }, }; diff --git a/sound/pci/fm801.c b/sound/pci/fm801.c index 62b3cb126c6d..7f4834c2d5e6 100644 --- a/sound/pci/fm801.c +++ b/sound/pci/fm801.c @@ -222,9 +222,7 @@ struct fm801 { struct snd_tea575x tea; #endif -#ifdef CONFIG_PM_SLEEP u16 saved_regs[0x20]; -#endif }; /* @@ -1339,7 +1337,6 @@ static int snd_card_fm801_probe(struct pci_dev *pci, return snd_card_free_on_error(&pci->dev, __snd_card_fm801_probe(pci, pci_id)); } -#ifdef CONFIG_PM_SLEEP static const unsigned char saved_regs[] = { FM801_PCM_VOL, FM801_I2S_VOL, FM801_FM_VOL, FM801_REC_SRC, FM801_PLY_CTRL, FM801_PLY_COUNT, FM801_PLY_BUF1, FM801_PLY_BUF2, @@ -1396,18 +1393,14 @@ static int snd_fm801_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(snd_fm801_pm, snd_fm801_suspend, snd_fm801_resume); -#define SND_FM801_PM_OPS &snd_fm801_pm -#else -#define SND_FM801_PM_OPS NULL -#endif /* CONFIG_PM_SLEEP */ +static DEFINE_SIMPLE_DEV_PM_OPS(snd_fm801_pm, snd_fm801_suspend, snd_fm801_resume); static struct pci_driver fm801_driver = { .name = KBUILD_MODNAME, .id_table = snd_fm801_ids, .probe = snd_card_fm801_probe, .driver = { - .pm = SND_FM801_PM_OPS, + .pm = &snd_fm801_pm, }, }; diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig index 8e0ff70fb610..84ebf19f2883 100644 --- a/sound/pci/hda/Kconfig +++ b/sound/pci/hda/Kconfig @@ -116,6 +116,9 @@ config SND_HDA_CS_DSP_CONTROLS tristate select FW_CS_DSP +config SND_HDA_SCODEC_COMPONENT + tristate + config SND_HDA_SCODEC_CS35L41_I2C tristate "Build CS35L41 HD-audio side codec support for I2C Bus" depends on I2C @@ -125,6 +128,7 @@ config SND_HDA_SCODEC_CS35L41_I2C select SND_SOC_CS35L41_LIB select SND_HDA_SCODEC_CS35L41 select SND_HDA_CS_DSP_CONTROLS + select SND_SOC_CS_AMP_LIB help Say Y or M here to include CS35L41 I2C HD-audio side codec support in snd-hda-intel driver, such as ALC287. @@ -141,6 +145,7 @@ config SND_HDA_SCODEC_CS35L41_SPI select SND_SOC_CS35L41_LIB select SND_HDA_SCODEC_CS35L41 select SND_HDA_CS_DSP_CONTROLS + select SND_SOC_CS_AMP_LIB help Say Y or M here to include CS35L41 SPI HD-audio side codec support in snd-hda-intel driver, such as ALC287. @@ -157,11 +162,13 @@ config SND_HDA_SCODEC_CS35L56_I2C depends on ACPI || COMPILE_TEST depends on SND_SOC select FW_CS_DSP + imply SERIAL_MULTI_INSTANTIATE select SND_HDA_GENERIC select SND_SOC_CS35L56_SHARED select SND_HDA_SCODEC_CS35L56 select SND_HDA_CIRRUS_SCODEC select SND_HDA_CS_DSP_CONTROLS + select SND_SOC_CS_AMP_LIB help Say Y or M here to include CS35L56 amplifier support with I2C control. @@ -172,11 +179,13 @@ config SND_HDA_SCODEC_CS35L56_SPI depends on ACPI || COMPILE_TEST depends on SND_SOC select FW_CS_DSP + imply SERIAL_MULTI_INSTANTIATE select SND_HDA_GENERIC select SND_SOC_CS35L56_SHARED select SND_HDA_SCODEC_CS35L56 select SND_HDA_CIRRUS_SCODEC select SND_HDA_CS_DSP_CONTROLS + select SND_SOC_CS_AMP_LIB help Say Y or M here to include CS35L56 amplifier support with SPI control. @@ -189,7 +198,7 @@ config SND_HDA_SCODEC_TAS2781_I2C depends on SND_SOC select SND_SOC_TAS2781_COMLIB select SND_SOC_TAS2781_FMWLIB - select CRC32_SARWATE + select CRC32 help Say Y or M here to include TAS2781 I2C HD-audio side codec support in snd-hda-intel driver, such as ALC287. @@ -197,10 +206,26 @@ config SND_HDA_SCODEC_TAS2781_I2C comment "Set to Y if you want auto-loading the side codec driver" depends on SND_HDA=y && SND_HDA_SCODEC_TAS2781_I2C=m +config SND_HDA_SCODEC_TAS2781_SPI + tristate "Build TAS2781 HD-audio side codec support for SPI Bus" + depends on SPI_MASTER + depends on ACPI + depends on EFI + depends on SND_SOC + select CRC32 + help + Say Y or M here to include TAS2781 SPI HD-audio side codec support + in snd-hda-intel driver, such as ALC287. + +comment "Set to Y if you want auto-loading the side codec driver" + depends on SND_HDA=y && SND_HDA_SCODEC_TAS2781_SPI=m + config SND_HDA_CODEC_REALTEK tristate "Build Realtek HD-audio codec support" + depends on INPUT select SND_HDA_GENERIC select SND_HDA_GENERIC_LEDS + select SND_HDA_SCODEC_COMPONENT help Say Y or M here to include Realtek HD-audio codec support in snd-hda-intel driver, such as ALC880. @@ -284,6 +309,17 @@ config SND_HDA_CODEC_CONEXANT comment "Set to Y if you want auto-loading the codec driver" depends on SND_HDA=y && SND_HDA_CODEC_CONEXANT=m +config SND_HDA_CODEC_SENARYTECH + tristate "Build Senarytech HD-audio codec support" + select SND_HDA_GENERIC + select SND_HDA_GENERIC_LEDS + help + Say Y or M here to include Senarytech HD-audio codec support in + snd-hda-intel driver, such as SN6186. + +comment "Set to Y if you want auto-loading the codec driver" + depends on SND_HDA=y && SND_HDA_CODEC_SENARYTECH=m + config SND_HDA_CODEC_CA0110 tristate "Build Creative CA0110-IBG codec support" select SND_HDA_GENERIC diff --git a/sound/pci/hda/Makefile b/sound/pci/hda/Makefile index 793e296c3f64..210c406dfbc5 100644 --- a/sound/pci/hda/Makefile +++ b/sound/pci/hda/Makefile @@ -1,6 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 -snd-hda-intel-objs := hda_intel.o -snd-hda-tegra-objs := hda_tegra.o +snd-hda-intel-y := hda_intel.o +snd-hda-tegra-y := hda_tegra.o snd-hda-codec-y := hda_bind.o hda_codec.o hda_jack.o hda_auto_parser.o hda_sysfs.o snd-hda-codec-y += hda_controller.o @@ -13,31 +13,34 @@ snd-hda-codec-$(CONFIG_SND_HDA_INPUT_BEEP) += hda_beep.o CFLAGS_hda_controller.o := -I$(src) CFLAGS_hda_intel.o := -I$(src) -snd-hda-codec-generic-objs := hda_generic.o -snd-hda-codec-realtek-objs := patch_realtek.o -snd-hda-codec-cmedia-objs := patch_cmedia.o -snd-hda-codec-analog-objs := patch_analog.o -snd-hda-codec-idt-objs := patch_sigmatel.o -snd-hda-codec-si3054-objs := patch_si3054.o -snd-hda-codec-cirrus-objs := patch_cirrus.o -snd-hda-codec-cs8409-objs := patch_cs8409.o patch_cs8409-tables.o -snd-hda-codec-ca0110-objs := patch_ca0110.o -snd-hda-codec-ca0132-objs := patch_ca0132.o -snd-hda-codec-conexant-objs := patch_conexant.o -snd-hda-codec-via-objs := patch_via.o -snd-hda-codec-hdmi-objs := patch_hdmi.o hda_eld.o +snd-hda-codec-generic-y := hda_generic.o +snd-hda-codec-realtek-y := patch_realtek.o +snd-hda-codec-cmedia-y := patch_cmedia.o +snd-hda-codec-analog-y := patch_analog.o +snd-hda-codec-idt-y := patch_sigmatel.o +snd-hda-codec-si3054-y := patch_si3054.o +snd-hda-codec-cirrus-y := patch_cirrus.o +snd-hda-codec-cs8409-y := patch_cs8409.o patch_cs8409-tables.o +snd-hda-codec-ca0110-y := patch_ca0110.o +snd-hda-codec-ca0132-y := patch_ca0132.o +snd-hda-codec-conexant-y := patch_conexant.o +snd-hda-codec-senarytech-y :=patch_senarytech.o +snd-hda-codec-via-y := patch_via.o +snd-hda-codec-hdmi-y := patch_hdmi.o hda_eld.o # side codecs -snd-hda-cirrus-scodec-objs := cirrus_scodec.o -snd-hda-cirrus-scodec-test-objs := cirrus_scodec_test.o -snd-hda-scodec-cs35l41-objs := cs35l41_hda.o cs35l41_hda_property.o -snd-hda-scodec-cs35l41-i2c-objs := cs35l41_hda_i2c.o -snd-hda-scodec-cs35l41-spi-objs := cs35l41_hda_spi.o -snd-hda-scodec-cs35l56-objs := cs35l56_hda.o -snd-hda-scodec-cs35l56-i2c-objs := cs35l56_hda_i2c.o -snd-hda-scodec-cs35l56-spi-objs := cs35l56_hda_spi.o -snd-hda-cs-dsp-ctls-objs := hda_cs_dsp_ctl.o -snd-hda-scodec-tas2781-i2c-objs := tas2781_hda_i2c.o +snd-hda-cirrus-scodec-y := cirrus_scodec.o +snd-hda-cirrus-scodec-test-y := cirrus_scodec_test.o +snd-hda-scodec-cs35l41-y := cs35l41_hda.o cs35l41_hda_property.o +snd-hda-scodec-cs35l41-i2c-y := cs35l41_hda_i2c.o +snd-hda-scodec-cs35l41-spi-y := cs35l41_hda_spi.o +snd-hda-scodec-cs35l56-y := cs35l56_hda.o +snd-hda-scodec-cs35l56-i2c-y := cs35l56_hda_i2c.o +snd-hda-scodec-cs35l56-spi-y := cs35l56_hda_spi.o +snd-hda-cs-dsp-ctls-y := hda_cs_dsp_ctl.o +snd-hda-scodec-component-y := hda_component.o +snd-hda-scodec-tas2781-i2c-y := tas2781_hda_i2c.o +snd-hda-scodec-tas2781-spi-y := tas2781_hda_spi.o tas2781_spi_fwlib.o # common driver obj-$(CONFIG_SND_HDA) := snd-hda-codec.o @@ -54,6 +57,7 @@ obj-$(CONFIG_SND_HDA_CODEC_CS8409) += snd-hda-codec-cs8409.o obj-$(CONFIG_SND_HDA_CODEC_CA0110) += snd-hda-codec-ca0110.o obj-$(CONFIG_SND_HDA_CODEC_CA0132) += snd-hda-codec-ca0132.o obj-$(CONFIG_SND_HDA_CODEC_CONEXANT) += snd-hda-codec-conexant.o +obj-$(CONFIG_SND_HDA_CODEC_SENARYTECH) += snd-hda-codec-senarytech.o obj-$(CONFIG_SND_HDA_CODEC_VIA) += snd-hda-codec-via.o obj-$(CONFIG_SND_HDA_CODEC_HDMI) += snd-hda-codec-hdmi.o @@ -67,7 +71,9 @@ obj-$(CONFIG_SND_HDA_SCODEC_CS35L56) += snd-hda-scodec-cs35l56.o obj-$(CONFIG_SND_HDA_SCODEC_CS35L56_I2C) += snd-hda-scodec-cs35l56-i2c.o obj-$(CONFIG_SND_HDA_SCODEC_CS35L56_SPI) += snd-hda-scodec-cs35l56-spi.o obj-$(CONFIG_SND_HDA_CS_DSP_CONTROLS) += snd-hda-cs-dsp-ctls.o +obj-$(CONFIG_SND_HDA_SCODEC_COMPONENT) += snd-hda-scodec-component.o obj-$(CONFIG_SND_HDA_SCODEC_TAS2781_I2C) += snd-hda-scodec-tas2781-i2c.o +obj-$(CONFIG_SND_HDA_SCODEC_TAS2781_SPI) += snd-hda-scodec-tas2781-spi.o # this must be the last entry after codec drivers; # otherwise the codec patches won't be hooked before the PCI probe diff --git a/sound/pci/hda/cirrus_scodec.c b/sound/pci/hda/cirrus_scodec.c index 8de3bc7448fa..3c670207ba30 100644 --- a/sound/pci/hda/cirrus_scodec.c +++ b/sound/pci/hda/cirrus_scodec.c @@ -66,7 +66,7 @@ int cirrus_scodec_get_speaker_id(struct device *dev, int amp_index, return speaker_id; } -EXPORT_SYMBOL_NS_GPL(cirrus_scodec_get_speaker_id, SND_HDA_CIRRUS_SCODEC); +EXPORT_SYMBOL_NS_GPL(cirrus_scodec_get_speaker_id, "SND_HDA_CIRRUS_SCODEC"); MODULE_DESCRIPTION("HDA Cirrus side-codec library"); MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>"); diff --git a/sound/pci/hda/cirrus_scodec_test.c b/sound/pci/hda/cirrus_scodec_test.c index 8ae373676bd1..f5d6241daee4 100644 --- a/sound/pci/hda/cirrus_scodec_test.c +++ b/sound/pci/hda/cirrus_scodec_test.c @@ -365,6 +365,7 @@ static struct kunit_suite cirrus_scodec_test_suite = { kunit_test_suite(cirrus_scodec_test_suite); -MODULE_IMPORT_NS(SND_HDA_CIRRUS_SCODEC); +MODULE_IMPORT_NS("SND_HDA_CIRRUS_SCODEC"); +MODULE_DESCRIPTION("KUnit test for the Cirrus side-codec library"); MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>"); MODULE_LICENSE("GPL"); diff --git a/sound/pci/hda/cs35l41_hda.c b/sound/pci/hda/cs35l41_hda.c index d3fa6e136744..5dc021976c79 100644 --- a/sound/pci/hda/cs35l41_hda.c +++ b/sound/pci/hda/cs35l41_hda.c @@ -13,6 +13,7 @@ #include <sound/soc.h> #include <linux/pm_runtime.h> #include <linux/spi/spi.h> +#include <linux/vmalloc.h> #include "hda_local.h" #include "hda_auto_parser.h" #include "hda_jack.h" @@ -22,7 +23,6 @@ #include "hda_cs_dsp_ctl.h" #include "cs35l41_hda_property.h" -#define CS35L41_FIRMWARE_ROOT "cirrus/" #define CS35L41_PART "cs35l41" #define HALO_STATE_DSP_CTL_NAME "HALO_STATE" @@ -37,6 +37,42 @@ #define CS35L41_UUID "50d90cdc-3de4-4f18-b528-c7fe3b71f40d" #define CS35L41_DSM_GET_MUTE 5 #define CS35L41_NOTIFY_EVENT 0x91 +#define CS35L41_TUNING_SIG 0x109A4A35 + +enum cs35l41_tuning_param_types { + TUNING_PARAM_GAIN, +}; + +struct cs35l41_tuning_param_hdr { + __le32 tuning_index; + __le32 type; + __le32 size; +} __packed; + +struct cs35l41_tuning_param { + struct cs35l41_tuning_param_hdr hdr; + union { + __le32 gain; + }; +} __packed; + +struct cs35l41_tuning_params { + __le32 signature; + __le32 version; + __le32 size; + __le32 num_entries; + u8 data[]; +} __packed; + +/* Firmware calibration controls */ +static const struct cirrus_amp_cal_controls cs35l41_calibration_controls = { + .alg_id = CAL_DSP_CTL_ALG, + .mem_region = CAL_DSP_CTL_TYPE, + .ambient = CAL_AMBIENT_DSP_CTL_NAME, + .calr = CAL_R_DSP_CTL_NAME, + .status = CAL_STATUS_DSP_CTL_NAME, + .checksum = CAL_CHECKSUM_DSP_CTL_NAME, +}; static bool firmware_autostart = 1; module_param(firmware_autostart, bool, 0444); @@ -75,7 +111,7 @@ static const struct reg_sequence cs35l41_hda_config_dsp[] = { { CS35L41_SP_HIZ_CTRL, 0x00000003 }, // Hi-Z unused/disabled { CS35L41_SP_TX_WL, 0x00000018 }, // 24 cycles/slot { CS35L41_SP_RX_WL, 0x00000018 }, // 24 cycles/slot - { CS35L41_DAC_PCM1_SRC, 0x00000032 }, // DACPCM1_SRC = ERR_VOL + { CS35L41_DAC_PCM1_SRC, 0x00000032 }, // DACPCM1_SRC = DSP1TX1 { CS35L41_ASP_TX1_SRC, 0x00000018 }, // ASPTX1 SRC = VMON { CS35L41_ASP_TX2_SRC, 0x00000019 }, // ASPTX2 SRC = IMON { CS35L41_ASP_TX3_SRC, 0x00000028 }, // ASPTX3 SRC = VPMON @@ -84,7 +120,7 @@ static const struct reg_sequence cs35l41_hda_config_dsp[] = { { CS35L41_DSP1_RX2_SRC, 0x00000008 }, // DSP1RX2 SRC = ASPRX1 { CS35L41_DSP1_RX3_SRC, 0x00000018 }, // DSP1RX3 SRC = VMON { CS35L41_DSP1_RX4_SRC, 0x00000019 }, // DSP1RX4 SRC = IMON - { CS35L41_DSP1_RX5_SRC, 0x00000029 }, // DSP1RX5 SRC = VBSTMON + { CS35L41_DSP1_RX6_SRC, 0x00000029 }, // DSP1RX6 SRC = VBSTMON }; static const struct reg_sequence cs35l41_hda_unmute[] = { @@ -92,34 +128,39 @@ static const struct reg_sequence cs35l41_hda_unmute[] = { { CS35L41_AMP_GAIN_CTRL, 0x00000084 }, // AMP_GAIN_PCM 4.5 dB }; -static const struct reg_sequence cs35l41_hda_unmute_dsp[] = { - { CS35L41_AMP_DIG_VOL_CTRL, 0x00008000 }, // AMP_HPF_PCM_EN = 1, AMP_VOL_PCM 0.0 dB - { CS35L41_AMP_GAIN_CTRL, 0x00000233 }, // AMP_GAIN_PCM = 17.5dB AMP_GAIN_PDM = 19.5dB -}; - static const struct reg_sequence cs35l41_hda_mute[] = { { CS35L41_AMP_GAIN_CTRL, 0x00000000 }, // AMP_GAIN_PCM 0.5 dB { CS35L41_AMP_DIG_VOL_CTRL, 0x0000A678 }, // AMP_HPF_PCM_EN = 1, AMP_VOL_PCM Mute }; -static void cs35l41_add_controls(struct cs35l41_hda *cs35l41) +static const struct cs_dsp_client_ops client_ops = { + /* cs_dsp requires the client to provide this even if it is empty */ +}; + +static int cs35l41_request_tuning_param_file(struct cs35l41_hda *cs35l41, char *tuning_filename, + const struct firmware **firmware, char **filename, + const char *ssid) { - struct hda_cs_dsp_ctl_info info; + int ret = 0; - info.device_name = cs35l41->amp_name; - info.fw_type = cs35l41->firmware_type; - info.card = cs35l41->codec->card; + /* Filename is the same as the tuning file with "cfg" suffix */ + *filename = kasprintf(GFP_KERNEL, "%scfg", tuning_filename); + if (*filename == NULL) + return -ENOMEM; - hda_cs_dsp_add_controls(&cs35l41->cs_dsp, &info); -} + ret = firmware_request_nowarn(firmware, *filename, cs35l41->dev); + if (ret != 0) { + dev_dbg(cs35l41->dev, "Failed to request '%s'\n", *filename); + kfree(*filename); + *filename = NULL; + } -static const struct cs_dsp_client_ops client_ops = { - .control_remove = hda_cs_dsp_control_remove, -}; + return ret; +} static int cs35l41_request_firmware_file(struct cs35l41_hda *cs35l41, const struct firmware **firmware, char **filename, - const char *dir, const char *ssid, const char *amp_name, + const char *ssid, const char *amp_name, int spkid, const char *filetype) { const char * const dsp_name = cs35l41->cs_dsp.name; @@ -127,23 +168,23 @@ static int cs35l41_request_firmware_file(struct cs35l41_hda *cs35l41, int ret = 0; if (spkid > -1 && ssid && amp_name) - *filename = kasprintf(GFP_KERNEL, "%s%s-%s-%s-%s-spkid%d-%s.%s", dir, CS35L41_PART, + *filename = kasprintf(GFP_KERNEL, "cirrus/%s-%s-%s-%s-spkid%d-%s.%s", CS35L41_PART, dsp_name, hda_cs_dsp_fw_ids[cs35l41->firmware_type], ssid, spkid, amp_name, filetype); else if (spkid > -1 && ssid) - *filename = kasprintf(GFP_KERNEL, "%s%s-%s-%s-%s-spkid%d.%s", dir, CS35L41_PART, + *filename = kasprintf(GFP_KERNEL, "cirrus/%s-%s-%s-%s-spkid%d.%s", CS35L41_PART, dsp_name, hda_cs_dsp_fw_ids[cs35l41->firmware_type], ssid, spkid, filetype); else if (ssid && amp_name) - *filename = kasprintf(GFP_KERNEL, "%s%s-%s-%s-%s-%s.%s", dir, CS35L41_PART, + *filename = kasprintf(GFP_KERNEL, "cirrus/%s-%s-%s-%s-%s.%s", CS35L41_PART, dsp_name, hda_cs_dsp_fw_ids[cs35l41->firmware_type], ssid, amp_name, filetype); else if (ssid) - *filename = kasprintf(GFP_KERNEL, "%s%s-%s-%s-%s.%s", dir, CS35L41_PART, + *filename = kasprintf(GFP_KERNEL, "cirrus/%s-%s-%s-%s.%s", CS35L41_PART, dsp_name, hda_cs_dsp_fw_ids[cs35l41->firmware_type], ssid, filetype); else - *filename = kasprintf(GFP_KERNEL, "%s%s-%s-%s.%s", dir, CS35L41_PART, + *filename = kasprintf(GFP_KERNEL, "cirrus/%s-%s-%s.%s", CS35L41_PART, dsp_name, hda_cs_dsp_fw_ids[cs35l41->firmware_type], filetype); @@ -184,13 +225,11 @@ static int cs35l41_request_firmware_files_spkid(struct cs35l41_hda *cs35l41, /* try cirrus/part-dspN-fwtype-sub<-spkidN><-ampname>.wmfw */ ret = cs35l41_request_firmware_file(cs35l41, wmfw_firmware, wmfw_filename, - CS35L41_FIRMWARE_ROOT, cs35l41->acpi_subsystem_id, cs35l41->amp_name, cs35l41->speaker_id, "wmfw"); if (!ret) { /* try cirrus/part-dspN-fwtype-sub<-spkidN><-ampname>.bin */ ret = cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename, - CS35L41_FIRMWARE_ROOT, cs35l41->acpi_subsystem_id, cs35l41->amp_name, cs35l41->speaker_id, "bin"); if (ret) @@ -201,12 +240,11 @@ static int cs35l41_request_firmware_files_spkid(struct cs35l41_hda *cs35l41, /* try cirrus/part-dspN-fwtype-sub<-ampname>.wmfw */ ret = cs35l41_request_firmware_file(cs35l41, wmfw_firmware, wmfw_filename, - CS35L41_FIRMWARE_ROOT, cs35l41->acpi_subsystem_id, + cs35l41->acpi_subsystem_id, cs35l41->amp_name, -1, "wmfw"); if (!ret) { /* try cirrus/part-dspN-fwtype-sub<-spkidN><-ampname>.bin */ ret = cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename, - CS35L41_FIRMWARE_ROOT, cs35l41->acpi_subsystem_id, cs35l41->amp_name, cs35l41->speaker_id, "bin"); if (ret) @@ -217,18 +255,17 @@ static int cs35l41_request_firmware_files_spkid(struct cs35l41_hda *cs35l41, /* try cirrus/part-dspN-fwtype-sub<-spkidN>.wmfw */ ret = cs35l41_request_firmware_file(cs35l41, wmfw_firmware, wmfw_filename, - CS35L41_FIRMWARE_ROOT, cs35l41->acpi_subsystem_id, + cs35l41->acpi_subsystem_id, NULL, cs35l41->speaker_id, "wmfw"); if (!ret) { /* try cirrus/part-dspN-fwtype-sub<-spkidN><-ampname>.bin */ ret = cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename, - CS35L41_FIRMWARE_ROOT, cs35l41->acpi_subsystem_id, cs35l41->amp_name, cs35l41->speaker_id, "bin"); if (ret) /* try cirrus/part-dspN-fwtype-sub<-spkidN>.bin */ ret = cs35l41_request_firmware_file(cs35l41, coeff_firmware, - coeff_filename, CS35L41_FIRMWARE_ROOT, + coeff_filename, cs35l41->acpi_subsystem_id, NULL, cs35l41->speaker_id, "bin"); if (ret) @@ -239,18 +276,17 @@ static int cs35l41_request_firmware_files_spkid(struct cs35l41_hda *cs35l41, /* try cirrus/part-dspN-fwtype-sub.wmfw */ ret = cs35l41_request_firmware_file(cs35l41, wmfw_firmware, wmfw_filename, - CS35L41_FIRMWARE_ROOT, cs35l41->acpi_subsystem_id, + cs35l41->acpi_subsystem_id, NULL, -1, "wmfw"); if (!ret) { /* try cirrus/part-dspN-fwtype-sub<-spkidN><-ampname>.bin */ ret = cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename, - CS35L41_FIRMWARE_ROOT, cs35l41->acpi_subsystem_id, cs35l41->amp_name, cs35l41->speaker_id, "bin"); if (ret) /* try cirrus/part-dspN-fwtype-sub<-spkidN>.bin */ ret = cs35l41_request_firmware_file(cs35l41, coeff_firmware, - coeff_filename, CS35L41_FIRMWARE_ROOT, + coeff_filename, cs35l41->acpi_subsystem_id, NULL, cs35l41->speaker_id, "bin"); if (ret) @@ -277,13 +313,13 @@ static int cs35l41_fallback_firmware_file(struct cs35l41_hda *cs35l41, /* fallback try cirrus/part-dspN-fwtype.wmfw */ ret = cs35l41_request_firmware_file(cs35l41, wmfw_firmware, wmfw_filename, - CS35L41_FIRMWARE_ROOT, NULL, NULL, -1, "wmfw"); + NULL, NULL, -1, "wmfw"); if (ret) goto err; /* fallback try cirrus/part-dspN-fwtype.bin */ ret = cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename, - CS35L41_FIRMWARE_ROOT, NULL, NULL, -1, "bin"); + NULL, NULL, -1, "bin"); if (ret) { release_firmware(*wmfw_firmware); kfree(*wmfw_filename); @@ -312,12 +348,11 @@ static int cs35l41_request_firmware_files(struct cs35l41_hda *cs35l41, /* try cirrus/part-dspN-fwtype-sub<-ampname>.wmfw */ ret = cs35l41_request_firmware_file(cs35l41, wmfw_firmware, wmfw_filename, - CS35L41_FIRMWARE_ROOT, cs35l41->acpi_subsystem_id, + cs35l41->acpi_subsystem_id, cs35l41->amp_name, -1, "wmfw"); if (!ret) { /* try cirrus/part-dspN-fwtype-sub<-ampname>.bin */ ret = cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename, - CS35L41_FIRMWARE_ROOT, cs35l41->acpi_subsystem_id, cs35l41->amp_name, -1, "bin"); if (ret) @@ -328,18 +363,16 @@ static int cs35l41_request_firmware_files(struct cs35l41_hda *cs35l41, /* try cirrus/part-dspN-fwtype-sub.wmfw */ ret = cs35l41_request_firmware_file(cs35l41, wmfw_firmware, wmfw_filename, - CS35L41_FIRMWARE_ROOT, cs35l41->acpi_subsystem_id, + cs35l41->acpi_subsystem_id, NULL, -1, "wmfw"); if (!ret) { /* try cirrus/part-dspN-fwtype-sub<-ampname>.bin */ ret = cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename, - CS35L41_FIRMWARE_ROOT, cs35l41->acpi_subsystem_id, cs35l41->amp_name, -1, "bin"); if (ret) /* try cirrus/part-dspN-fwtype-sub.bin */ ret = cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename, - CS35L41_FIRMWARE_ROOT, cs35l41->acpi_subsystem_id, NULL, -1, "bin"); if (ret) @@ -361,95 +394,162 @@ fallback: coeff_firmware, coeff_filename); } -#if IS_ENABLED(CONFIG_EFI) -static int cs35l41_apply_calibration(struct cs35l41_hda *cs35l41, __be32 ambient, __be32 r0, - __be32 status, __be32 checksum) + +static void cs35l41_hda_apply_calibration(struct cs35l41_hda *cs35l41) +{ + int ret; + + if (!cs35l41->cal_data_valid) + return; + + ret = cs_amp_write_cal_coeffs(&cs35l41->cs_dsp, &cs35l41_calibration_controls, + &cs35l41->cal_data); + if (ret < 0) + dev_warn(cs35l41->dev, "Failed to apply calibration: %d\n", ret); + else + dev_info(cs35l41->dev, "Calibration applied: R0=%d\n", cs35l41->cal_data.calR); +} + +static int cs35l41_read_silicon_uid(struct cs35l41_hda *cs35l41, u64 *uid) { + u32 tmp; int ret; - ret = hda_cs_dsp_write_ctl(&cs35l41->cs_dsp, CAL_AMBIENT_DSP_CTL_NAME, CAL_DSP_CTL_TYPE, - CAL_DSP_CTL_ALG, &ambient, 4); + ret = regmap_read(cs35l41->regmap, CS35L41_DIE_STS2, &tmp); if (ret) { - dev_err(cs35l41->dev, "Cannot Write Control: %s - %d\n", CAL_AMBIENT_DSP_CTL_NAME, - ret); + dev_err(cs35l41->dev, "Cannot obtain CS35L41_DIE_STS2: %d\n", ret); return ret; } - ret = hda_cs_dsp_write_ctl(&cs35l41->cs_dsp, CAL_R_DSP_CTL_NAME, CAL_DSP_CTL_TYPE, - CAL_DSP_CTL_ALG, &r0, 4); + + *uid = tmp; + *uid <<= 32; + + ret = regmap_read(cs35l41->regmap, CS35L41_DIE_STS1, &tmp); if (ret) { - dev_err(cs35l41->dev, "Cannot Write Control: %s - %d\n", CAL_R_DSP_CTL_NAME, ret); + dev_err(cs35l41->dev, "Cannot obtain CS35L41_DIE_STS1: %d\n", ret); return ret; } - ret = hda_cs_dsp_write_ctl(&cs35l41->cs_dsp, CAL_STATUS_DSP_CTL_NAME, CAL_DSP_CTL_TYPE, - CAL_DSP_CTL_ALG, &status, 4); - if (ret) { - dev_err(cs35l41->dev, "Cannot Write Control: %s - %d\n", CAL_STATUS_DSP_CTL_NAME, - ret); + + *uid |= tmp; + + dev_dbg(cs35l41->dev, "UniqueID = %#llx\n", *uid); + + return 0; +} + +static int cs35l41_get_calibration(struct cs35l41_hda *cs35l41) +{ + u64 silicon_uid; + int ret; + + ret = cs35l41_read_silicon_uid(cs35l41, &silicon_uid); + if (ret < 0) return ret; - } - ret = hda_cs_dsp_write_ctl(&cs35l41->cs_dsp, CAL_CHECKSUM_DSP_CTL_NAME, CAL_DSP_CTL_TYPE, - CAL_DSP_CTL_ALG, &checksum, 4); - if (ret) { - dev_err(cs35l41->dev, "Cannot Write Control: %s - %d\n", CAL_CHECKSUM_DSP_CTL_NAME, - ret); + + ret = cs_amp_get_efi_calibration_data(cs35l41->dev, silicon_uid, + cs35l41->index, + &cs35l41->cal_data); + + /* Only return an error status if probe should be aborted */ + if ((ret == -ENOENT) || (ret == -EOVERFLOW)) + return 0; + + if (ret < 0) return ret; - } + + cs35l41->cal_data_valid = true; return 0; } -static int cs35l41_save_calibration(struct cs35l41_hda *cs35l41) + +static void cs35l41_set_default_tuning_params(struct cs35l41_hda *cs35l41) { - static efi_guid_t efi_guid = EFI_GUID(0x02f9af02, 0x7734, 0x4233, 0xb4, 0x3d, 0x93, 0xfe, - 0x5a, 0xa3, 0x5d, 0xb3); - static efi_char16_t efi_name[] = L"CirrusSmartAmpCalibrationData"; - const struct cs35l41_amp_efi_data *efi_data; - const struct cs35l41_amp_cal_data *cl; - unsigned long data_size = 0; - efi_status_t status; - int ret = 0; - u8 *data = NULL; - u32 attr; + cs35l41->tuning_gain = DEFAULT_AMP_GAIN_PCM; +} - /* Get real size of UEFI variable */ - status = efi.get_variable(efi_name, &efi_guid, &attr, &data_size, data); - if (status == EFI_BUFFER_TOO_SMALL) { - ret = -ENODEV; - /* Allocate data buffer of data_size bytes */ - data = vmalloc(data_size); - if (!data) - return -ENOMEM; - /* Get variable contents into buffer */ - status = efi.get_variable(efi_name, &efi_guid, &attr, &data_size, data); - if (status == EFI_SUCCESS) { - efi_data = (struct cs35l41_amp_efi_data *)data; - dev_dbg(cs35l41->dev, "Calibration: Size=%d, Amp Count=%d\n", - efi_data->size, efi_data->count); - if (efi_data->count > cs35l41->index) { - cl = &efi_data->data[cs35l41->index]; - dev_dbg(cs35l41->dev, - "Calibration: Ambient=%02x, Status=%02x, R0=%d\n", - cl->calAmbient, cl->calStatus, cl->calR); - - /* Calibration can only be applied whilst the DSP is not running */ - ret = cs35l41_apply_calibration(cs35l41, - cpu_to_be32(cl->calAmbient), - cpu_to_be32(cl->calR), - cpu_to_be32(cl->calStatus), - cpu_to_be32(cl->calR + 1)); - } +static int cs35l41_read_tuning_params(struct cs35l41_hda *cs35l41, const struct firmware *firmware) +{ + struct cs35l41_tuning_params *params; + unsigned int offset = 0; + unsigned int end; + int i; + + params = (void *)&firmware->data[0]; + + if (le32_to_cpu(params->size) != firmware->size) { + dev_err(cs35l41->dev, "Wrong Size for Tuning Param file. Expected %d got %zu\n", + le32_to_cpu(params->size), firmware->size); + return -EINVAL; + } + + if (le32_to_cpu(params->version) != 1) { + dev_err(cs35l41->dev, "Unsupported Tuning Param Version: %d\n", + le32_to_cpu(params->version)); + return -EINVAL; + } + + if (le32_to_cpu(params->signature) != CS35L41_TUNING_SIG) { + dev_err(cs35l41->dev, + "Mismatched Signature for Tuning Param file. Expected %#x got %#x\n", + CS35L41_TUNING_SIG, le32_to_cpu(params->signature)); + return -EINVAL; + } + + end = firmware->size - sizeof(struct cs35l41_tuning_params); + + for (i = 0; i < le32_to_cpu(params->num_entries); i++) { + struct cs35l41_tuning_param *param; + + if ((offset >= end) || ((offset + sizeof(struct cs35l41_tuning_param_hdr)) >= end)) + return -EFAULT; + + param = (void *)¶ms->data[offset]; + offset += le32_to_cpu(param->hdr.size); + + if (offset > end) + return -EFAULT; + + switch (le32_to_cpu(param->hdr.type)) { + case TUNING_PARAM_GAIN: + cs35l41->tuning_gain = le32_to_cpu(param->gain); + dev_dbg(cs35l41->dev, "Applying Gain: %d\n", cs35l41->tuning_gain); + break; + default: + break; } - vfree(data); } - return ret; + + return 0; } -#else -static int cs35l41_save_calibration(struct cs35l41_hda *cs35l41) + +static int cs35l41_load_tuning_params(struct cs35l41_hda *cs35l41, char *tuning_filename) { - dev_warn(cs35l41->dev, "Calibration not supported without EFI support.\n"); - return 0; + const struct firmware *tuning_param_file = NULL; + char *tuning_param_filename = NULL; + int ret; + + ret = cs35l41_request_tuning_param_file(cs35l41, tuning_filename, &tuning_param_file, + &tuning_param_filename, cs35l41->acpi_subsystem_id); + if (ret) { + dev_dbg(cs35l41->dev, "Missing Tuning Param for file: %s: %d\n", tuning_filename, + ret); + return 0; + } + + ret = cs35l41_read_tuning_params(cs35l41, tuning_param_file); + if (ret) { + dev_err(cs35l41->dev, "Error reading Tuning Params from file: %s: %d\n", + tuning_param_filename, ret); + /* Reset to default Tuning Parameters */ + cs35l41_set_default_tuning_params(cs35l41); + } + + release_firmware(tuning_param_file); + kfree(tuning_param_filename); + + return ret; } -#endif static int cs35l41_init_dsp(struct cs35l41_hda *cs35l41) { @@ -470,27 +570,33 @@ static int cs35l41_init_dsp(struct cs35l41_hda *cs35l41) cs35l41->halo_initialized = true; } + cs35l41_set_default_tuning_params(cs35l41); + ret = cs35l41_request_firmware_files(cs35l41, &wmfw_firmware, &wmfw_filename, &coeff_firmware, &coeff_filename); if (ret < 0) return ret; dev_dbg(cs35l41->dev, "Loading WMFW Firmware: %s\n", wmfw_filename); - if (coeff_filename) + if (coeff_filename) { dev_dbg(cs35l41->dev, "Loading Coefficient File: %s\n", coeff_filename); - else + ret = cs35l41_load_tuning_params(cs35l41, coeff_filename); + if (ret) + dev_warn(cs35l41->dev, "Unable to load Tuning Parameters: %d\n", ret); + } else { dev_warn(cs35l41->dev, "No Coefficient File available.\n"); + } ret = cs_dsp_power_up(dsp, wmfw_firmware, wmfw_filename, coeff_firmware, coeff_filename, hda_cs_dsp_fw_ids[cs35l41->firmware_type]); if (ret) - goto err_release; - - cs35l41_add_controls(cs35l41); + goto err; - ret = cs35l41_save_calibration(cs35l41); + cs35l41_hda_apply_calibration(cs35l41); -err_release: +err: + if (ret) + cs35l41_set_default_tuning_params(cs35l41); release_firmware(wmfw_firmware); release_firmware(coeff_firmware); kfree(wmfw_filename); @@ -503,6 +609,7 @@ static void cs35l41_shutdown_dsp(struct cs35l41_hda *cs35l41) { struct cs_dsp *dsp = &cs35l41->cs_dsp; + cs35l41_set_default_tuning_params(cs35l41); cs_dsp_stop(dsp); cs_dsp_power_down(dsp); dev_dbg(cs35l41->dev, "Unloaded Firmware\n"); @@ -553,6 +660,10 @@ static void cs35l41_hda_play_start(struct device *dev) if (cs35l41->cs_dsp.running) { regmap_multi_reg_write(reg, cs35l41_hda_config_dsp, ARRAY_SIZE(cs35l41_hda_config_dsp)); + if (cs35l41->hw_cfg.bst_type == CS35L41_INT_BOOST) + regmap_write(reg, CS35L41_DSP1_RX5_SRC, CS35L41_INPUT_SRC_VPMON); + else + regmap_write(reg, CS35L41_DSP1_RX5_SRC, CS35L41_INPUT_SRC_VBSTMON); regmap_update_bits(reg, CS35L41_PWR_CTRL2, CS35L41_VMON_EN_MASK | CS35L41_IMON_EN_MASK, 1 << CS35L41_VMON_EN_SHIFT | 1 << CS35L41_IMON_EN_SHIFT); @@ -570,6 +681,7 @@ static void cs35l41_mute(struct device *dev, bool mute) { struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev); struct regmap *reg = cs35l41->regmap; + unsigned int amp_gain; dev_dbg(dev, "Mute(%d:%d) Playback Started: %d\n", mute, cs35l41->mute_override, cs35l41->playback_started); @@ -581,8 +693,13 @@ static void cs35l41_mute(struct device *dev, bool mute) } else { dev_dbg(dev, "Unmuting\n"); if (cs35l41->cs_dsp.running) { - regmap_multi_reg_write(reg, cs35l41_hda_unmute_dsp, - ARRAY_SIZE(cs35l41_hda_unmute_dsp)); + dev_dbg(dev, "Using Tuned Gain: %d\n", cs35l41->tuning_gain); + amp_gain = (cs35l41->tuning_gain << CS35L41_AMP_GAIN_PCM_SHIFT) | + (DEFAULT_AMP_GAIN_PDM << CS35L41_AMP_GAIN_PDM_SHIFT); + + /* AMP_HPF_PCM_EN = 1, AMP_VOL_PCM 0.0 dB */ + regmap_write(reg, CS35L41_AMP_DIG_VOL_CTRL, 0x00008000); + regmap_write(reg, CS35L41_AMP_GAIN_CTRL, amp_gain); } else { regmap_multi_reg_write(reg, cs35l41_hda_unmute, ARRAY_SIZE(cs35l41_hda_unmute)); @@ -1056,6 +1173,9 @@ static int cs35l41_smart_amp(struct cs35l41_hda *cs35l41) goto clean_dsp; } + dev_info(cs35l41->dev, "Firmware Loaded - Type: %s, Gain: %d\n", + hda_cs_dsp_fw_ids[cs35l41->firmware_type], cs35l41->tuning_gain); + return 0; clean_dsp: @@ -1286,27 +1406,29 @@ static void cs35l41_acpi_device_notify(acpi_handle handle, u32 event, struct dev static int cs35l41_hda_bind(struct device *dev, struct device *master, void *master_data) { struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev); - struct hda_component *comps = master_data; + struct hda_component_parent *parent = master_data; + struct hda_component *comp; unsigned int sleep_flags; int ret = 0; - if (!comps || cs35l41->index < 0 || cs35l41->index >= HDA_MAX_COMPONENTS) + comp = hda_component_from_index(parent, cs35l41->index); + if (!comp) return -EINVAL; - comps = &comps[cs35l41->index]; - if (comps->dev) + if (comp->dev) return -EBUSY; pm_runtime_get_sync(dev); mutex_lock(&cs35l41->fw_mutex); - comps->dev = dev; + comp->dev = dev; + cs35l41->codec = parent->codec; if (!cs35l41->acpi_subsystem_id) cs35l41->acpi_subsystem_id = kasprintf(GFP_KERNEL, "%.8x", - comps->codec->core.subsystem_id); - cs35l41->codec = comps->codec; - strscpy(comps->name, dev_name(dev), sizeof(comps->name)); + cs35l41->codec->core.subsystem_id); + + strscpy(comp->name, dev_name(dev), sizeof(comp->name)); cs35l41->firmware_type = HDA_CS_DSP_FW_SPK_PROT; @@ -1321,13 +1443,13 @@ static int cs35l41_hda_bind(struct device *dev, struct device *master, void *mas ret = cs35l41_create_controls(cs35l41); - comps->playback_hook = cs35l41_hda_playback_hook; - comps->pre_playback_hook = cs35l41_hda_pre_playback_hook; - comps->post_playback_hook = cs35l41_hda_post_playback_hook; - comps->acpi_notify = cs35l41_acpi_device_notify; - comps->adev = cs35l41->dacpi; + comp->playback_hook = cs35l41_hda_playback_hook; + comp->pre_playback_hook = cs35l41_hda_pre_playback_hook; + comp->post_playback_hook = cs35l41_hda_post_playback_hook; + comp->acpi_notify = cs35l41_acpi_device_notify; + comp->adev = cs35l41->dacpi; - comps->acpi_notifications_supported = cs35l41_dsm_supported(acpi_device_handle(comps->adev), + comp->acpi_notifications_supported = cs35l41_dsm_supported(acpi_device_handle(comp->adev), CS35L41_DSM_GET_MUTE); cs35l41->mute_override = cs35l41_get_acpi_mute_state(cs35l41, @@ -1336,7 +1458,7 @@ static int cs35l41_hda_bind(struct device *dev, struct device *master, void *mas mutex_unlock(&cs35l41->fw_mutex); sleep_flags = lock_system_sleep(); - if (!device_link_add(&comps->codec->core.dev, cs35l41->dev, DL_FLAG_STATELESS)) + if (!device_link_add(&cs35l41->codec->core.dev, cs35l41->dev, DL_FLAG_STATELESS)) dev_warn(dev, "Unable to create device link\n"); unlock_system_sleep(sleep_flags); @@ -1356,14 +1478,19 @@ static int cs35l41_hda_bind(struct device *dev, struct device *master, void *mas static void cs35l41_hda_unbind(struct device *dev, struct device *master, void *master_data) { struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev); - struct hda_component *comps = master_data; + struct hda_component_parent *parent = master_data; + struct hda_component *comp; unsigned int sleep_flags; - if (comps[cs35l41->index].dev == dev) { - memset(&comps[cs35l41->index], 0, sizeof(*comps)); + comp = hda_component_from_index(parent, cs35l41->index); + if (!comp) + return; + + if (comp->dev == dev) { sleep_flags = lock_system_sleep(); - device_link_remove(&comps->codec->core.dev, cs35l41->dev); + device_link_remove(&cs35l41->codec->core.dev, cs35l41->dev); unlock_system_sleep(sleep_flags); + memset(comp, 0, sizeof(*comp)); } } @@ -1461,13 +1588,56 @@ static struct regmap_irq_chip cs35l41_regmap_irq_chip = { .runtime_pm = true, }; +static void cs35l41_configure_interrupt(struct cs35l41_hda *cs35l41, int irq_pol) +{ + int irq; + int ret; + int i; + + if (!cs35l41->irq) { + dev_warn(cs35l41->dev, "No Interrupt Found"); + goto err; + } + + ret = devm_regmap_add_irq_chip(cs35l41->dev, cs35l41->regmap, cs35l41->irq, + IRQF_ONESHOT | IRQF_SHARED | irq_pol, + 0, &cs35l41_regmap_irq_chip, &cs35l41->irq_data); + if (ret) { + dev_dbg(cs35l41->dev, "Unable to add IRQ Chip: %d.", ret); + goto err; + } + + for (i = 0; i < ARRAY_SIZE(cs35l41_irqs); i++) { + irq = regmap_irq_get_virq(cs35l41->irq_data, cs35l41_irqs[i].irq); + if (irq < 0) { + ret = irq; + dev_dbg(cs35l41->dev, "Unable to map IRQ %s: %d.", cs35l41_irqs[i].name, + ret); + goto err; + } + + ret = devm_request_threaded_irq(cs35l41->dev, irq, NULL, + cs35l41_irqs[i].handler, + IRQF_ONESHOT | IRQF_SHARED | irq_pol, + cs35l41_irqs[i].name, cs35l41); + if (ret) { + dev_dbg(cs35l41->dev, "Unable to allocate IRQ %s:: %d.", + cs35l41_irqs[i].name, ret); + goto err; + } + } + return; +err: + dev_warn(cs35l41->dev, + "IRQ Config Failed. Amp errors may not be recoverable without reboot."); +} + static int cs35l41_hda_apply_properties(struct cs35l41_hda *cs35l41) { struct cs35l41_hw_cfg *hw_cfg = &cs35l41->hw_cfg; bool using_irq = false; - int irq, irq_pol; + int irq_pol; int ret; - int i; if (!cs35l41->hw_cfg.valid) return -EINVAL; @@ -1510,26 +1680,8 @@ static int cs35l41_hda_apply_properties(struct cs35l41_hda *cs35l41) irq_pol = cs35l41_gpio_config(cs35l41->regmap, hw_cfg); - if (cs35l41->irq && using_irq) { - ret = devm_regmap_add_irq_chip(cs35l41->dev, cs35l41->regmap, cs35l41->irq, - IRQF_ONESHOT | IRQF_SHARED | irq_pol, - 0, &cs35l41_regmap_irq_chip, &cs35l41->irq_data); - if (ret) - return ret; - - for (i = 0; i < ARRAY_SIZE(cs35l41_irqs); i++) { - irq = regmap_irq_get_virq(cs35l41->irq_data, cs35l41_irqs[i].irq); - if (irq < 0) - return irq; - - ret = devm_request_threaded_irq(cs35l41->dev, irq, NULL, - cs35l41_irqs[i].handler, - IRQF_ONESHOT | IRQF_SHARED | irq_pol, - cs35l41_irqs[i].name, cs35l41); - if (ret) - return ret; - } - } + if (using_irq) + cs35l41_configure_interrupt(cs35l41, irq_pol); return cs35l41_hda_channel_map(cs35l41->dev, 0, NULL, 1, &hw_cfg->spk_pos); } @@ -1588,38 +1740,14 @@ int cs35l41_get_speaker_id(struct device *dev, int amp_index, int num_amps, int return speaker_id; } -static int cs35l41_hda_read_acpi(struct cs35l41_hda *cs35l41, const char *hid, int id) +int cs35l41_hda_parse_acpi(struct cs35l41_hda *cs35l41, struct device *physdev, int id) { struct cs35l41_hw_cfg *hw_cfg = &cs35l41->hw_cfg; u32 values[HDA_MAX_COMPONENTS]; - struct acpi_device *adev; - struct device *physdev; - struct spi_device *spi; - const char *sub; char *property; size_t nval; int i, ret; - adev = acpi_dev_get_first_match_dev(hid, NULL, -1); - if (!adev) { - dev_err(cs35l41->dev, "Failed to find an ACPI device for %s\n", hid); - return -ENODEV; - } - - cs35l41->dacpi = adev; - physdev = get_device(acpi_get_first_physical_node(adev)); - - sub = acpi_get_subsystem_id(ACPI_HANDLE(physdev)); - if (IS_ERR(sub)) - sub = NULL; - cs35l41->acpi_subsystem_id = sub; - - ret = cs35l41_add_dsd_properties(cs35l41, physdev, id, hid); - if (!ret) { - dev_info(cs35l41->dev, "Using extra _DSD properties, bypassing _DSD in ACPI\n"); - goto out; - } - property = "cirrus,dev-index"; ret = device_property_count_u32(physdev, property); if (ret <= 0) @@ -1651,8 +1779,9 @@ static int cs35l41_hda_read_acpi(struct cs35l41_hda *cs35l41, const char *hid, i /* To use the same release code for all laptop variants we can't use devm_ version of * gpiod_get here, as CLSA010* don't have a fully functional bios with an _DSD node */ - cs35l41->reset_gpio = fwnode_gpiod_get_index(acpi_fwnode_handle(adev), "reset", cs35l41->index, - GPIOD_OUT_LOW, "cs35l41-reset"); + cs35l41->reset_gpio = fwnode_gpiod_get_index(acpi_fwnode_handle(cs35l41->dacpi), "reset", + cs35l41->index, GPIOD_OUT_LOW, + "cs35l41-reset"); property = "cirrus,speaker-position"; ret = device_property_read_u32_array(physdev, property, values, nval); @@ -1708,6 +1837,51 @@ static int cs35l41_hda_read_acpi(struct cs35l41_hda *cs35l41, const char *hid, i hw_cfg->bst_type = CS35L41_EXT_BOOST; hw_cfg->valid = true; + + return 0; +err: + dev_err(cs35l41->dev, "Failed property %s: %d\n", property, ret); + hw_cfg->valid = false; + hw_cfg->gpio1.valid = false; + hw_cfg->gpio2.valid = false; + acpi_dev_put(cs35l41->dacpi); + + return ret; +} + +static int cs35l41_hda_read_acpi(struct cs35l41_hda *cs35l41, const char *hid, int id) +{ + struct acpi_device *adev; + struct device *physdev; + struct spi_device *spi; + const char *sub; + int ret; + + adev = acpi_dev_get_first_match_dev(hid, NULL, -1); + if (!adev) { + dev_err(cs35l41->dev, "Failed to find an ACPI device for %s\n", hid); + return -ENODEV; + } + + cs35l41->dacpi = adev; + physdev = get_device(acpi_get_first_physical_node(adev)); + + sub = acpi_get_subsystem_id(ACPI_HANDLE(physdev)); + if (IS_ERR(sub)) + sub = NULL; + cs35l41->acpi_subsystem_id = sub; + + ret = cs35l41_add_dsd_properties(cs35l41, physdev, id, hid); + if (!ret) { + dev_info(cs35l41->dev, "Using extra _DSD properties, bypassing _DSD in ACPI\n"); + goto out; + } + + ret = cs35l41_hda_parse_acpi(cs35l41, physdev, id); + if (ret) { + put_device(physdev); + return ret; + } out: put_device(physdev); @@ -1723,16 +1897,6 @@ out: } return 0; - -err: - dev_err(cs35l41->dev, "Failed property %s: %d\n", property, ret); - hw_cfg->valid = false; - hw_cfg->gpio1.valid = false; - hw_cfg->gpio2.valid = false; - acpi_dev_put(cs35l41->dacpi); - put_device(physdev); - - return ret; } int cs35l41_hda_probe(struct device *dev, const char *device_name, int id, int irq, @@ -1808,6 +1972,10 @@ int cs35l41_hda_probe(struct device *dev, const char *device_name, int id, int i if (ret) goto err; + ret = cs35l41_get_calibration(cs35l41); + if (ret && ret != -ENOENT) + goto err; + cs35l41_mute(cs35l41->dev, true); INIT_WORK(&cs35l41->fw_load_work, cs35l41_fw_load_work); @@ -1851,12 +2019,14 @@ err: return ret; } -EXPORT_SYMBOL_NS_GPL(cs35l41_hda_probe, SND_HDA_SCODEC_CS35L41); +EXPORT_SYMBOL_NS_GPL(cs35l41_hda_probe, "SND_HDA_SCODEC_CS35L41"); void cs35l41_hda_remove(struct device *dev) { struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev); + component_del(cs35l41->dev, &cs35l41_hda_comp_ops); + pm_runtime_get_sync(cs35l41->dev); pm_runtime_dont_use_autosuspend(cs35l41->dev); pm_runtime_disable(cs35l41->dev); @@ -1864,8 +2034,6 @@ void cs35l41_hda_remove(struct device *dev) if (cs35l41->halo_initialized) cs35l41_remove_dsp(cs35l41); - component_del(cs35l41->dev, &cs35l41_hda_comp_ops); - acpi_dev_put(cs35l41->dacpi); pm_runtime_put_noidle(cs35l41->dev); @@ -1876,7 +2044,7 @@ void cs35l41_hda_remove(struct device *dev) gpiod_put(cs35l41->cs_gpio); kfree(cs35l41->acpi_subsystem_id); } -EXPORT_SYMBOL_NS_GPL(cs35l41_hda_remove, SND_HDA_SCODEC_CS35L41); +EXPORT_SYMBOL_NS_GPL(cs35l41_hda_remove, "SND_HDA_SCODEC_CS35L41"); const struct dev_pm_ops cs35l41_hda_pm_ops = { RUNTIME_PM_OPS(cs35l41_runtime_suspend, cs35l41_runtime_resume, @@ -1884,10 +2052,11 @@ const struct dev_pm_ops cs35l41_hda_pm_ops = { .prepare = cs35l41_system_suspend_prep, SYSTEM_SLEEP_PM_OPS(cs35l41_system_suspend, cs35l41_system_resume) }; -EXPORT_SYMBOL_NS_GPL(cs35l41_hda_pm_ops, SND_HDA_SCODEC_CS35L41); +EXPORT_SYMBOL_NS_GPL(cs35l41_hda_pm_ops, "SND_HDA_SCODEC_CS35L41"); MODULE_DESCRIPTION("CS35L41 HDA Driver"); -MODULE_IMPORT_NS(SND_HDA_CS_DSP_CONTROLS); +MODULE_IMPORT_NS("SND_HDA_CS_DSP_CONTROLS"); +MODULE_IMPORT_NS("SND_SOC_CS_AMP_LIB"); MODULE_AUTHOR("Lucas Tanure, Cirrus Logic Inc, <tanureal@opensource.cirrus.com>"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(FW_CS_DSP); +MODULE_IMPORT_NS("FW_CS_DSP"); diff --git a/sound/pci/hda/cs35l41_hda.h b/sound/pci/hda/cs35l41_hda.h index 43d55292b327..c730b3351589 100644 --- a/sound/pci/hda/cs35l41_hda.h +++ b/sound/pci/hda/cs35l41_hda.h @@ -16,11 +16,14 @@ #include <linux/gpio/consumer.h> #include <linux/device.h> #include <sound/cs35l41.h> +#include <sound/cs-amp-lib.h> #include <linux/firmware/cirrus/cs_dsp.h> #include <linux/firmware/cirrus/wmfw.h> #define CS35L41_MAX_ACCEPTABLE_SPI_SPEED_HZ 1000000 +#define DEFAULT_AMP_GAIN_PCM 17 /* 17.5dB Gain */ +#define DEFAULT_AMP_GAIN_PDM 19 /* 19.5dB Gain */ struct cs35l41_amp_cal_data { u32 calTarget[2]; @@ -83,6 +86,9 @@ struct cs35l41_hda { bool mute_override; enum control_bus control_bus; bool bypass_fw; + unsigned int tuning_gain; + struct cirrus_amp_cal_data cal_data; + bool cal_data_valid; }; @@ -98,5 +104,6 @@ int cs35l41_hda_probe(struct device *dev, const char *device_name, int id, int i struct regmap *regmap, enum control_bus control_bus); void cs35l41_hda_remove(struct device *dev); int cs35l41_get_speaker_id(struct device *dev, int amp_index, int num_amps, int fixed_gpio_id); +int cs35l41_hda_parse_acpi(struct cs35l41_hda *cs35l41, struct device *physdev, int id); #endif /*__CS35L41_HDA_H__*/ diff --git a/sound/pci/hda/cs35l41_hda_i2c.c b/sound/pci/hda/cs35l41_hda_i2c.c index 603e9bff3a71..e77495413c21 100644 --- a/sound/pci/hda/cs35l41_hda_i2c.c +++ b/sound/pci/hda/cs35l41_hda_i2c.c @@ -39,7 +39,7 @@ static void cs35l41_hda_i2c_remove(struct i2c_client *clt) } static const struct i2c_device_id cs35l41_hda_i2c_id[] = { - { "cs35l41-hda", 0 }, + { "cs35l41-hda" }, {} }; @@ -64,6 +64,6 @@ static struct i2c_driver cs35l41_i2c_driver = { module_i2c_driver(cs35l41_i2c_driver); MODULE_DESCRIPTION("HDA CS35L41 driver"); -MODULE_IMPORT_NS(SND_HDA_SCODEC_CS35L41); +MODULE_IMPORT_NS("SND_HDA_SCODEC_CS35L41"); MODULE_AUTHOR("Lucas Tanure <tanureal@opensource.cirrus.com>"); MODULE_LICENSE("GPL"); diff --git a/sound/pci/hda/cs35l41_hda_property.c b/sound/pci/hda/cs35l41_hda_property.c index e436d4dab317..61d2314834e7 100644 --- a/sound/pci/hda/cs35l41_hda_property.c +++ b/sound/pci/hda/cs35l41_hda_property.c @@ -51,19 +51,32 @@ static const struct cs35l41_config cs35l41_config_table[] = { { "103C8A2E", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, { "103C8A30", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, { "103C8A31", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, + { "103C8A6E", 4, EXTERNAL, { CS35L41_LEFT, CS35L41_LEFT, CS35L41_RIGHT, CS35L41_RIGHT }, 0, -1, -1, 0, 0, 0 }, { "103C8BB3", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, { "103C8BB4", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, + { "103C8BDD", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, + { "103C8BDE", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, { "103C8BDF", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, { "103C8BE0", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, { "103C8BE1", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, { "103C8BE2", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, - { "103C8BE9", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, - { "103C8BDD", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, - { "103C8BDE", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, { "103C8BE3", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, { "103C8BE5", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, { "103C8BE6", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, + { "103C8BE7", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, + { "103C8BE8", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, + { "103C8BE9", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, { "103C8B3A", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, + { "103C8C15", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4000, 24 }, + { "103C8C16", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4000, 24 }, + { "103C8C17", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4000, 24 }, + { "103C8C4D", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, + { "103C8C4E", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, + { "103C8C4F", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, + { "103C8C50", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, + { "103C8C51", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, + { "103C8CDD", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, + { "103C8CDE", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 3900, 24 }, { "104312AF", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 }, { "10431433", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4500, 24 }, { "10431463", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4500, 24 }, @@ -84,6 +97,7 @@ static const struct cs35l41_config cs35l41_config_table[] = { { "10431863", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 }, { "104318D3", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 0, 0, 0 }, { "10431A83", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4500, 24 }, + { "10431B93", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 }, { "10431C9F", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 }, { "10431CAF", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 }, { "10431CCF", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 }, @@ -97,13 +111,27 @@ static const struct cs35l41_config cs35l41_config_table[] = { { "10431F12", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4500, 24 }, { "10431F1F", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, -1, 0, 0, 0, 0 }, { "10431F62", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 0, 0, 0 }, + { "10433A20", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 }, + { "10433A30", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 }, + { "10433A40", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 }, + { "10433A50", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 }, + { "10433A60", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 }, + { "17AA3865", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, -1, -1, 0, 0, 0 }, + { "17AA3866", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, -1, -1, 0, 0, 0 }, + { "17AA386E", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 2, -1, 0, 0, 0 }, { "17AA386F", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, -1, -1, 0, 0, 0 }, + { "17AA3877", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, -1, -1, 0, 0, 0 }, + { "17AA3878", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, -1, -1, 0, 0, 0 }, { "17AA38A9", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 2, -1, 0, 0, 0 }, { "17AA38AB", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 2, -1, 0, 0, 0 }, { "17AA38B4", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 0, 0, 0 }, { "17AA38B5", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 0, 0, 0 }, { "17AA38B6", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 0, 0, 0 }, { "17AA38B7", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 0, 0, 0 }, + { "17AA38C7", 4, INTERNAL, { CS35L41_RIGHT, CS35L41_LEFT, CS35L41_RIGHT, CS35L41_LEFT }, 0, 2, -1, 1000, 4500, 24 }, + { "17AA38C8", 4, INTERNAL, { CS35L41_RIGHT, CS35L41_LEFT, CS35L41_RIGHT, CS35L41_LEFT }, 0, 2, -1, 1000, 4500, 24 }, + { "17AA38F9", 2, EXTERNAL, { CS35L41_RIGHT, CS35L41_LEFT, 0, 0 }, 0, 2, -1, 0, 0, 0 }, + { "17AA38FA", 2, EXTERNAL, { CS35L41_RIGHT, CS35L41_LEFT, 0, 0 }, 0, 2, -1, 0, 0, 0 }, {} }; @@ -210,6 +238,7 @@ static int generic_dsd_config(struct cs35l41_hda *cs35l41, struct device *physde struct spi_device *spi; bool dsd_found; int ret; + int i; for (cfg = cs35l41_config_table; cfg->ssid; cfg++) { if (!strcasecmp(cfg->ssid, cs35l41->acpi_subsystem_id)) @@ -295,16 +324,6 @@ static int generic_dsd_config(struct cs35l41_hda *cs35l41, struct device *physde cs35l41->index = id == 0x40 ? 0 : 1; } - if (cfg->num_amps == 3) - /* 3 amps means a center channel, so no duplicate channels */ - cs35l41->channel_index = 0; - else - /* - * if 4 amps, there are duplicate channels, so they need different indexes - * if 2 amps, no duplicate channels, channel_index would be 0 - */ - cs35l41->channel_index = cs35l41->index / 2; - cs35l41->reset_gpio = fwnode_gpiod_get_index(acpi_fwnode_handle(cs35l41->dacpi), "reset", cs35l41->index, GPIOD_OUT_LOW, "cs35l41-reset"); @@ -312,6 +331,11 @@ static int generic_dsd_config(struct cs35l41_hda *cs35l41, struct device *physde hw_cfg->spk_pos = cfg->channel[cs35l41->index]; + cs35l41->channel_index = 0; + for (i = 0; i < cs35l41->index; i++) + if (cfg->channel[i] == hw_cfg->spk_pos) + cs35l41->channel_index++; + if (cfg->boost_type == INTERNAL) { hw_cfg->bst_type = CS35L41_INT_BOOST; hw_cfg->bst_ind = cfg->boost_ind_nanohenry; @@ -336,6 +360,42 @@ static int generic_dsd_config(struct cs35l41_hda *cs35l41, struct device *physde } /* + * Systems 103C8C66, 103C8C67, 103C8C68, 103C8C6A use a dual speaker id system - each speaker has + * its own speaker id. + */ +static int hp_i2c_int_2amp_dual_spkid(struct cs35l41_hda *cs35l41, struct device *physdev, int id, + const char *hid) +{ + struct cs35l41_hw_cfg *hw_cfg = &cs35l41->hw_cfg; + + /* If _DSD exists for this laptop, we cannot support it through here */ + if (acpi_dev_has_props(cs35l41->dacpi)) + return -ENOENT; + + /* check I2C address to assign the index */ + cs35l41->index = id == 0x40 ? 0 : 1; + cs35l41->channel_index = 0; + cs35l41->reset_gpio = gpiod_get_index(physdev, NULL, 0, GPIOD_OUT_HIGH); + if (cs35l41->index == 0) + cs35l41->speaker_id = cs35l41_get_speaker_id(physdev, 0, 0, 1); + else + cs35l41->speaker_id = cs35l41_get_speaker_id(physdev, 0, 0, 2); + hw_cfg->spk_pos = cs35l41->index; + hw_cfg->gpio2.func = CS35L41_INTERRUPT; + hw_cfg->gpio2.valid = true; + hw_cfg->valid = true; + + hw_cfg->bst_type = CS35L41_INT_BOOST; + hw_cfg->bst_ind = 1000; + hw_cfg->bst_ipk = 4100; + hw_cfg->bst_cap = 24; + hw_cfg->gpio1.func = CS35L41_NOT_USED; + hw_cfg->gpio1.valid = true; + + return 0; +} + +/* * Device CLSA010(0/1) doesn't have _DSD so a gpiod_get by the label reset won't work. * And devices created by serial-multi-instantiate don't have their device struct * pointing to the correct fwnode, so acpi_dev must be used here. @@ -368,6 +428,20 @@ static int lenovo_legion_no_acpi(struct cs35l41_hda *cs35l41, struct device *phy return 0; } +static int missing_speaker_id_gpio2(struct cs35l41_hda *cs35l41, struct device *physdev, int id, + const char *hid) +{ + int ret; + + ret = cs35l41_add_gpios(cs35l41, physdev, -1, 2, -1, 2); + if (ret) { + dev_err(cs35l41->dev, "Error adding GPIO mapping: %d\n", ret); + return ret; + } + + return cs35l41_hda_parse_acpi(cs35l41, physdev, id); +} + struct cs35l41_prop_model { const char *hid; const char *ssid; @@ -392,19 +466,36 @@ static const struct cs35l41_prop_model cs35l41_prop_model_table[] = { { "CSC3551", "103C8A2E", generic_dsd_config }, { "CSC3551", "103C8A30", generic_dsd_config }, { "CSC3551", "103C8A31", generic_dsd_config }, + { "CSC3551", "103C8A6E", generic_dsd_config }, { "CSC3551", "103C8BB3", generic_dsd_config }, { "CSC3551", "103C8BB4", generic_dsd_config }, + { "CSC3551", "103C8BDD", generic_dsd_config }, + { "CSC3551", "103C8BDE", generic_dsd_config }, { "CSC3551", "103C8BDF", generic_dsd_config }, { "CSC3551", "103C8BE0", generic_dsd_config }, { "CSC3551", "103C8BE1", generic_dsd_config }, { "CSC3551", "103C8BE2", generic_dsd_config }, - { "CSC3551", "103C8BE9", generic_dsd_config }, - { "CSC3551", "103C8BDD", generic_dsd_config }, - { "CSC3551", "103C8BDE", generic_dsd_config }, { "CSC3551", "103C8BE3", generic_dsd_config }, { "CSC3551", "103C8BE5", generic_dsd_config }, { "CSC3551", "103C8BE6", generic_dsd_config }, + { "CSC3551", "103C8BE7", generic_dsd_config }, + { "CSC3551", "103C8BE8", generic_dsd_config }, + { "CSC3551", "103C8BE9", generic_dsd_config }, { "CSC3551", "103C8B3A", generic_dsd_config }, + { "CSC3551", "103C8C15", generic_dsd_config }, + { "CSC3551", "103C8C16", generic_dsd_config }, + { "CSC3551", "103C8C17", generic_dsd_config }, + { "CSC3551", "103C8C4D", generic_dsd_config }, + { "CSC3551", "103C8C4E", generic_dsd_config }, + { "CSC3551", "103C8C4F", generic_dsd_config }, + { "CSC3551", "103C8C50", generic_dsd_config }, + { "CSC3551", "103C8C51", generic_dsd_config }, + { "CSC3551", "103C8C66", hp_i2c_int_2amp_dual_spkid }, + { "CSC3551", "103C8C67", hp_i2c_int_2amp_dual_spkid }, + { "CSC3551", "103C8C68", hp_i2c_int_2amp_dual_spkid }, + { "CSC3551", "103C8C6A", hp_i2c_int_2amp_dual_spkid }, + { "CSC3551", "103C8CDD", generic_dsd_config }, + { "CSC3551", "103C8CDE", generic_dsd_config }, { "CSC3551", "104312AF", generic_dsd_config }, { "CSC3551", "10431433", generic_dsd_config }, { "CSC3551", "10431463", generic_dsd_config }, @@ -424,7 +515,9 @@ static const struct cs35l41_prop_model cs35l41_prop_model_table[] = { { "CSC3551", "104317F3", generic_dsd_config }, { "CSC3551", "10431863", generic_dsd_config }, { "CSC3551", "104318D3", generic_dsd_config }, + { "CSC3551", "10431A63", missing_speaker_id_gpio2 }, { "CSC3551", "10431A83", generic_dsd_config }, + { "CSC3551", "10431B93", generic_dsd_config }, { "CSC3551", "10431C9F", generic_dsd_config }, { "CSC3551", "10431CAF", generic_dsd_config }, { "CSC3551", "10431CCF", generic_dsd_config }, @@ -438,13 +531,27 @@ static const struct cs35l41_prop_model cs35l41_prop_model_table[] = { { "CSC3551", "10431F12", generic_dsd_config }, { "CSC3551", "10431F1F", generic_dsd_config }, { "CSC3551", "10431F62", generic_dsd_config }, + { "CSC3551", "10433A20", generic_dsd_config }, + { "CSC3551", "10433A30", generic_dsd_config }, + { "CSC3551", "10433A40", generic_dsd_config }, + { "CSC3551", "10433A50", generic_dsd_config }, + { "CSC3551", "10433A60", generic_dsd_config }, + { "CSC3551", "17AA3865", generic_dsd_config }, + { "CSC3551", "17AA3866", generic_dsd_config }, + { "CSC3551", "17AA386E", generic_dsd_config }, { "CSC3551", "17AA386F", generic_dsd_config }, + { "CSC3551", "17AA3877", generic_dsd_config }, + { "CSC3551", "17AA3878", generic_dsd_config }, { "CSC3551", "17AA38A9", generic_dsd_config }, { "CSC3551", "17AA38AB", generic_dsd_config }, { "CSC3551", "17AA38B4", generic_dsd_config }, { "CSC3551", "17AA38B5", generic_dsd_config }, { "CSC3551", "17AA38B6", generic_dsd_config }, { "CSC3551", "17AA38B7", generic_dsd_config }, + { "CSC3551", "17AA38C7", generic_dsd_config }, + { "CSC3551", "17AA38C8", generic_dsd_config }, + { "CSC3551", "17AA38F9", generic_dsd_config }, + { "CSC3551", "17AA38FA", generic_dsd_config }, {} }; diff --git a/sound/pci/hda/cs35l41_hda_spi.c b/sound/pci/hda/cs35l41_hda_spi.c index b76c0dfd5fef..2acbaf8467a0 100644 --- a/sound/pci/hda/cs35l41_hda_spi.c +++ b/sound/pci/hda/cs35l41_hda_spi.c @@ -38,6 +38,7 @@ static const struct spi_device_id cs35l41_hda_spi_id[] = { { "cs35l41-hda", 0 }, {} }; +MODULE_DEVICE_TABLE(spi, cs35l41_hda_spi_id); static const struct acpi_device_id cs35l41_acpi_hda_match[] = { { "CSC3551", 0 }, @@ -58,6 +59,6 @@ static struct spi_driver cs35l41_spi_driver = { module_spi_driver(cs35l41_spi_driver); MODULE_DESCRIPTION("HDA CS35L41 driver"); -MODULE_IMPORT_NS(SND_HDA_SCODEC_CS35L41); +MODULE_IMPORT_NS("SND_HDA_SCODEC_CS35L41"); MODULE_AUTHOR("Lucas Tanure <tanureal@opensource.cirrus.com>"); MODULE_LICENSE("GPL"); diff --git a/sound/pci/hda/cs35l56_hda.c b/sound/pci/hda/cs35l56_hda.c index 75a14ba54fcd..4ef7878e8fd4 100644 --- a/sound/pci/hda/cs35l56_hda.c +++ b/sound/pci/hda/cs35l56_hda.c @@ -14,6 +14,7 @@ #include <linux/regmap.h> #include <linux/slab.h> #include <sound/core.h> +#include <sound/cs-amp-lib.h> #include <sound/hda_codec.h> #include <sound/tlv.h> #include "cirrus_scodec.h" @@ -49,11 +50,19 @@ static const struct reg_sequence cs35l56_hda_dai_config[] = { }; +static void cs35l56_hda_wait_dsp_ready(struct cs35l56_hda *cs35l56) +{ + /* Wait for patching to complete */ + flush_work(&cs35l56->dsp_work); +} + static void cs35l56_hda_play(struct cs35l56_hda *cs35l56) { unsigned int val; int ret; + cs35l56_hda_wait_dsp_ready(cs35l56); + pm_runtime_get_sync(cs35l56->base.dev); ret = cs35l56_mbox_send(&cs35l56->base, CS35L56_MBOX_CMD_AUDIO_PLAY); if (ret == 0) { @@ -142,10 +151,6 @@ static int cs35l56_hda_runtime_resume(struct device *dev) } } - ret = cs35l56_force_sync_asp1_registers_from_cache(&cs35l56->base); - if (ret) - goto err; - return 0; err: @@ -179,6 +184,8 @@ static int cs35l56_hda_mixer_get(struct snd_kcontrol *kcontrol, unsigned int reg_val; int i; + cs35l56_hda_wait_dsp_ready(cs35l56); + regmap_read(cs35l56->base.regmap, kcontrol->private_value, ®_val); reg_val &= CS35L56_ASP_TXn_SRC_MASK; @@ -202,6 +209,8 @@ static int cs35l56_hda_mixer_put(struct snd_kcontrol *kcontrol, if (item >= CS35L56_NUM_INPUT_SRC) return -EINVAL; + cs35l56_hda_wait_dsp_ready(cs35l56); + regmap_update_bits_check(cs35l56->base.regmap, kcontrol->private_value, CS35L56_INPUT_MASK, cs35l56_tx_input_values[item], &changed); @@ -226,6 +235,8 @@ static int cs35l56_hda_posture_get(struct snd_kcontrol *kcontrol, unsigned int pos; int ret; + cs35l56_hda_wait_dsp_ready(cs35l56); + ret = regmap_read(cs35l56->base.regmap, CS35L56_MAIN_POSTURE_NUMBER, &pos); if (ret) return ret; @@ -247,6 +258,8 @@ static int cs35l56_hda_posture_put(struct snd_kcontrol *kcontrol, (pos > CS35L56_MAIN_POSTURE_MAX)) return -EINVAL; + cs35l56_hda_wait_dsp_ready(cs35l56); + ret = regmap_update_bits_check(cs35l56->base.regmap, CS35L56_MAIN_POSTURE_NUMBER, CS35L56_MAIN_POSTURE_MASK, @@ -290,6 +303,8 @@ static int cs35l56_hda_vol_get(struct snd_kcontrol *kcontrol, int vol; int ret; + cs35l56_hda_wait_dsp_ready(cs35l56); + ret = regmap_read(cs35l56->base.regmap, CS35L56_MAIN_RENDER_USER_VOLUME, &raw_vol); if (ret) @@ -322,6 +337,8 @@ static int cs35l56_hda_vol_put(struct snd_kcontrol *kcontrol, raw_vol = (vol + CS35L56_MAIN_RENDER_USER_VOLUME_MIN) << CS35L56_MAIN_RENDER_USER_VOLUME_SHIFT; + cs35l56_hda_wait_dsp_ready(cs35l56); + ret = regmap_update_bits_check(cs35l56->base.regmap, CS35L56_MAIN_RENDER_USER_VOLUME, CS35L56_MAIN_RENDER_USER_VOLUME_MASK, @@ -392,7 +409,7 @@ static void cs35l56_hda_remove_controls(struct cs35l56_hda *cs35l56) } static const struct cs_dsp_client_ops cs35l56_hda_client_ops = { - .control_remove = hda_cs_dsp_control_remove, + /* cs_dsp requires the client to provide this even if it is empty */ }; static int cs35l56_hda_request_firmware_file(struct cs35l56_hda *cs35l56, @@ -458,13 +475,15 @@ static void cs35l56_hda_request_firmware_files(struct cs35l56_hda *cs35l56, if (preloaded_fw_ver) { snprintf(base_name, sizeof(base_name), - "cirrus/cs35l56-%02x%s-%06x-dsp1-misc", + "cirrus/cs35l%02x-%02x%s-%06x-dsp1-misc", + cs35l56->base.type, cs35l56->base.rev, cs35l56->base.secured ? "-s" : "", preloaded_fw_ver & 0xffffff); } else { snprintf(base_name, sizeof(base_name), - "cirrus/cs35l56-%02x%s-dsp1-misc", + "cirrus/cs35l%02x-%02x%s-dsp1-misc", + cs35l56->base.type, cs35l56->base.rev, cs35l56->base.secured ? "-s" : ""); } @@ -536,18 +555,23 @@ static void cs35l56_hda_release_firmware_files(const struct firmware *wmfw_firmw kfree(coeff_filename); } -static void cs35l56_hda_add_dsp_controls(struct cs35l56_hda *cs35l56) +static void cs35l56_hda_apply_calibration(struct cs35l56_hda *cs35l56) { - struct hda_cs_dsp_ctl_info info; + int ret; - info.device_name = cs35l56->amp_name; - info.fw_type = HDA_CS_DSP_FW_MISC; - info.card = cs35l56->codec->card; + if (!cs35l56->base.cal_data_valid || cs35l56->base.secured) + return; - hda_cs_dsp_add_controls(&cs35l56->cs_dsp, &info); + ret = cs_amp_write_cal_coeffs(&cs35l56->cs_dsp, + &cs35l56_calibration_controls, + &cs35l56->base.cal_data); + if (ret < 0) + dev_warn(cs35l56->base.dev, "Failed to write calibration: %d\n", ret); + else + dev_info(cs35l56->base.dev, "Calibration applied\n"); } -static int cs35l56_hda_fw_load(struct cs35l56_hda *cs35l56) +static void cs35l56_hda_fw_load(struct cs35l56_hda *cs35l56) { const struct firmware *coeff_firmware = NULL; const struct firmware *wmfw_firmware = NULL; @@ -555,15 +579,23 @@ static int cs35l56_hda_fw_load(struct cs35l56_hda *cs35l56) char *wmfw_filename = NULL; unsigned int preloaded_fw_ver; bool firmware_missing; - int ret = 0; + int ret; - /* Prepare for a new DSP power-up */ + /* + * Prepare for a new DSP power-up. If the DSP has had firmware + * downloaded previously then it needs to be powered down so that it + * can be updated. + */ if (cs35l56->base.fw_patched) cs_dsp_power_down(&cs35l56->cs_dsp); cs35l56->base.fw_patched = false; - pm_runtime_get_sync(cs35l56->base.dev); + ret = pm_runtime_resume_and_get(cs35l56->base.dev); + if (ret < 0) { + dev_err(cs35l56->base.dev, "Failed to resume and get %d\n", ret); + return; + } /* * The firmware can only be upgraded if it is currently running @@ -587,7 +619,6 @@ static int cs35l56_hda_fw_load(struct cs35l56_hda *cs35l56) */ if (!coeff_firmware && firmware_missing) { dev_err(cs35l56->base.dev, ".bin file required but not found\n"); - ret = -ENOENT; goto err_fw_release; } @@ -618,17 +649,15 @@ static int cs35l56_hda_fw_load(struct cs35l56_hda *cs35l56) if (coeff_filename) dev_dbg(cs35l56->base.dev, "Loaded Coefficients: %s\n", coeff_filename); - if (!firmware_missing) { - ret = cs35l56_mbox_send(&cs35l56->base, CS35L56_MBOX_CMD_AUDIO_REINIT); - if (ret) - goto err_powered_up; - } else if (wmfw_firmware || coeff_firmware) { - /* If we downloaded firmware, reset the device and wait for it to boot */ + /* If we downloaded firmware, reset the device and wait for it to boot */ + if (firmware_missing && (wmfw_firmware || coeff_firmware)) { cs35l56_system_reset(&cs35l56->base, false); regcache_mark_dirty(cs35l56->base.regmap); ret = cs35l56_wait_for_firmware_boot(&cs35l56->base); if (ret) goto err_powered_up; + + regcache_cache_only(cs35l56->base.regmap, false); } /* Disable auto-hibernate so that runtime_pm has control */ @@ -646,6 +675,11 @@ static int cs35l56_hda_fw_load(struct cs35l56_hda *cs35l56) if (ret) dev_dbg(cs35l56->base.dev, "%s: cs_dsp_run ret %d\n", __func__, ret); + cs35l56_hda_apply_calibration(cs35l56); + ret = cs35l56_mbox_send(&cs35l56->base, CS35L56_MBOX_CMD_AUDIO_REINIT); + if (ret) + cs_dsp_stop(&cs35l56->cs_dsp); + err_powered_up: if (!cs35l56->base.fw_patched) cs_dsp_power_down(&cs35l56->cs_dsp); @@ -656,34 +690,36 @@ err_fw_release: coeff_firmware, coeff_filename); err_pm_put: pm_runtime_put(cs35l56->base.dev); +} - return ret; +static void cs35l56_hda_dsp_work(struct work_struct *work) +{ + struct cs35l56_hda *cs35l56 = container_of(work, struct cs35l56_hda, dsp_work); + + cs35l56_hda_fw_load(cs35l56); } static int cs35l56_hda_bind(struct device *dev, struct device *master, void *master_data) { struct cs35l56_hda *cs35l56 = dev_get_drvdata(dev); - struct hda_component *comps = master_data; - int ret; + struct hda_component_parent *parent = master_data; + struct hda_component *comp; - if (!comps || cs35l56->index < 0 || cs35l56->index >= HDA_MAX_COMPONENTS) + comp = hda_component_from_index(parent, cs35l56->index); + if (!comp) return -EINVAL; - comps = &comps[cs35l56->index]; - if (comps->dev) + if (comp->dev) return -EBUSY; - comps->dev = dev; - cs35l56->codec = comps->codec; - strscpy(comps->name, dev_name(dev), sizeof(comps->name)); - comps->playback_hook = cs35l56_hda_playback_hook; + comp->dev = dev; + cs35l56->codec = parent->codec; + strscpy(comp->name, dev_name(dev), sizeof(comp->name)); + comp->playback_hook = cs35l56_hda_playback_hook; - ret = cs35l56_hda_fw_load(cs35l56); - if (ret) - return ret; + queue_work(system_long_wq, &cs35l56->dsp_work); cs35l56_hda_create_controls(cs35l56); - cs35l56_hda_add_dsp_controls(cs35l56); #if IS_ENABLED(CONFIG_SND_DEBUG) cs35l56->debugfs_root = debugfs_create_dir(dev_name(cs35l56->base.dev), sound_debugfs_root); @@ -698,7 +734,10 @@ static int cs35l56_hda_bind(struct device *dev, struct device *master, void *mas static void cs35l56_hda_unbind(struct device *dev, struct device *master, void *master_data) { struct cs35l56_hda *cs35l56 = dev_get_drvdata(dev); - struct hda_component *comps = master_data; + struct hda_component_parent *parent = master_data; + struct hda_component *comp; + + cancel_work_sync(&cs35l56->dsp_work); cs35l56_hda_remove_controls(cs35l56); @@ -710,10 +749,11 @@ static void cs35l56_hda_unbind(struct device *dev, struct device *master, void * if (cs35l56->base.fw_patched) cs_dsp_power_down(&cs35l56->cs_dsp); - cs_dsp_remove(&cs35l56->cs_dsp); + comp = hda_component_from_index(parent, cs35l56->index); + if (comp && (comp->dev == dev)) + memset(comp, 0, sizeof(*comp)); - if (comps[cs35l56->index].dev == dev) - memset(&comps[cs35l56->index], 0, sizeof(*comps)); + cs35l56->codec = NULL; dev_dbg(cs35l56->base.dev, "Unbound\n"); } @@ -727,6 +767,8 @@ static int cs35l56_hda_system_suspend(struct device *dev) { struct cs35l56_hda *cs35l56 = dev_get_drvdata(dev); + cs35l56_hda_wait_dsp_ready(cs35l56); + if (cs35l56->playing) cs35l56_hda_pause(cs35l56); @@ -820,13 +862,13 @@ static int cs35l56_hda_system_resume(struct device *dev) cs35l56->suspended = false; + if (!cs35l56->codec) + return 0; + ret = cs35l56_is_fw_reload_needed(&cs35l56->base); dev_dbg(cs35l56->base.dev, "fw_reload_needed: %d\n", ret); - if (ret > 0) { - ret = cs35l56_hda_fw_load(cs35l56); - if (ret) - return ret; - } + if (ret > 0) + queue_work(system_long_wq, &cs35l56->dsp_work); if (cs35l56->playing) cs35l56_hda_play(cs35l56); @@ -834,9 +876,10 @@ static int cs35l56_hda_system_resume(struct device *dev) return 0; } -static int cs35l56_hda_read_acpi(struct cs35l56_hda *cs35l56, int id) +static int cs35l56_hda_read_acpi(struct cs35l56_hda *cs35l56, int hid, int id) { u32 values[HDA_MAX_COMPONENTS]; + char hid_string[8]; struct acpi_device *adev; const char *property, *sub; size_t nval; @@ -847,7 +890,8 @@ static int cs35l56_hda_read_acpi(struct cs35l56_hda *cs35l56, int id) * the serial-multi-instantiate driver, so lookup the node by HID */ if (!ACPI_COMPANION(cs35l56->base.dev)) { - adev = acpi_dev_get_first_match_dev("CSC3556", NULL, -1); + snprintf(hid_string, sizeof(hid_string), "CSC%04X", hid); + adev = acpi_dev_get_first_match_dev(hid_string, NULL, -1); if (!adev) { dev_err(cs35l56->base.dev, "Failed to find an ACPI device for %s\n", dev_name(cs35l56->base.dev)); @@ -935,14 +979,16 @@ err: return ret; } -int cs35l56_hda_common_probe(struct cs35l56_hda *cs35l56, int id) +int cs35l56_hda_common_probe(struct cs35l56_hda *cs35l56, int hid, int id) { int ret; mutex_init(&cs35l56->base.irq_lock); dev_set_drvdata(cs35l56->base.dev, cs35l56); - ret = cs35l56_hda_read_acpi(cs35l56, id); + INIT_WORK(&cs35l56->dsp_work, cs35l56_hda_dsp_work); + + ret = cs35l56_hda_read_acpi(cs35l56, hid, id); if (ret) goto err; @@ -953,6 +999,8 @@ int cs35l56_hda_common_probe(struct cs35l56_hda *cs35l56, int id) goto err; } + cs35l56->base.cal_index = -1; + cs35l56_init_cs_dsp(&cs35l56->base, &cs35l56->cs_dsp); cs35l56->cs_dsp.client_ops = &cs35l56_hda_client_ops; @@ -978,6 +1026,8 @@ int cs35l56_hda_common_probe(struct cs35l56_hda *cs35l56, int id) if (ret) goto err; + regcache_cache_only(cs35l56->base.regmap, false); + ret = cs35l56_set_patch(&cs35l56->base); if (ret) goto err; @@ -990,20 +1040,21 @@ int cs35l56_hda_common_probe(struct cs35l56_hda *cs35l56, int id) if (ret) goto err; + ret = cs35l56_get_calibration(&cs35l56->base); + if (ret) + goto err; + ret = cs_dsp_halo_init(&cs35l56->cs_dsp); if (ret) { dev_err_probe(cs35l56->base.dev, ret, "cs_dsp_halo_init failed\n"); goto err; } - dev_dbg(cs35l56->base.dev, "DSP system name: '%s', amp name: '%s'\n", - cs35l56->system_name, cs35l56->amp_name); + dev_info(cs35l56->base.dev, "DSP system name: '%s', amp name: '%s'\n", + cs35l56->system_name, cs35l56->amp_name); regmap_multi_reg_write(cs35l56->base.regmap, cs35l56_hda_dai_config, ARRAY_SIZE(cs35l56_hda_dai_config)); - ret = cs35l56_force_sync_asp1_registers_from_cache(&cs35l56->base); - if (ret) - goto err; /* * By default only enable one ASP1TXn, where n=amplifier index, @@ -1017,41 +1068,44 @@ int cs35l56_hda_common_probe(struct cs35l56_hda *cs35l56, int id) pm_runtime_mark_last_busy(cs35l56->base.dev); pm_runtime_enable(cs35l56->base.dev); + cs35l56->base.init_done = true; + ret = component_add(cs35l56->base.dev, &cs35l56_hda_comp_ops); if (ret) { dev_err(cs35l56->base.dev, "Register component failed: %d\n", ret); goto pm_err; } - cs35l56->base.init_done = true; - return 0; pm_err: pm_runtime_disable(cs35l56->base.dev); + cs_dsp_remove(&cs35l56->cs_dsp); err: gpiod_set_value_cansleep(cs35l56->base.reset_gpio, 0); return ret; } -EXPORT_SYMBOL_NS_GPL(cs35l56_hda_common_probe, SND_HDA_SCODEC_CS35L56); +EXPORT_SYMBOL_NS_GPL(cs35l56_hda_common_probe, "SND_HDA_SCODEC_CS35L56"); void cs35l56_hda_remove(struct device *dev) { struct cs35l56_hda *cs35l56 = dev_get_drvdata(dev); + component_del(cs35l56->base.dev, &cs35l56_hda_comp_ops); + pm_runtime_dont_use_autosuspend(cs35l56->base.dev); pm_runtime_get_sync(cs35l56->base.dev); pm_runtime_disable(cs35l56->base.dev); - component_del(cs35l56->base.dev, &cs35l56_hda_comp_ops); + cs_dsp_remove(&cs35l56->cs_dsp); kfree(cs35l56->system_name); pm_runtime_put_noidle(cs35l56->base.dev); gpiod_set_value_cansleep(cs35l56->base.reset_gpio, 0); } -EXPORT_SYMBOL_NS_GPL(cs35l56_hda_remove, SND_HDA_SCODEC_CS35L56); +EXPORT_SYMBOL_NS_GPL(cs35l56_hda_remove, "SND_HDA_SCODEC_CS35L56"); const struct dev_pm_ops cs35l56_hda_pm_ops = { RUNTIME_PM_OPS(cs35l56_hda_runtime_suspend, cs35l56_hda_runtime_resume, NULL) @@ -1061,13 +1115,14 @@ const struct dev_pm_ops cs35l56_hda_pm_ops = { NOIRQ_SYSTEM_SLEEP_PM_OPS(cs35l56_hda_system_suspend_no_irq, cs35l56_hda_system_resume_no_irq) }; -EXPORT_SYMBOL_NS_GPL(cs35l56_hda_pm_ops, SND_HDA_SCODEC_CS35L56); +EXPORT_SYMBOL_NS_GPL(cs35l56_hda_pm_ops, "SND_HDA_SCODEC_CS35L56"); MODULE_DESCRIPTION("CS35L56 HDA Driver"); -MODULE_IMPORT_NS(SND_HDA_CIRRUS_SCODEC); -MODULE_IMPORT_NS(SND_HDA_CS_DSP_CONTROLS); -MODULE_IMPORT_NS(SND_SOC_CS35L56_SHARED); +MODULE_IMPORT_NS("FW_CS_DSP"); +MODULE_IMPORT_NS("SND_HDA_CIRRUS_SCODEC"); +MODULE_IMPORT_NS("SND_HDA_CS_DSP_CONTROLS"); +MODULE_IMPORT_NS("SND_SOC_CS35L56_SHARED"); +MODULE_IMPORT_NS("SND_SOC_CS_AMP_LIB"); MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>"); MODULE_AUTHOR("Simon Trimmer <simont@opensource.cirrus.com>"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(FW_CS_DSP); diff --git a/sound/pci/hda/cs35l56_hda.h b/sound/pci/hda/cs35l56_hda.h index 6e5bc5397db5..38d94fb213a5 100644 --- a/sound/pci/hda/cs35l56_hda.h +++ b/sound/pci/hda/cs35l56_hda.h @@ -14,6 +14,7 @@ #include <linux/firmware/cirrus/cs_dsp.h> #include <linux/firmware/cirrus/wmfw.h> #include <linux/regulator/consumer.h> +#include <linux/workqueue.h> #include <sound/cs35l56.h> struct dentry; @@ -21,6 +22,7 @@ struct dentry; struct cs35l56_hda { struct cs35l56_base base; struct hda_codec *codec; + struct work_struct dsp_work; int index; const char *system_name; @@ -42,7 +44,7 @@ struct cs35l56_hda { extern const struct dev_pm_ops cs35l56_hda_pm_ops; -int cs35l56_hda_common_probe(struct cs35l56_hda *cs35l56, int id); +int cs35l56_hda_common_probe(struct cs35l56_hda *cs35l56, int hid, int id); void cs35l56_hda_remove(struct device *dev); #endif /*__CS35L56_HDA_H__*/ diff --git a/sound/pci/hda/cs35l56_hda_i2c.c b/sound/pci/hda/cs35l56_hda_i2c.c index a9ef6d86de83..c7b836613149 100644 --- a/sound/pci/hda/cs35l56_hda_i2c.c +++ b/sound/pci/hda/cs35l56_hda_i2c.c @@ -13,6 +13,7 @@ static int cs35l56_hda_i2c_probe(struct i2c_client *clt) { + const struct i2c_device_id *id = i2c_client_get_device_id(clt); struct cs35l56_hda *cs35l56; int ret; @@ -33,7 +34,7 @@ static int cs35l56_hda_i2c_probe(struct i2c_client *clt) return ret; } - ret = cs35l56_hda_common_probe(cs35l56, clt->addr); + ret = cs35l56_hda_common_probe(cs35l56, id->driver_data, clt->addr); if (ret) return ret; ret = cs35l56_irq_request(&cs35l56->base, clt->irq); @@ -49,14 +50,25 @@ static void cs35l56_hda_i2c_remove(struct i2c_client *clt) } static const struct i2c_device_id cs35l56_hda_i2c_id[] = { - { "cs35l56-hda", 0 }, + { "cs35l54-hda", 0x3554 }, + { "cs35l56-hda", 0x3556 }, + { "cs35l57-hda", 0x3557 }, {} }; +static const struct acpi_device_id cs35l56_acpi_hda_match[] = { + { "CSC3554", 0 }, + { "CSC3556", 0 }, + { "CSC3557", 0 }, + {} +}; +MODULE_DEVICE_TABLE(acpi, cs35l56_acpi_hda_match); + static struct i2c_driver cs35l56_hda_i2c_driver = { .driver = { - .name = "cs35l56-hda", - .pm = &cs35l56_hda_pm_ops, + .name = "cs35l56-hda", + .acpi_match_table = cs35l56_acpi_hda_match, + .pm = &cs35l56_hda_pm_ops, }, .id_table = cs35l56_hda_i2c_id, .probe = cs35l56_hda_i2c_probe, @@ -65,8 +77,8 @@ static struct i2c_driver cs35l56_hda_i2c_driver = { module_i2c_driver(cs35l56_hda_i2c_driver); MODULE_DESCRIPTION("HDA CS35L56 I2C driver"); -MODULE_IMPORT_NS(SND_HDA_SCODEC_CS35L56); -MODULE_IMPORT_NS(SND_SOC_CS35L56_SHARED); +MODULE_IMPORT_NS("SND_HDA_SCODEC_CS35L56"); +MODULE_IMPORT_NS("SND_SOC_CS35L56_SHARED"); MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>"); MODULE_AUTHOR("Simon Trimmer <simont@opensource.cirrus.com>"); MODULE_LICENSE("GPL"); diff --git a/sound/pci/hda/cs35l56_hda_spi.c b/sound/pci/hda/cs35l56_hda_spi.c index 080426de9083..903578466905 100644 --- a/sound/pci/hda/cs35l56_hda_spi.c +++ b/sound/pci/hda/cs35l56_hda_spi.c @@ -13,6 +13,7 @@ static int cs35l56_hda_spi_probe(struct spi_device *spi) { + const struct spi_device_id *id = spi_get_device_id(spi); struct cs35l56_hda *cs35l56; int ret; @@ -21,6 +22,9 @@ static int cs35l56_hda_spi_probe(struct spi_device *spi) return -ENOMEM; cs35l56->base.dev = &spi->dev; + ret = cs35l56_init_config_for_spi(&cs35l56->base, spi); + if (ret) + return ret; #ifdef CS35L56_WAKE_HOLD_TIME_US cs35l56->base.can_hibernate = true; @@ -33,7 +37,7 @@ static int cs35l56_hda_spi_probe(struct spi_device *spi) return ret; } - ret = cs35l56_hda_common_probe(cs35l56, spi_get_chipselect(spi, 0)); + ret = cs35l56_hda_common_probe(cs35l56, id->driver_data, spi_get_chipselect(spi, 0)); if (ret) return ret; ret = cs35l56_irq_request(&cs35l56->base, spi->irq); @@ -49,14 +53,25 @@ static void cs35l56_hda_spi_remove(struct spi_device *spi) } static const struct spi_device_id cs35l56_hda_spi_id[] = { - { "cs35l56-hda", 0 }, + { "cs35l54-hda", 0x3554 }, + { "cs35l56-hda", 0x3556 }, + { "cs35l57-hda", 0x3557 }, + {} +}; + +static const struct acpi_device_id cs35l56_acpi_hda_match[] = { + { "CSC3554", 0 }, + { "CSC3556", 0 }, + { "CSC3557", 0 }, {} }; +MODULE_DEVICE_TABLE(acpi, cs35l56_acpi_hda_match); static struct spi_driver cs35l56_hda_spi_driver = { .driver = { - .name = "cs35l56-hda", - .pm = &cs35l56_hda_pm_ops, + .name = "cs35l56-hda", + .acpi_match_table = cs35l56_acpi_hda_match, + .pm = &cs35l56_hda_pm_ops, }, .id_table = cs35l56_hda_spi_id, .probe = cs35l56_hda_spi_probe, @@ -65,8 +80,8 @@ static struct spi_driver cs35l56_hda_spi_driver = { module_spi_driver(cs35l56_hda_spi_driver); MODULE_DESCRIPTION("HDA CS35L56 SPI driver"); -MODULE_IMPORT_NS(SND_HDA_SCODEC_CS35L56); -MODULE_IMPORT_NS(SND_SOC_CS35L56_SHARED); +MODULE_IMPORT_NS("SND_HDA_SCODEC_CS35L56"); +MODULE_IMPORT_NS("SND_SOC_CS35L56_SHARED"); MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>"); MODULE_AUTHOR("Simon Trimmer <simont@opensource.cirrus.com>"); MODULE_LICENSE("GPL"); diff --git a/sound/pci/hda/hda_auto_parser.c b/sound/pci/hda/hda_auto_parser.c index 7c6b1fe8dfcc..8923813ce424 100644 --- a/sound/pci/hda/hda_auto_parser.c +++ b/sound/pci/hda/hda_auto_parser.c @@ -80,7 +80,11 @@ static int compare_input_type(const void *ap, const void *bp) /* In case one has boost and the other one has not, pick the one with boost first. */ - return (int)(b->has_boost_on_pin - a->has_boost_on_pin); + if (a->has_boost_on_pin != b->has_boost_on_pin) + return (int)(b->has_boost_on_pin - a->has_boost_on_pin); + + /* Keep the original order */ + return a->order - b->order; } /* Reorder the surround channels @@ -400,6 +404,8 @@ int snd_hda_parse_pin_defcfg(struct hda_codec *codec, reorder_outputs(cfg->speaker_outs, cfg->speaker_pins); /* sort inputs in the order of AUTO_PIN_* type */ + for (i = 0; i < cfg->num_inputs; i++) + cfg->inputs[i].order = i; sort(cfg->inputs, cfg->num_inputs, sizeof(cfg->inputs[0]), compare_input_type, NULL); @@ -933,6 +939,7 @@ void snd_hda_pick_pin_fixup(struct hda_codec *codec, bool match_all_pins) { const struct snd_hda_pin_quirk *pq; + const char *name = NULL; if (codec->fixup_id != HDA_FIXUP_ID_NOT_SET) return; @@ -946,9 +953,10 @@ void snd_hda_pick_pin_fixup(struct hda_codec *codec, codec->fixup_id = pq->value; #ifdef CONFIG_SND_DEBUG_VERBOSE codec->fixup_name = pq->name; - codec_dbg(codec, "%s: picked fixup %s (pin match)\n", - codec->core.chip_name, codec->fixup_name); + name = pq->name; #endif + codec_info(codec, "%s: picked fixup %s (pin match)\n", + codec->core.chip_name, name ? name : ""); codec->fixup_list = fixlist; return; } @@ -956,6 +964,28 @@ void snd_hda_pick_pin_fixup(struct hda_codec *codec, } EXPORT_SYMBOL_GPL(snd_hda_pick_pin_fixup); +/* check whether the given quirk entry matches with vendor/device pair */ +static bool hda_quirk_match(u16 vendor, u16 device, const struct hda_quirk *q) +{ + if (q->subvendor != vendor) + return false; + return !q->subdevice || + (device & q->subdevice_mask) == q->subdevice; +} + +/* look through the quirk list and return the matching entry */ +static const struct hda_quirk * +hda_quirk_lookup_id(u16 vendor, u16 device, const struct hda_quirk *list) +{ + const struct hda_quirk *q; + + for (q = list; q->subvendor || q->subdevice; q++) { + if (hda_quirk_match(vendor, device, q)) + return q; + } + return NULL; +} + /** * snd_hda_pick_fixup - Pick up a fixup matching with PCI/codec SSID or model string * @codec: the HDA codec @@ -975,14 +1005,16 @@ EXPORT_SYMBOL_GPL(snd_hda_pick_pin_fixup); */ void snd_hda_pick_fixup(struct hda_codec *codec, const struct hda_model_fixup *models, - const struct snd_pci_quirk *quirk, + const struct hda_quirk *quirk, const struct hda_fixup *fixlist) { - const struct snd_pci_quirk *q; + const struct hda_quirk *q; int id = HDA_FIXUP_ID_NOT_SET; const char *name = NULL; const char *type = NULL; unsigned int vendor, device; + u16 pci_vendor, pci_device; + u16 codec_vendor, codec_device; if (codec->fixup_id != HDA_FIXUP_ID_NOT_SET) return; @@ -991,8 +1023,8 @@ void snd_hda_pick_fixup(struct hda_codec *codec, if (codec->modelname && !strcmp(codec->modelname, "nofixup")) { id = HDA_FIXUP_ID_NO_FIXUP; fixlist = NULL; - codec_dbg(codec, "%s: picked no fixup (nofixup specified)\n", - codec->core.chip_name); + codec_info(codec, "%s: picked no fixup (nofixup specified)\n", + codec->core.chip_name); goto found; } @@ -1002,8 +1034,8 @@ void snd_hda_pick_fixup(struct hda_codec *codec, if (!strcmp(codec->modelname, models->name)) { id = models->id; name = models->name; - codec_dbg(codec, "%s: picked fixup %s (model specified)\n", - codec->core.chip_name, codec->fixup_name); + codec_info(codec, "%s: picked fixup %s (model specified)\n", + codec->core.chip_name, name); goto found; } models++; @@ -1013,27 +1045,42 @@ void snd_hda_pick_fixup(struct hda_codec *codec, if (!quirk) return; + if (codec->bus->pci) { + pci_vendor = codec->bus->pci->subsystem_vendor; + pci_device = codec->bus->pci->subsystem_device; + } + + codec_vendor = codec->core.subsystem_id >> 16; + codec_device = codec->core.subsystem_id & 0xffff; + /* match with the SSID alias given by the model string "XXXX:YYYY" */ if (codec->modelname && sscanf(codec->modelname, "%04x:%04x", &vendor, &device) == 2) { - q = snd_pci_quirk_lookup_id(vendor, device, quirk); + q = hda_quirk_lookup_id(vendor, device, quirk); if (q) { type = "alias SSID"; goto found_device; } } - /* match with the PCI SSID */ - q = snd_pci_quirk_lookup(codec->bus->pci, quirk); - if (q) { - type = "PCI SSID"; - goto found_device; + /* match primarily with the PCI SSID */ + for (q = quirk; q->subvendor || q->subdevice; q++) { + /* if the entry is specific to codec SSID, check with it */ + if (!codec->bus->pci || q->match_codec_ssid) { + if (hda_quirk_match(codec_vendor, codec_device, q)) { + type = "codec SSID"; + goto found_device; + } + } else { + if (hda_quirk_match(pci_vendor, pci_device, q)) { + type = "PCI SSID"; + goto found_device; + } + } } /* match with the codec SSID */ - q = snd_pci_quirk_lookup_id(codec->core.subsystem_id >> 16, - codec->core.subsystem_id & 0xffff, - quirk); + q = hda_quirk_lookup_id(codec_vendor, codec_device, quirk); if (q) { type = "codec SSID"; goto found_device; @@ -1046,9 +1093,9 @@ void snd_hda_pick_fixup(struct hda_codec *codec, #ifdef CONFIG_SND_DEBUG_VERBOSE name = q->name; #endif - codec_dbg(codec, "%s: picked fixup %s for %s %04x:%04x\n", - codec->core.chip_name, name ? name : "", - type, q->subvendor, q->subdevice); + codec_info(codec, "%s: picked fixup %s for %s %04x:%04x\n", + codec->core.chip_name, name ? name : "", + type, q->subvendor, q->subdevice); found: codec->fixup_id = id; codec->fixup_list = fixlist; diff --git a/sound/pci/hda/hda_auto_parser.h b/sound/pci/hda/hda_auto_parser.h index 579b11beac71..87af3d8c02f7 100644 --- a/sound/pci/hda/hda_auto_parser.h +++ b/sound/pci/hda/hda_auto_parser.h @@ -37,6 +37,7 @@ struct auto_pin_cfg_item { unsigned int is_headset_mic:1; unsigned int is_headphone_mic:1; /* Mic-only in headphone jack */ unsigned int has_boost_on_pin:1; + int order; }; struct auto_pin_cfg; diff --git a/sound/pci/hda/hda_beep.c b/sound/pci/hda/hda_beep.c index e63621bcb214..e51d47572557 100644 --- a/sound/pci/hda/hda_beep.c +++ b/sound/pci/hda/hda_beep.c @@ -231,7 +231,6 @@ int snd_hda_attach_beep_device(struct hda_codec *codec, int nid) codec->beep = beep; INIT_WORK(&beep->beep_work, &snd_hda_generate_beep); - mutex_init(&beep->mutex); input_dev = input_allocate_device(); if (!input_dev) { diff --git a/sound/pci/hda/hda_beep.h b/sound/pci/hda/hda_beep.h index db76e3ddba65..923ea862446a 100644 --- a/sound/pci/hda/hda_beep.h +++ b/sound/pci/hda/hda_beep.h @@ -27,7 +27,6 @@ struct hda_beep { unsigned int playing:1; unsigned int keep_power_at_enable:1; /* set by driver */ struct work_struct beep_work; /* scheduled task for beep event */ - struct mutex mutex; void (*power_hook)(struct hda_beep *beep, bool on); }; diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 12f02cdc9659..46a220404999 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -616,7 +616,6 @@ void snd_hda_shutup_pins(struct hda_codec *codec) } EXPORT_SYMBOL_GPL(snd_hda_shutup_pins); -#ifdef CONFIG_PM /* Restore the pin controls cleared previously via snd_hda_shutup_pins() */ static void restore_shutup_pins(struct hda_codec *codec) { @@ -634,7 +633,6 @@ static void restore_shutup_pins(struct hda_codec *codec) } codec->pins_shutup = 0; } -#endif static void hda_jackpoll_work(struct work_struct *work) { @@ -1001,9 +999,7 @@ int snd_hda_codec_device_new(struct hda_bus *bus, struct snd_card *card, codec->card = card; codec->addr = codec_addr; -#ifdef CONFIG_PM codec->power_jiffies = jiffies; -#endif snd_hda_sysfs_init(codec); @@ -1238,7 +1234,6 @@ static void purify_inactive_streams(struct hda_codec *codec) } } -#ifdef CONFIG_PM /* clean up all streams; called from suspend */ static void hda_cleanup_all_streams(struct hda_codec *codec) { @@ -1250,7 +1245,6 @@ static void hda_cleanup_all_streams(struct hda_codec *codec) really_cleanup_stream(codec, p); } } -#endif /* * amp access functions @@ -1502,7 +1496,7 @@ update_amp_value(struct hda_codec *codec, hda_nid_t nid, /* ofs = 0: raw max value */ maxval = get_amp_max_value(codec, nid, dir, 0); if (val > maxval) - val = maxval; + return -EINVAL; return snd_hda_codec_amp_update(codec, nid, ch, dir, idx, HDA_AMP_VOLMASK, val); } @@ -1553,13 +1547,21 @@ int snd_hda_mixer_amp_volume_put(struct snd_kcontrol *kcontrol, unsigned int ofs = get_amp_offset(kcontrol); long *valp = ucontrol->value.integer.value; int change = 0; + int err; if (chs & 1) { - change = update_amp_value(codec, nid, 0, dir, idx, ofs, *valp); + err = update_amp_value(codec, nid, 0, dir, idx, ofs, *valp); + if (err < 0) + return err; + change |= err; valp++; } - if (chs & 2) - change |= update_amp_value(codec, nid, 1, dir, idx, ofs, *valp); + if (chs & 2) { + err = update_amp_value(codec, nid, 1, dir, idx, ofs, *valp); + if (err < 0) + return err; + change |= err; + } return change; } EXPORT_SYMBOL_GPL(snd_hda_mixer_amp_volume_put); @@ -1732,9 +1734,9 @@ EXPORT_SYMBOL_GPL(snd_hda_ctl_add); /** * snd_hda_add_nid - Assign a NID to a control element * @codec: HD-audio codec - * @nid: corresponding NID (optional) * @kctl: the control element to assign * @index: index to kctl + * @nid: corresponding NID (optional) * * Add the given control element to an array inside the codec instance. * This function is used when #snd_hda_ctl_add cannot be used for 1:1 @@ -2155,15 +2157,20 @@ int snd_hda_mixer_amp_switch_put(struct snd_kcontrol *kcontrol, int change = 0; if (chs & 1) { + if (*valp < 0 || *valp > 1) + return -EINVAL; change = snd_hda_codec_amp_update(codec, nid, 0, dir, idx, HDA_AMP_MUTE, *valp ? 0 : HDA_AMP_MUTE); valp++; } - if (chs & 2) + if (chs & 2) { + if (*valp < 0 || *valp > 1) + return -EINVAL; change |= snd_hda_codec_amp_update(codec, nid, 1, dir, idx, HDA_AMP_MUTE, *valp ? 0 : HDA_AMP_MUTE); + } hda_call_check_power_status(codec, nid); return change; } @@ -2463,7 +2470,9 @@ int snd_hda_create_dig_out_ctls(struct hda_codec *codec, break; id = kctl->id; id.index = spdif_index; - snd_ctl_rename_id(codec->card, &kctl->id, &id); + err = snd_ctl_rename_id(codec->card, &kctl->id, &id); + if (err < 0) + return err; } bus->primary_dig_out_type = HDA_PCM_TYPE_HDMI; } @@ -2858,7 +2867,6 @@ static void hda_exec_init_verbs(struct hda_codec *codec) static inline void hda_exec_init_verbs(struct hda_codec *codec) {} #endif -#ifdef CONFIG_PM /* update the power on/off account with the current jiffies */ static void update_power_acct(struct hda_codec *codec, bool on) { @@ -2966,9 +2974,6 @@ static int hda_codec_runtime_resume(struct device *dev) return 0; } -#endif /* CONFIG_PM */ - -#ifdef CONFIG_PM_SLEEP static int hda_codec_pm_prepare(struct device *dev) { struct hda_codec *codec = dev_to_hda_codec(dev); @@ -3023,22 +3028,19 @@ static int hda_codec_pm_restore(struct device *dev) dev->power.power_state = PMSG_RESTORE; return pm_runtime_force_resume(dev); } -#endif /* CONFIG_PM_SLEEP */ /* referred in hda_bind.c */ const struct dev_pm_ops hda_codec_driver_pm = { -#ifdef CONFIG_PM_SLEEP - .prepare = hda_codec_pm_prepare, - .complete = hda_codec_pm_complete, - .suspend = hda_codec_pm_suspend, - .resume = hda_codec_pm_resume, - .freeze = hda_codec_pm_freeze, - .thaw = hda_codec_pm_thaw, - .poweroff = hda_codec_pm_suspend, - .restore = hda_codec_pm_restore, -#endif /* CONFIG_PM_SLEEP */ - SET_RUNTIME_PM_OPS(hda_codec_runtime_suspend, hda_codec_runtime_resume, - NULL) + .prepare = pm_sleep_ptr(hda_codec_pm_prepare), + .complete = pm_sleep_ptr(hda_codec_pm_complete), + .suspend = pm_sleep_ptr(hda_codec_pm_suspend), + .resume = pm_sleep_ptr(hda_codec_pm_resume), + .freeze = pm_sleep_ptr(hda_codec_pm_freeze), + .thaw = pm_sleep_ptr(hda_codec_pm_thaw), + .poweroff = pm_sleep_ptr(hda_codec_pm_suspend), + .restore = pm_sleep_ptr(hda_codec_pm_restore), + .runtime_suspend = pm_ptr(hda_codec_runtime_suspend), + .runtime_resume = pm_ptr(hda_codec_runtime_resume), }; /* suspend the codec at shutdown; called from driver's shutdown callback */ @@ -3313,7 +3315,7 @@ int snd_hda_codec_parse_pcms(struct hda_codec *codec) list_for_each_entry(cpcm, &codec->pcm_list_head, list) { int stream; - for (stream = 0; stream < 2; stream++) { + for_each_pcm_streams(stream) { struct hda_pcm_stream *info = &cpcm->stream[stream]; if (!info->substreams) @@ -3425,7 +3427,6 @@ int snd_hda_add_new_ctls(struct hda_codec *codec, } EXPORT_SYMBOL_GPL(snd_hda_add_new_ctls); -#ifdef CONFIG_PM /** * snd_hda_codec_set_power_save - Configure codec's runtime PM * @codec: codec device to configure @@ -3516,7 +3517,6 @@ int snd_hda_check_amp_list_power(struct hda_codec *codec, return 0; } EXPORT_SYMBOL_GPL(snd_hda_check_amp_list_power); -#endif /* * input MUX helper @@ -4060,12 +4060,10 @@ void snd_hda_bus_reset_codecs(struct hda_bus *bus) /* FIXME: maybe a better way needed for forced reset */ if (current_work() != &codec->jackpoll_work.work) cancel_delayed_work_sync(&codec->jackpoll_work); -#ifdef CONFIG_PM if (hda_codec_is_power_on(codec)) { hda_call_codec_suspend(codec); hda_call_codec_resume(codec); } -#endif } } diff --git a/sound/pci/hda/hda_component.c b/sound/pci/hda/hda_component.c new file mode 100644 index 000000000000..71860e2d6377 --- /dev/null +++ b/sound/pci/hda/hda_component.c @@ -0,0 +1,212 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * HD audio Component Binding Interface + * + * Copyright (C) 2021, 2023 Cirrus Logic, Inc. and + * Cirrus Logic International Semiconductor Ltd. + */ + +#include <linux/acpi.h> +#include <linux/component.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <sound/hda_codec.h> +#include "hda_component.h" +#include "hda_local.h" + +#ifdef CONFIG_ACPI +void hda_component_acpi_device_notify(struct hda_component_parent *parent, + acpi_handle handle, u32 event, void *data) +{ + struct hda_component *comp; + int i; + + mutex_lock(&parent->mutex); + for (i = 0; i < ARRAY_SIZE(parent->comps); i++) { + comp = hda_component_from_index(parent, i); + if (comp->dev && comp->acpi_notify) + comp->acpi_notify(acpi_device_handle(comp->adev), event, comp->dev); + } + mutex_unlock(&parent->mutex); +} +EXPORT_SYMBOL_NS_GPL(hda_component_acpi_device_notify, "SND_HDA_SCODEC_COMPONENT"); + +int hda_component_manager_bind_acpi_notifications(struct hda_codec *cdc, + struct hda_component_parent *parent, + acpi_notify_handler handler, void *data) +{ + bool support_notifications = false; + struct acpi_device *adev; + struct hda_component *comp; + int ret; + int i; + + adev = parent->comps[0].adev; + if (!acpi_device_handle(adev)) + return 0; + + for (i = 0; i < ARRAY_SIZE(parent->comps); i++) { + comp = hda_component_from_index(parent, i); + support_notifications = support_notifications || + comp->acpi_notifications_supported; + } + + if (support_notifications) { + ret = acpi_install_notify_handler(adev->handle, ACPI_DEVICE_NOTIFY, + handler, data); + if (ret < 0) { + codec_warn(cdc, "Failed to install notify handler: %d\n", ret); + return 0; + } + + codec_dbg(cdc, "Notify handler installed\n"); + } + + return 0; +} +EXPORT_SYMBOL_NS_GPL(hda_component_manager_bind_acpi_notifications, "SND_HDA_SCODEC_COMPONENT"); + +void hda_component_manager_unbind_acpi_notifications(struct hda_codec *cdc, + struct hda_component_parent *parent, + acpi_notify_handler handler) +{ + struct acpi_device *adev; + int ret; + + adev = parent->comps[0].adev; + if (!acpi_device_handle(adev)) + return; + + ret = acpi_remove_notify_handler(adev->handle, ACPI_DEVICE_NOTIFY, handler); + if (ret < 0) + codec_warn(cdc, "Failed to uninstall notify handler: %d\n", ret); +} +EXPORT_SYMBOL_NS_GPL(hda_component_manager_unbind_acpi_notifications, "SND_HDA_SCODEC_COMPONENT"); +#endif /* ifdef CONFIG_ACPI */ + +void hda_component_manager_playback_hook(struct hda_component_parent *parent, int action) +{ + struct hda_component *comp; + int i; + + mutex_lock(&parent->mutex); + for (i = 0; i < ARRAY_SIZE(parent->comps); i++) { + comp = hda_component_from_index(parent, i); + if (comp->dev && comp->pre_playback_hook) + comp->pre_playback_hook(comp->dev, action); + } + for (i = 0; i < ARRAY_SIZE(parent->comps); i++) { + comp = hda_component_from_index(parent, i); + if (comp->dev && comp->playback_hook) + comp->playback_hook(comp->dev, action); + } + for (i = 0; i < ARRAY_SIZE(parent->comps); i++) { + comp = hda_component_from_index(parent, i); + if (comp->dev && comp->post_playback_hook) + comp->post_playback_hook(comp->dev, action); + } + mutex_unlock(&parent->mutex); +} +EXPORT_SYMBOL_NS_GPL(hda_component_manager_playback_hook, "SND_HDA_SCODEC_COMPONENT"); + +struct hda_scodec_match { + const char *bus; + const char *hid; + const char *match_str; + int index; +}; + +/* match the device name in a slightly relaxed manner */ +static int hda_comp_match_dev_name(struct device *dev, void *data) +{ + struct hda_scodec_match *p = data; + const char *d = dev_name(dev); + int n = strlen(p->bus); + char tmp[32]; + + /* check the bus name */ + if (strncmp(d, p->bus, n)) + return 0; + /* skip the bus number */ + if (isdigit(d[n])) + n++; + /* the rest must be exact matching */ + snprintf(tmp, sizeof(tmp), p->match_str, p->hid, p->index); + return !strcmp(d + n, tmp); +} + +int hda_component_manager_bind(struct hda_codec *cdc, + struct hda_component_parent *parent) +{ + int ret; + + /* Init shared and component specific data */ + memset(parent->comps, 0, sizeof(parent->comps)); + + mutex_lock(&parent->mutex); + ret = component_bind_all(hda_codec_dev(cdc), parent); + mutex_unlock(&parent->mutex); + + return ret; +} +EXPORT_SYMBOL_NS_GPL(hda_component_manager_bind, "SND_HDA_SCODEC_COMPONENT"); + +int hda_component_manager_init(struct hda_codec *cdc, + struct hda_component_parent *parent, int count, + const char *bus, const char *hid, + const char *match_str, + const struct component_master_ops *ops) +{ + struct device *dev = hda_codec_dev(cdc); + struct component_match *match = NULL; + struct hda_scodec_match *sm; + int ret, i; + + if (parent->codec) { + codec_err(cdc, "Component binding already created (SSID: %x)\n", + cdc->core.subsystem_id); + return -EINVAL; + } + parent->codec = cdc; + + mutex_init(&parent->mutex); + + for (i = 0; i < count; i++) { + sm = devm_kmalloc(dev, sizeof(*sm), GFP_KERNEL); + if (!sm) + return -ENOMEM; + + sm->bus = bus; + sm->hid = hid; + sm->match_str = match_str; + sm->index = i; + component_match_add(dev, &match, hda_comp_match_dev_name, sm); + } + + ret = component_master_add_with_match(dev, ops, match); + if (ret) + codec_err(cdc, "Fail to register component aggregator %d\n", ret); + + return ret; +} +EXPORT_SYMBOL_NS_GPL(hda_component_manager_init, "SND_HDA_SCODEC_COMPONENT"); + +void hda_component_manager_free(struct hda_component_parent *parent, + const struct component_master_ops *ops) +{ + struct device *dev; + + if (!parent->codec) + return; + + dev = hda_codec_dev(parent->codec); + + component_master_del(dev, ops); + + parent->codec = NULL; +} +EXPORT_SYMBOL_NS_GPL(hda_component_manager_free, "SND_HDA_SCODEC_COMPONENT"); + +MODULE_DESCRIPTION("HD Audio component binding library"); +MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/pci/hda/hda_component.h b/sound/pci/hda/hda_component.h index bbd6f0ed16c1..7ee37154749f 100644 --- a/sound/pci/hda/hda_component.h +++ b/sound/pci/hda/hda_component.h @@ -6,8 +6,13 @@ * Cirrus Logic International Semiconductor Ltd. */ +#ifndef __HDA_COMPONENT_H__ +#define __HDA_COMPONENT_H__ + #include <linux/acpi.h> #include <linux/component.h> +#include <linux/mutex.h> +#include <sound/hda_codec.h> #define HDA_MAX_COMPONENTS 4 #define HDA_MAX_NAME_SIZE 50 @@ -15,7 +20,6 @@ struct hda_component { struct device *dev; char name[HDA_MAX_NAME_SIZE]; - struct hda_codec *codec; struct acpi_device *adev; bool acpi_notifications_supported; void (*acpi_notify)(acpi_handle handle, u32 event, struct device *dev); @@ -23,3 +27,77 @@ struct hda_component { void (*playback_hook)(struct device *dev, int action); void (*post_playback_hook)(struct device *dev, int action); }; + +struct hda_component_parent { + struct mutex mutex; + struct hda_codec *codec; + struct hda_component comps[HDA_MAX_COMPONENTS]; +}; + +#ifdef CONFIG_ACPI +void hda_component_acpi_device_notify(struct hda_component_parent *parent, + acpi_handle handle, u32 event, void *data); +int hda_component_manager_bind_acpi_notifications(struct hda_codec *cdc, + struct hda_component_parent *parent, + acpi_notify_handler handler, void *data); +void hda_component_manager_unbind_acpi_notifications(struct hda_codec *cdc, + struct hda_component_parent *parent, + acpi_notify_handler handler); +#else +static inline void hda_component_acpi_device_notify(struct hda_component_parent *parent, + acpi_handle handle, + u32 event, + void *data) +{ +} + +static inline int hda_component_manager_bind_acpi_notifications(struct hda_codec *cdc, + struct hda_component_parent *parent, + acpi_notify_handler handler, + void *data) + +{ + return 0; +} + +static inline void hda_component_manager_unbind_acpi_notifications(struct hda_codec *cdc, + struct hda_component_parent *parent, + acpi_notify_handler handler) +{ +} +#endif /* ifdef CONFIG_ACPI */ + +void hda_component_manager_playback_hook(struct hda_component_parent *parent, int action); + +int hda_component_manager_init(struct hda_codec *cdc, + struct hda_component_parent *parent, int count, + const char *bus, const char *hid, + const char *match_str, + const struct component_master_ops *ops); + +void hda_component_manager_free(struct hda_component_parent *parent, + const struct component_master_ops *ops); + +int hda_component_manager_bind(struct hda_codec *cdc, struct hda_component_parent *parent); + +static inline struct hda_component *hda_component_from_index(struct hda_component_parent *parent, + int index) +{ + if (!parent) + return NULL; + + if (index < 0 || index >= ARRAY_SIZE(parent->comps)) + return NULL; + + return &parent->comps[index]; +} + +static inline void hda_component_manager_unbind(struct hda_codec *cdc, + struct hda_component_parent *parent) +{ + mutex_lock(&parent->mutex); + component_unbind_all(hda_codec_dev(cdc), parent); + mutex_unlock(&parent->mutex); +} + +#endif /* ifndef __HDA_COMPONENT_H__ */ diff --git a/sound/pci/hda/hda_controller.c b/sound/pci/hda/hda_controller.c index efe98f6f19a3..f3330b7e0fcf 100644 --- a/sound/pci/hda/hda_controller.c +++ b/sound/pci/hda/hda_controller.c @@ -3,7 +3,7 @@ * * Implementation of primary alsa driver code base for Intel HD Audio. * - * Copyright(c) 2004 Intel Corporation. All rights reserved. + * Copyright(c) 2004 Intel Corporation * * Copyright (c) 2004 Takashi Iwai <tiwai@suse.de> * PeiSen Hou <pshou@realtek.com.tw> @@ -24,6 +24,7 @@ #include <sound/core.h> #include <sound/initval.h> +#include <sound/pcm_params.h> #include "hda_controller.h" #include "hda_local.h" @@ -108,6 +109,7 @@ static int azx_pcm_hw_params(struct snd_pcm_substream *substream, struct azx_pcm *apcm = snd_pcm_substream_chip(substream); struct azx *chip = apcm->chip; struct azx_dev *azx_dev = get_azx_dev(substream); + struct hdac_stream *hdas = azx_stream(azx_dev); int ret = 0; trace_azx_pcm_hw_params(chip, azx_dev); @@ -117,9 +119,15 @@ static int azx_pcm_hw_params(struct snd_pcm_substream *substream, goto unlock; } - azx_dev->core.bufsize = 0; - azx_dev->core.period_bytes = 0; - azx_dev->core.format_val = 0; + /* Set up BDLEs here, return -ENOMEM if too many BDLEs are required */ + hdas->bufsize = params_buffer_bytes(hw_params); + hdas->period_bytes = params_period_bytes(hw_params); + hdas->format_val = 0; + hdas->no_period_wakeup = + (hw_params->info & SNDRV_PCM_INFO_NO_PERIOD_WAKEUP) && + (hw_params->flags & SNDRV_PCM_HW_PARAMS_NO_PERIOD_WAKEUP); + if (snd_hdac_stream_setup_periods(hdas) < 0) + ret = -ENOMEM; unlock: dsp_unlock(azx_dev); @@ -267,8 +275,7 @@ static int azx_pcm_trigger(struct snd_pcm_substream *substream, int cmd) spin_lock(&bus->reg_lock); /* reset SYNC bits */ snd_hdac_stream_sync_trigger(hstr, false, sbits, sync_reg); - if (start) - snd_hdac_stream_timecounter_init(hstr, sbits); + snd_hdac_stream_timecounter_init(hstr, sbits, start); spin_unlock(&bus->reg_lock); return 0; } @@ -455,7 +462,8 @@ static int azx_get_sync_time(ktime_t *device, *device = ktime_add_ns(*device, (wallclk_cycles * NSEC_PER_SEC) / ((HDA_MAX_CYCLE_VALUE + 1) * runtime->rate)); - *system = convert_art_to_tsc(tsc_counter); + system->cycles = tsc_counter; + system->cs_id = CSID_X86_ART; return 0; } @@ -906,7 +914,7 @@ static int azx_send_cmd(struct hdac_bus *bus, unsigned int val) if (chip->disabled) return 0; - if (chip->single_cmd) + if (chip->single_cmd || bus->use_pio_for_commands) return azx_single_send_cmd(bus, val); else return snd_hdac_bus_send_cmd(bus, val); @@ -920,7 +928,7 @@ static int azx_get_response(struct hdac_bus *bus, unsigned int addr, if (chip->disabled) return 0; - if (chip->single_cmd) + if (chip->single_cmd || bus->use_pio_for_commands) return azx_single_get_response(bus, addr, res); else return azx_rirb_get_response(bus, addr, res); @@ -1067,11 +1075,9 @@ irqreturn_t azx_interrupt(int irq, void *dev_id) bool active, handled = false; int repeat = 0; /* count for avoiding endless loop */ -#ifdef CONFIG_PM if (azx_has_pm_runtime(chip)) if (!pm_runtime_active(chip->card->dev)) return IRQ_NONE; -#endif spin_lock(&bus->reg_lock); @@ -1180,6 +1186,9 @@ int azx_bus_init(struct azx *chip, const char *model) if (chip->driver_caps & AZX_DCAPS_4K_BDLE_BOUNDARY) bus->core.align_bdle_4k = true; + if (chip->driver_caps & AZX_DCAPS_PIO_COMMANDS) + bus->core.use_pio_for_commands = true; + /* enable sync_write flag for stable communication as default */ bus->core.sync_write = 1; diff --git a/sound/pci/hda/hda_controller.h b/sound/pci/hda/hda_controller.h index 8556031bcd68..c2d0109866e6 100644 --- a/sound/pci/hda/hda_controller.h +++ b/sound/pci/hda/hda_controller.h @@ -45,6 +45,7 @@ #define AZX_DCAPS_CORBRP_SELF_CLEAR (1 << 28) /* CORBRP clears itself after reset */ #define AZX_DCAPS_NO_MSI64 (1 << 29) /* Stick to 32-bit MSIs */ #define AZX_DCAPS_SEPARATE_STREAM_TAG (1 << 30) /* capture and playback use separate stream tag */ +#define AZX_DCAPS_PIO_COMMANDS (1 << 31) /* Use PIO instead of CORB for commands */ enum { AZX_SNOOP_TYPE_NONE, diff --git a/sound/pci/hda/hda_cs_dsp_ctl.c b/sound/pci/hda/hda_cs_dsp_ctl.c index 463ca06036bf..18fa6e7edb49 100644 --- a/sound/pci/hda/hda_cs_dsp_ctl.c +++ b/sound/pci/hda/hda_cs_dsp_ctl.c @@ -8,6 +8,7 @@ #include <linux/module.h> #include <sound/soc.h> +#include <linux/cleanup.h> #include <linux/firmware/cirrus/cs_dsp.h> #include <linux/firmware/cirrus/wmfw.h> #include "hda_cs_dsp_ctl.h" @@ -33,7 +34,7 @@ const char * const hda_cs_dsp_fw_ids[HDA_CS_DSP_NUM_FW] = { [HDA_CS_DSP_FW_SPK_DIAG] = "spk-diag", [HDA_CS_DSP_FW_MISC] = "misc", }; -EXPORT_SYMBOL_NS_GPL(hda_cs_dsp_fw_ids, SND_HDA_CS_DSP_CONTROLS); +EXPORT_SYMBOL_NS_GPL(hda_cs_dsp_fw_ids, "SND_HDA_CS_DSP_CONTROLS"); static int hda_cs_dsp_coeff_info(struct snd_kcontrol *kctl, struct snd_ctl_elem_info *uinfo) { @@ -51,13 +52,8 @@ static int hda_cs_dsp_coeff_put(struct snd_kcontrol *kctl, struct snd_ctl_elem_v struct hda_cs_dsp_coeff_ctl *ctl = (struct hda_cs_dsp_coeff_ctl *)snd_kcontrol_chip(kctl); struct cs_dsp_coeff_ctl *cs_ctl = ctl->cs_ctl; char *p = ucontrol->value.bytes.data; - int ret = 0; - - mutex_lock(&cs_ctl->dsp->pwr_lock); - ret = cs_dsp_coeff_write_ctrl(cs_ctl, 0, p, cs_ctl->len); - mutex_unlock(&cs_ctl->dsp->pwr_lock); - return ret; + return cs_dsp_coeff_lock_and_write_ctrl(cs_ctl, 0, p, cs_ctl->len); } static int hda_cs_dsp_coeff_get(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol) @@ -65,13 +61,8 @@ static int hda_cs_dsp_coeff_get(struct snd_kcontrol *kctl, struct snd_ctl_elem_v struct hda_cs_dsp_coeff_ctl *ctl = (struct hda_cs_dsp_coeff_ctl *)snd_kcontrol_chip(kctl); struct cs_dsp_coeff_ctl *cs_ctl = ctl->cs_ctl; char *p = ucontrol->value.bytes.data; - int ret; - - mutex_lock(&cs_ctl->dsp->pwr_lock); - ret = cs_dsp_coeff_read_ctrl(cs_ctl, 0, p, cs_ctl->len); - mutex_unlock(&cs_ctl->dsp->pwr_lock); - return ret; + return cs_dsp_coeff_lock_and_read_ctrl(cs_ctl, 0, p, cs_ctl->len); } static unsigned int wmfw_convert_flags(unsigned int in) @@ -97,11 +88,23 @@ static unsigned int wmfw_convert_flags(unsigned int in) return out; } -static void hda_cs_dsp_add_kcontrol(struct hda_cs_dsp_coeff_ctl *ctl, const char *name) +static void hda_cs_dsp_free_kcontrol(struct snd_kcontrol *kctl) { + struct hda_cs_dsp_coeff_ctl *ctl = (struct hda_cs_dsp_coeff_ctl *)snd_kcontrol_chip(kctl); struct cs_dsp_coeff_ctl *cs_ctl = ctl->cs_ctl; + + /* NULL priv to prevent a double-free in hda_cs_dsp_control_remove() */ + cs_ctl->priv = NULL; + kfree(ctl); +} + +static void hda_cs_dsp_add_kcontrol(struct cs_dsp_coeff_ctl *cs_ctl, + const struct hda_cs_dsp_ctl_info *info, + const char *name) +{ struct snd_kcontrol_new kcontrol = {0}; struct snd_kcontrol *kctl; + struct hda_cs_dsp_coeff_ctl *ctl __free(kfree) = NULL; int ret = 0; if (cs_ctl->len > ADSP_MAX_STD_CTRL_SIZE) { @@ -110,6 +113,13 @@ static void hda_cs_dsp_add_kcontrol(struct hda_cs_dsp_coeff_ctl *ctl, const char return; } + ctl = kzalloc(sizeof(*ctl), GFP_KERNEL); + if (!ctl) + return; + + ctl->cs_ctl = cs_ctl; + ctl->card = info->card; + kcontrol.name = name; kcontrol.info = hda_cs_dsp_coeff_info; kcontrol.iface = SNDRV_CTL_ELEM_IFACE_MIXER; @@ -117,20 +127,22 @@ static void hda_cs_dsp_add_kcontrol(struct hda_cs_dsp_coeff_ctl *ctl, const char kcontrol.get = hda_cs_dsp_coeff_get; kcontrol.put = hda_cs_dsp_coeff_put; - /* Save ctl inside private_data, ctl is owned by cs_dsp, - * and will be freed when cs_dsp removes the control */ kctl = snd_ctl_new1(&kcontrol, (void *)ctl); if (!kctl) return; - ret = snd_ctl_add(ctl->card, kctl); + kctl->private_free = hda_cs_dsp_free_kcontrol; + ctl->kctl = kctl; + + /* snd_ctl_add() calls our private_free on error, which will kfree(ctl) */ + cs_ctl->priv = no_free_ptr(ctl); + ret = snd_ctl_add(info->card, kctl); if (ret) { dev_err(cs_ctl->dsp->dev, "Failed to add KControl %s = %d\n", kcontrol.name, ret); return; } dev_dbg(cs_ctl->dsp->dev, "Added KControl: %s\n", kcontrol.name); - ctl->kctl = kctl; } static void hda_cs_dsp_control_add(struct cs_dsp_coeff_ctl *cs_ctl, @@ -138,7 +150,6 @@ static void hda_cs_dsp_control_add(struct cs_dsp_coeff_ctl *cs_ctl, { struct cs_dsp *cs_dsp = cs_ctl->dsp; char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; - struct hda_cs_dsp_coeff_ctl *ctl; const char *region_name; int ret; @@ -163,15 +174,7 @@ static void hda_cs_dsp_control_add(struct cs_dsp_coeff_ctl *cs_ctl, " %.*s", cs_ctl->subname_len - skip, cs_ctl->subname + skip); } - ctl = kzalloc(sizeof(*ctl), GFP_KERNEL); - if (!ctl) - return; - - ctl->cs_ctl = cs_ctl; - ctl->card = info->card; - cs_ctl->priv = ctl; - - hda_cs_dsp_add_kcontrol(ctl, name); + hda_cs_dsp_add_kcontrol(cs_ctl, info, name); } void hda_cs_dsp_add_controls(struct cs_dsp *dsp, const struct hda_cs_dsp_ctl_info *info) @@ -197,21 +200,22 @@ void hda_cs_dsp_add_controls(struct cs_dsp *dsp, const struct hda_cs_dsp_ctl_inf hda_cs_dsp_control_add(cs_ctl, info); } } -EXPORT_SYMBOL_NS_GPL(hda_cs_dsp_add_controls, SND_HDA_CS_DSP_CONTROLS); +EXPORT_SYMBOL_NS_GPL(hda_cs_dsp_add_controls, "SND_HDA_CS_DSP_CONTROLS"); void hda_cs_dsp_control_remove(struct cs_dsp_coeff_ctl *cs_ctl) { struct hda_cs_dsp_coeff_ctl *ctl = cs_ctl->priv; - kfree(ctl); + /* ctl and kctl may already have been removed by ALSA private_free */ + if (ctl) + snd_ctl_remove(ctl->card, ctl->kctl); } -EXPORT_SYMBOL_NS_GPL(hda_cs_dsp_control_remove, SND_HDA_CS_DSP_CONTROLS); +EXPORT_SYMBOL_NS_GPL(hda_cs_dsp_control_remove, "SND_HDA_CS_DSP_CONTROLS"); int hda_cs_dsp_write_ctl(struct cs_dsp *dsp, const char *name, int type, unsigned int alg, const void *buf, size_t len) { struct cs_dsp_coeff_ctl *cs_ctl; - struct hda_cs_dsp_coeff_ctl *ctl; int ret; mutex_lock(&dsp->pwr_lock); @@ -221,16 +225,9 @@ int hda_cs_dsp_write_ctl(struct cs_dsp *dsp, const char *name, int type, if (ret < 0) return ret; - if (ret == 0 || (cs_ctl->flags & WMFW_CTL_FLAG_SYS)) - return 0; - - ctl = cs_ctl->priv; - - snd_ctl_notify(ctl->card, SNDRV_CTL_EVENT_MASK_VALUE, &ctl->kctl->id); - return 0; } -EXPORT_SYMBOL_NS_GPL(hda_cs_dsp_write_ctl, SND_HDA_CS_DSP_CONTROLS); +EXPORT_SYMBOL_NS_GPL(hda_cs_dsp_write_ctl, "SND_HDA_CS_DSP_CONTROLS"); int hda_cs_dsp_read_ctl(struct cs_dsp *dsp, const char *name, int type, unsigned int alg, void *buf, size_t len) @@ -244,9 +241,9 @@ int hda_cs_dsp_read_ctl(struct cs_dsp *dsp, const char *name, int type, return ret; } -EXPORT_SYMBOL_NS_GPL(hda_cs_dsp_read_ctl, SND_HDA_CS_DSP_CONTROLS); +EXPORT_SYMBOL_NS_GPL(hda_cs_dsp_read_ctl, "SND_HDA_CS_DSP_CONTROLS"); MODULE_DESCRIPTION("CS_DSP ALSA Control HDA Library"); MODULE_AUTHOR("Stefan Binding, <sbinding@opensource.cirrus.com>"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(FW_CS_DSP); +MODULE_IMPORT_NS("FW_CS_DSP"); diff --git a/sound/pci/hda/hda_eld.c b/sound/pci/hda/hda_eld.c index 1d108ed5c6f2..301730432375 100644 --- a/sound/pci/hda/hda_eld.c +++ b/sound/pci/hda/hda_eld.c @@ -12,7 +12,7 @@ #include <linux/init.h> #include <linux/slab.h> #include <sound/core.h> -#include <asm/unaligned.h> +#include <linux/unaligned.h> #include <sound/hda_chmap.h> #include <sound/hda_codec.h> #include "hda_local.h" diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index de2a3d08c73c..b34d84fedcc8 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -1383,7 +1383,7 @@ static int try_assign_dacs(struct hda_codec *codec, int num_outs, struct nid_path *path; hda_nid_t pin = pins[i]; - if (!spec->obey_preferred_dacs) { + if (!spec->preferred_dacs) { path = snd_hda_get_path_from_idx(codec, path_idx[i]); if (path) { badness += assign_out_path_ctls(codec, path); @@ -1395,7 +1395,7 @@ static int try_assign_dacs(struct hda_codec *codec, int num_outs, if (dacs[i]) { if (is_dac_already_used(codec, dacs[i])) badness += bad->shared_primary; - } else if (spec->obey_preferred_dacs) { + } else if (spec->preferred_dacs) { badness += BAD_NO_PRIMARY_DAC; } @@ -4955,6 +4955,69 @@ void snd_hda_gen_stream_pm(struct hda_codec *codec, hda_nid_t nid, bool on) } EXPORT_SYMBOL_GPL(snd_hda_gen_stream_pm); +/* forcibly mute the speaker output without caching; return true if updated */ +static bool force_mute_output_path(struct hda_codec *codec, hda_nid_t nid) +{ + if (!nid) + return false; + if (!nid_has_mute(codec, nid, HDA_OUTPUT)) + return false; /* no mute, skip */ + if (snd_hda_codec_amp_read(codec, nid, 0, HDA_OUTPUT, 0) & + snd_hda_codec_amp_read(codec, nid, 1, HDA_OUTPUT, 0) & + HDA_AMP_MUTE) + return false; /* both channels already muted, skip */ + + /* direct amp update without caching */ + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, + AC_AMP_SET_OUTPUT | AC_AMP_SET_LEFT | + AC_AMP_SET_RIGHT | HDA_AMP_MUTE); + return true; +} + +/** + * snd_hda_gen_shutup_speakers - Forcibly mute the speaker outputs + * @codec: the HDA codec + * + * Forcibly mute the speaker outputs, to be called at suspend or shutdown. + * + * The mute state done by this function isn't cached, hence the original state + * will be restored at resume. + * + * Return true if the mute state has been changed. + */ +bool snd_hda_gen_shutup_speakers(struct hda_codec *codec) +{ + struct hda_gen_spec *spec = codec->spec; + const int *paths; + const struct nid_path *path; + int i, p, num_paths; + bool updated = false; + + /* if already powered off, do nothing */ + if (!snd_hdac_is_power_on(&codec->core)) + return false; + + if (spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT) { + paths = spec->out_paths; + num_paths = spec->autocfg.line_outs; + } else { + paths = spec->speaker_paths; + num_paths = spec->autocfg.speaker_outs; + } + + for (i = 0; i < num_paths; i++) { + path = snd_hda_get_path_from_idx(codec, paths[i]); + if (!path) + continue; + for (p = 0; p < path->depth; p++) + if (force_mute_output_path(codec, path->path[p])) + updated = true; + } + + return updated; +} +EXPORT_SYMBOL_GPL(snd_hda_gen_shutup_speakers); + /** * snd_hda_gen_parse_auto_config - Parse the given BIOS configuration and * set up the hda_gen_spec @@ -6021,7 +6084,6 @@ void snd_hda_gen_free(struct hda_codec *codec) } EXPORT_SYMBOL_GPL(snd_hda_gen_free); -#ifdef CONFIG_PM /** * snd_hda_gen_check_power_status - check the loopback power save state * @codec: the HDA codec @@ -6035,7 +6097,6 @@ int snd_hda_gen_check_power_status(struct hda_codec *codec, hda_nid_t nid) return snd_hda_check_amp_list_power(codec, &spec->loopback, nid); } EXPORT_SYMBOL_GPL(snd_hda_gen_check_power_status); -#endif /* @@ -6048,9 +6109,7 @@ static const struct hda_codec_ops generic_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 }; /* diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h index a8eea8367629..9612afaa61c2 100644 --- a/sound/pci/hda/hda_generic.h +++ b/sound/pci/hda/hda_generic.h @@ -232,7 +232,6 @@ struct hda_gen_spec { unsigned int power_down_unused:1; /* power down unused widgets */ unsigned int dac_min_mute:1; /* minimal = mute for DACs */ unsigned int suppress_vmaster:1; /* don't create vmaster kctls */ - unsigned int obey_preferred_dacs:1; /* obey preferred_dacs assignment */ /* other internal flags */ unsigned int no_analog:1; /* digital I/O only */ @@ -340,9 +339,7 @@ void snd_hda_gen_mic_autoswitch(struct hda_codec *codec, struct hda_jack_callback *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 unsigned int snd_hda_gen_path_power_filter(struct hda_codec *codec, hda_nid_t nid, unsigned int power_state); @@ -355,5 +352,6 @@ int snd_hda_gen_add_mute_led_cdev(struct hda_codec *codec, int snd_hda_gen_add_micmute_led_cdev(struct hda_codec *codec, int (*callback)(struct led_classdev *, enum led_brightness)); +bool snd_hda_gen_shutup_speakers(struct hda_codec *codec); #endif /* __SOUND_HDA_GENERIC_H */ diff --git a/sound/pci/hda/hda_hwdep.c b/sound/pci/hda/hda_hwdep.c index 727f39acedfc..9325e5c3cbe6 100644 --- a/sound/pci/hda/hda_hwdep.c +++ b/sound/pci/hda/hda_hwdep.c @@ -84,10 +84,8 @@ static int hda_hwdep_ioctl_compat(struct snd_hwdep *hw, struct file *file, static int hda_hwdep_open(struct snd_hwdep *hw, struct file *file) { -#ifndef CONFIG_SND_DEBUG_VERBOSE if (!capable(CAP_SYS_RAWIO)) return -EACCES; -#endif return 0; } diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 1b550c42db09..e67c22c59f02 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -4,7 +4,7 @@ * hda_intel.c - Implementation of primary alsa driver code base * for Intel HD Audio. * - * Copyright(c) 2004 Intel Corporation. All rights reserved. + * Copyright(c) 2004 Intel Corporation * * Copyright (c) 2004 Takashi Iwai <tiwai@suse.de> * PeiSen Hou <pshou@realtek.com.tw> @@ -175,8 +175,8 @@ module_param(power_save, xint, 0644); MODULE_PARM_DESC(power_save, "Automatic power-saving timeout " "(in second, 0 = disable)."); -static bool pm_blacklist = true; -module_param(pm_blacklist, bool, 0644); +static int pm_blacklist = -1; +module_param(pm_blacklist, bint, 0644); MODULE_PARM_DESC(pm_blacklist, "Enable power-management denylist"); /* reset the HD-audio controller in power save mode. @@ -186,8 +186,10 @@ MODULE_PARM_DESC(pm_blacklist, "Enable power-management denylist"); static bool power_save_controller = 1; module_param(power_save_controller, bool, 0644); MODULE_PARM_DESC(power_save_controller, "Reset controller in power save mode."); -#else +#else /* CONFIG_PM */ #define power_save 0 +#define pm_blacklist 0 +#define power_save_controller false #endif /* CONFIG_PM */ static int align_buffer_size = -1; @@ -289,6 +291,9 @@ enum { #define AZX_DCAPS_INTEL_BROXTON AZX_DCAPS_INTEL_SKYLAKE +#define AZX_DCAPS_INTEL_LNL \ + (AZX_DCAPS_INTEL_SKYLAKE | AZX_DCAPS_PIO_COMMANDS) + /* quirks for ATI SB / AMD Hudson */ #define AZX_DCAPS_PRESET_ATI_SB \ (AZX_DCAPS_NO_TCSEL | AZX_DCAPS_POSFIX_LPIB |\ @@ -768,6 +773,14 @@ static void azx_clear_irq_pending(struct azx *chip) static int azx_acquire_irq(struct azx *chip, int do_disconnect) { struct hdac_bus *bus = azx_bus(chip); + int ret; + + if (!chip->msi || pci_alloc_irq_vectors(chip->pci, 1, 1, PCI_IRQ_MSI) < 0) { + ret = pci_alloc_irq_vectors(chip->pci, 1, 1, PCI_IRQ_INTX); + if (ret < 0) + return ret; + chip->msi = 0; + } if (request_irq(chip->pci->irq, azx_interrupt, chip->msi ? 0 : IRQF_SHARED, @@ -781,7 +794,6 @@ static int azx_acquire_irq(struct azx *chip, int do_disconnect) } bus->irq = chip->pci->irq; chip->card->sync_irq = bus->irq; - pci_intx(chip->pci, !chip->msi); return 0; } @@ -890,7 +902,6 @@ static void __azx_shutdown_chip(struct azx *chip, bool skip_link_reset) display_power(chip, false); } -#ifdef CONFIG_PM static DEFINE_MUTEX(card_list_lock); static LIST_HEAD(card_list); @@ -916,7 +927,7 @@ static void azx_del_card_list(struct azx *chip) } /* trigger power-save check at writing parameter */ -static int param_set_xint(const char *val, const struct kernel_param *kp) +static int __maybe_unused param_set_xint(const char *val, const struct kernel_param *kp) { struct hda_intel *hda; struct azx *chip; @@ -926,10 +937,14 @@ static int param_set_xint(const char *val, const struct kernel_param *kp) if (ret || prev == power_save) return ret; + if (pm_blacklist > 0) + return 0; + mutex_lock(&card_list_lock); list_for_each_entry(hda, &card_list, list) { chip = &hda->chip; - if (!hda->probe_continued || chip->disabled) + if (!hda->probe_continued || chip->disabled || + hda->runtime_pm_disabled) continue; snd_hda_set_power_save(&chip->bus, power_save * 1000); } @@ -987,7 +1002,6 @@ static void __azx_runtime_resume(struct azx *chip) display_power(chip, false); } -#ifdef CONFIG_PM_SLEEP static int azx_prepare(struct device *dev) { struct snd_card *card = dev_get_drvdata(dev); @@ -1025,28 +1039,18 @@ static int azx_suspend(struct device *dev) { struct snd_card *card = dev_get_drvdata(dev); struct azx *chip; - struct hdac_bus *bus; if (!azx_is_pm_ready(card)) return 0; chip = card->private_data; - bus = azx_bus(chip); azx_shutdown_chip(chip); - if (bus->irq >= 0) { - free_irq(bus->irq, chip); - bus->irq = -1; - chip->card->sync_irq = -1; - } - - if (chip->msi) - pci_disable_msi(chip->pci); trace_azx_suspend(chip); return 0; } -static int azx_resume(struct device *dev) +static int __maybe_unused azx_resume(struct device *dev) { struct snd_card *card = dev_get_drvdata(dev); struct azx *chip; @@ -1055,11 +1059,6 @@ static int azx_resume(struct device *dev) return 0; chip = card->private_data; - if (chip->msi) - if (pci_enable_msi(chip->pci) < 0) - chip->msi = 0; - if (azx_acquire_irq(chip, 1) < 0) - return -EIO; __azx_runtime_resume(chip); @@ -1097,9 +1096,8 @@ static int azx_thaw_noirq(struct device *dev) return 0; } -#endif /* CONFIG_PM_SLEEP */ -static int azx_runtime_suspend(struct device *dev) +static int __maybe_unused azx_runtime_suspend(struct device *dev) { struct snd_card *card = dev_get_drvdata(dev); struct azx *chip; @@ -1116,7 +1114,7 @@ static int azx_runtime_suspend(struct device *dev) return 0; } -static int azx_runtime_resume(struct device *dev) +static int __maybe_unused azx_runtime_resume(struct device *dev) { struct snd_card *card = dev_get_drvdata(dev); struct azx *chip; @@ -1133,7 +1131,7 @@ static int azx_runtime_resume(struct device *dev) return 0; } -static int azx_runtime_idle(struct device *dev) +static int __maybe_unused azx_runtime_idle(struct device *dev) { struct snd_card *card = dev_get_drvdata(dev); struct azx *chip; @@ -1159,23 +1157,14 @@ static int azx_runtime_idle(struct device *dev) } static const struct dev_pm_ops azx_pm = { - SET_SYSTEM_SLEEP_PM_OPS(azx_suspend, azx_resume) -#ifdef CONFIG_PM_SLEEP - .prepare = azx_prepare, - .complete = azx_complete, - .freeze_noirq = azx_freeze_noirq, - .thaw_noirq = azx_thaw_noirq, -#endif + SYSTEM_SLEEP_PM_OPS(azx_suspend, azx_resume) + .prepare = pm_sleep_ptr(azx_prepare), + .complete = pm_sleep_ptr(azx_complete), + .freeze_noirq = pm_sleep_ptr(azx_freeze_noirq), + .thaw_noirq = pm_sleep_ptr(azx_thaw_noirq), SET_RUNTIME_PM_OPS(azx_runtime_suspend, azx_runtime_resume, azx_runtime_idle) }; -#define AZX_PM_OPS &azx_pm -#else -#define azx_add_card_list(chip) /* NOP */ -#define azx_del_card_list(chip) /* NOP */ -#define AZX_PM_OPS NULL -#endif /* CONFIG_PM */ - static int azx_probe_continue(struct azx *chip); @@ -1816,7 +1805,7 @@ static int azx_create(struct snd_card *card, struct pci_dev *pci, /* use the non-cached pages in non-snoop mode */ if (!azx_snoop(chip)) - azx_bus(chip)->dma_type = SNDRV_DMA_TYPE_DEV_WC_SG; + azx_bus(chip)->dma_type = SNDRV_DMA_TYPE_DEV_WC; if (chip->driver_type == AZX_DRIVER_NVIDIA) { dev_dbg(chip->card->dev, "Enable delay in RIRB handling\n"); @@ -1870,6 +1859,8 @@ static int azx_first_init(struct azx *chip) bus->polling_mode = 1; bus->not_use_interrupts = 1; bus->access_sdnctl_in_dword = 1; + if (!chip->jackpoll_interval) + chip->jackpoll_interval = msecs_to_jiffies(1500); } err = pcim_iomap_regions(pci, 1 << 0, "ICH HD audio"); @@ -1895,13 +1886,9 @@ static int azx_first_init(struct azx *chip) chip->gts_present = true; #endif - if (chip->msi) { - if (chip->driver_caps & AZX_DCAPS_NO_MSI64) { - dev_dbg(card->dev, "Disabling 64bit MSI\n"); - pci->no_64bit_msi = true; - } - if (pci_enable_msi(pci) < 0) - chip->msi = 0; + if (chip->msi && chip->driver_caps & AZX_DCAPS_NO_MSI64) { + dev_dbg(card->dev, "Disabling 64bit MSI\n"); + pci->no_64bit_msi = true; } pci_set_master(pci); @@ -2053,7 +2040,7 @@ static int disable_msi_reset_irq(struct azx *chip) free_irq(bus->irq, chip); bus->irq = -1; chip->card->sync_irq = -1; - pci_disable_msi(chip->pci); + pci_free_irq_vectors(chip->pci); chip->msi = 0; err = azx_acquire_irq(chip, 1); if (err < 0) @@ -2206,7 +2193,6 @@ out_free: return err; } -#ifdef CONFIG_PM /* On some boards setting power_save to a non 0 value leads to clicking / * popping sounds when ever we enter/leave powersaving mode. Ideally we would * figure out how to avoid these sounds, but that is not always feasible. @@ -2246,16 +2232,17 @@ static const struct snd_pci_quirk power_save_denylist[] = { SND_PCI_QUIRK(0x1631, 0xe017, "Packard Bell NEC IMEDIA 5204", 0), /* KONTRON SinglePC may cause a stall at runtime resume */ SND_PCI_QUIRK(0x1734, 0x1232, "KONTRON SinglePC", 0), + /* Dell ALC3271 */ + SND_PCI_QUIRK(0x1028, 0x0962, "Dell ALC3271", 0), {} }; -#endif /* CONFIG_PM */ static void set_default_power_save(struct azx *chip) { + struct hda_intel *hda = container_of(chip, struct hda_intel, chip); int val = power_save; -#ifdef CONFIG_PM - if (pm_blacklist) { + if (pm_blacklist < 0) { const struct snd_pci_quirk *q; q = snd_pci_quirk_lookup(chip->pci, power_save_denylist); @@ -2263,9 +2250,12 @@ static void set_default_power_save(struct azx *chip) dev_info(chip->card->dev, "device %04x:%04x is on the power_save denylist, forcing power_save to 0\n", q->subvendor, q->subdevice); val = 0; + hda->runtime_pm_disabled = 1; } + } else if (pm_blacklist > 0) { + dev_info(chip->card->dev, "Forcing power_save to 0 via option\n"); + val = 0; } -#endif /* CONFIG_PM */ snd_hda_set_power_save(&chip->bus, val * 1000); } @@ -2321,10 +2311,6 @@ static int azx_probe_continue(struct azx *chip) chip->fw->data); if (err < 0) goto out_free; -#ifndef CONFIG_PM - release_firmware(chip->fw); /* no longer needed */ - chip->fw = NULL; -#endif } #endif @@ -2502,12 +2488,18 @@ static const struct pci_device_id azx_ids[] = { { PCI_DEVICE_DATA(INTEL, HDA_RPL_M, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) }, { PCI_DEVICE_DATA(INTEL, HDA_RPL_PX, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) }, { PCI_DEVICE_DATA(INTEL, HDA_MTL, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) }, + /* Battlemage */ + { PCI_DEVICE_DATA(INTEL, HDA_BMG, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) }, /* Lunarlake-P */ - { PCI_DEVICE_DATA(INTEL, HDA_LNL_P, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) }, + { PCI_DEVICE_DATA(INTEL, HDA_LNL_P, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_LNL) }, /* Arrow Lake-S */ { PCI_DEVICE_DATA(INTEL, HDA_ARL_S, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) }, /* Arrow Lake */ { PCI_DEVICE_DATA(INTEL, HDA_ARL, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) }, + /* Panther Lake */ + { PCI_DEVICE_DATA(INTEL, HDA_PTL, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_LNL) }, + /* Panther Lake-H */ + { PCI_DEVICE_DATA(INTEL, HDA_PTL_H, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_LNL) }, /* Apollolake (Broxton-P) */ { PCI_DEVICE_DATA(INTEL, HDA_APL, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_BROXTON) }, /* Gemini-Lake */ @@ -2682,7 +2674,7 @@ static const struct pci_device_id azx_ids[] = { .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS | AZX_DCAPS_PM_RUNTIME }, /* GLENFLY */ - { PCI_DEVICE(0x6766, PCI_ANY_ID), + { PCI_DEVICE(PCI_VENDOR_ID_GLENFLY, PCI_ANY_ID), .class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8, .class_mask = 0xffffff, .driver_data = AZX_DRIVER_GFHDMI | AZX_DCAPS_POSFIX_LPIB | @@ -2750,9 +2742,9 @@ static const struct pci_device_id azx_ids[] = { { PCI_VDEVICE(ZHAOXIN, 0x3288), .driver_data = AZX_DRIVER_ZHAOXIN }, /* Loongson HDAudio*/ { PCI_VDEVICE(LOONGSON, PCI_DEVICE_ID_LOONGSON_HDA), - .driver_data = AZX_DRIVER_LOONGSON }, + .driver_data = AZX_DRIVER_LOONGSON | AZX_DCAPS_NO_TCSEL }, { PCI_VDEVICE(LOONGSON, PCI_DEVICE_ID_LOONGSON_HDMI), - .driver_data = AZX_DRIVER_LOONGSON }, + .driver_data = AZX_DRIVER_LOONGSON | AZX_DCAPS_NO_TCSEL }, { 0, } }; MODULE_DEVICE_TABLE(pci, azx_ids); @@ -2765,7 +2757,7 @@ static struct pci_driver azx_driver = { .remove = azx_remove, .shutdown = azx_shutdown, .driver = { - .pm = AZX_PM_OPS, + .pm = &azx_pm, }, }; diff --git a/sound/pci/hda/hda_intel.h b/sound/pci/hda/hda_intel.h index 0f39418f9328..2d1725f86ef1 100644 --- a/sound/pci/hda/hda_intel.h +++ b/sound/pci/hda/hda_intel.h @@ -22,6 +22,7 @@ struct hda_intel { /* extra flags */ unsigned int irq_pending_warned:1; unsigned int probe_continued:1; + unsigned int runtime_pm_disabled:1; /* vga_switcheroo setup */ unsigned int use_vga_switcheroo:1; diff --git a/sound/pci/hda/hda_intel_trace.h b/sound/pci/hda/hda_intel_trace.h index 73a7adfa192d..2775fa81a500 100644 --- a/sound/pci/hda/hda_intel_trace.h +++ b/sound/pci/hda/hda_intel_trace.h @@ -34,7 +34,6 @@ DEFINE_EVENT(hda_pm, azx_resume, TP_ARGS(chip) ); -#ifdef CONFIG_PM DEFINE_EVENT(hda_pm, azx_runtime_suspend, TP_PROTO(struct azx *chip), TP_ARGS(chip) @@ -44,7 +43,6 @@ DEFINE_EVENT(hda_pm, azx_runtime_resume, TP_PROTO(struct azx *chip), TP_ARGS(chip) ); -#endif #endif /* _TRACE_HDA_INTEL_H */ diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index 53a5a62b78fa..763f79f6f32e 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -292,6 +292,32 @@ struct hda_fixup { } v; }; +/* + * extended form of snd_pci_quirk: + * for PCI SSID matching, use SND_PCI_QUIRK() like before; + * for codec SSID matching, use the new HDA_CODEC_QUIRK() instead + */ +struct hda_quirk { + unsigned short subvendor; /* PCI subvendor ID */ + unsigned short subdevice; /* PCI subdevice ID */ + unsigned short subdevice_mask; /* bitmask to match */ + bool match_codec_ssid; /* match only with codec SSID */ + int value; /* value */ +#ifdef CONFIG_SND_DEBUG_VERBOSE + const char *name; /* name of the device (optional) */ +#endif +}; + +#ifdef CONFIG_SND_DEBUG_VERBOSE +#define HDA_CODEC_QUIRK(vend, dev, xname, val) \ + { _SND_PCI_QUIRK_ID(vend, dev), .value = (val), .name = (xname),\ + .match_codec_ssid = true } +#else +#define HDA_CODEC_QUIRK(vend, dev, xname, val) \ + { _SND_PCI_QUIRK_ID(vend, dev), .value = (val), \ + .match_codec_ssid = true } +#endif + struct snd_hda_pin_quirk { unsigned int codec; /* Codec vendor/device ID */ unsigned short subvendor; /* PCI subvendor ID */ @@ -351,7 +377,7 @@ void snd_hda_apply_fixup(struct hda_codec *codec, int action); void __snd_hda_apply_fixup(struct hda_codec *codec, int id, int action, int depth); void snd_hda_pick_fixup(struct hda_codec *codec, const struct hda_model_fixup *models, - const struct snd_pci_quirk *quirk, + const struct hda_quirk *quirk, const struct hda_fixup *fixlist); void snd_hda_pick_pin_fixup(struct hda_codec *codec, const struct snd_hda_pin_quirk *pin_quirk, diff --git a/sound/pci/hda/hda_sysfs.c b/sound/pci/hda/hda_sysfs.c index 69ebc37a4d6f..140e24bf4d7f 100644 --- a/sound/pci/hda/hda_sysfs.c +++ b/sound/pci/hda/hda_sysfs.c @@ -26,7 +26,6 @@ struct hda_hint { const char *val; /* contained in the same alloc as key */ }; -#ifdef CONFIG_PM static ssize_t power_on_acct_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -47,7 +46,6 @@ static ssize_t power_off_acct_show(struct device *dev, static DEVICE_ATTR_RO(power_on_acct); static DEVICE_ATTR_RO(power_off_acct); -#endif /* CONFIG_PM */ #define CODEC_INFO_SHOW(type, field) \ static ssize_t type##_show(struct device *dev, \ @@ -650,7 +648,7 @@ static const struct hda_patch_item patch_items[NUM_LINE_MODES] = { }, }; -/* check the line starting with '[' -- change the parser mode accodingly */ +/* check the line starting with '[' -- change the parser mode accordingly */ static int parse_line_mode(char *buf, struct hda_bus *bus) { int i; @@ -745,10 +743,8 @@ static struct attribute *hda_dev_attrs[] = { &dev_attr_modelname.attr, &dev_attr_init_pin_configs.attr, &dev_attr_driver_pin_configs.attr, -#ifdef CONFIG_PM &dev_attr_power_on_acct.attr, &dev_attr_power_off_acct.attr, -#endif #ifdef CONFIG_SND_HDA_RECONFIG &dev_attr_init_verbs.attr, &dev_attr_hints.attr, diff --git a/sound/pci/hda/hda_tegra.c b/sound/pci/hda/hda_tegra.c index d967e70a7058..b1e30a83dfb0 100644 --- a/sound/pci/hda/hda_tegra.c +++ b/sound/pci/hda/hda_tegra.c @@ -606,7 +606,7 @@ static struct platform_driver tegra_platform_hda = { .of_match_table = hda_tegra_match, }, .probe = hda_tegra_probe, - .remove_new = hda_tegra_remove, + .remove = hda_tegra_remove, .shutdown = hda_tegra_shutdown, }; module_platform_driver(tegra_platform_hda); diff --git a/sound/pci/hda/ideapad_hotkey_led_helper.c b/sound/pci/hda/ideapad_hotkey_led_helper.c new file mode 100644 index 000000000000..c10d97964d49 --- /dev/null +++ b/sound/pci/hda/ideapad_hotkey_led_helper.c @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Ideapad helper functions for Lenovo Ideapad LED control, + * It should be included from codec driver. + */ + +#if IS_ENABLED(CONFIG_IDEAPAD_LAPTOP) + +#include <linux/acpi.h> +#include <linux/leds.h> + +static bool is_ideapad(struct hda_codec *codec) +{ + return (codec->core.subsystem_id >> 16 == 0x17aa) && + (acpi_dev_found("LHK2019") || acpi_dev_found("VPC2004")); +} + +static void hda_fixup_ideapad_acpi(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + if (action == HDA_FIXUP_ACT_PRE_PROBE) { + if (!is_ideapad(codec)) + return; + snd_hda_gen_add_mute_led_cdev(codec, NULL); + snd_hda_gen_add_micmute_led_cdev(codec, NULL); + } +} + +#else /* CONFIG_IDEAPAD_LAPTOP */ + +static void hda_fixup_ideapad_acpi(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ +} + +#endif /* CONFIG_IDEAPAD_LAPTOP */ diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index 8afe6000f7da..56354fe060a1 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c @@ -72,7 +72,6 @@ static int create_beep_ctls(struct hda_codec *codec) #define create_beep_ctls(codec) 0 #endif -#ifdef CONFIG_PM static void ad198x_power_eapd_write(struct hda_codec *codec, hda_nid_t front, hda_nid_t hp) { @@ -118,7 +117,6 @@ static int ad198x_suspend(struct hda_codec *codec) ad198x_power_eapd(codec); return 0; } -#endif /* follow EAPD via vmaster hook */ static void ad_vmaster_eapd_hook(void *private_data, int enabled) @@ -158,10 +156,8 @@ static const struct hda_codec_ops ad198x_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, .suspend = ad198x_suspend, -#endif }; @@ -349,7 +345,7 @@ static const struct hda_fixup ad1986a_fixups[] = { }, }; -static const struct snd_pci_quirk ad1986a_fixup_tbl[] = { +static const struct hda_quirk ad1986a_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x30af, "HP B2800", AD1986A_FIXUP_LAPTOP_IMIC), SND_PCI_QUIRK(0x1043, 0x1153, "ASUS M9V", AD1986A_FIXUP_LAPTOP_IMIC), SND_PCI_QUIRK(0x1043, 0x1443, "ASUS Z99He", AD1986A_FIXUP_EAPD), @@ -592,7 +588,7 @@ static const struct hda_fixup ad1981_fixups[] = { }, }; -static const struct snd_pci_quirk ad1981_fixup_tbl[] = { +static const struct hda_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), @@ -1065,7 +1061,7 @@ static const struct hda_fixup ad1884_fixups[] = { }, }; -static const struct snd_pci_quirk ad1884_fixup_tbl[] = { +static const struct hda_quirk ad1884_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x2a82, "HP Touchsmart", AD1884_FIXUP_HP_TOUCHSMART), SND_PCI_QUIRK_VENDOR(0x103c, "HP", AD1884_FIXUP_HP_EAPD), SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo Thinkpad", AD1884_FIXUP_THINKPAD), diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index aa312441604f..d40197fb5fbd 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -1134,7 +1134,6 @@ struct ca0132_spec { struct hda_codec *codec; struct delayed_work unsol_hp_work; - int quirk; #ifdef ENABLE_TUNING_CONTROLS long cur_ctl_vals[TUNING_CTLS_COUNT]; @@ -1166,7 +1165,6 @@ struct ca0132_spec { * CA0132 quirks table */ enum { - QUIRK_NONE, QUIRK_ALIENWARE, QUIRK_ALIENWARE_M17XR4, QUIRK_SBZ, @@ -1176,10 +1174,11 @@ enum { QUIRK_R3D, QUIRK_AE5, QUIRK_AE7, + QUIRK_NONE = HDA_FIXUP_ID_NOT_SET, }; #ifdef CONFIG_PCI -#define ca0132_quirk(spec) ((spec)->quirk) +#define ca0132_quirk(spec) ((spec)->codec->fixup_id) #define ca0132_use_pci_mmio(spec) ((spec)->use_pci_mmio) #define ca0132_use_alt_functions(spec) ((spec)->use_alt_functions) #define ca0132_use_alt_controls(spec) ((spec)->use_alt_controls) @@ -1293,7 +1292,7 @@ static const struct hda_pintbl ae7_pincfgs[] = { {} }; -static const struct snd_pci_quirk ca0132_quirks[] = { +static const struct hda_quirk ca0132_quirks[] = { SND_PCI_QUIRK(0x1028, 0x057b, "Alienware M17x R4", QUIRK_ALIENWARE_M17XR4), SND_PCI_QUIRK(0x1028, 0x0685, "Alienware 15 2015", QUIRK_ALIENWARE), SND_PCI_QUIRK(0x1028, 0x0688, "Alienware 17 2015", QUIRK_ALIENWARE), @@ -1316,6 +1315,19 @@ static const struct snd_pci_quirk ca0132_quirks[] = { {} }; +static const struct hda_model_fixup ca0132_quirk_models[] = { + { .id = QUIRK_ALIENWARE, .name = "alienware" }, + { .id = QUIRK_ALIENWARE_M17XR4, .name = "alienware-m17xr4" }, + { .id = QUIRK_SBZ, .name = "sbz" }, + { .id = QUIRK_ZXR, .name = "zxr" }, + { .id = QUIRK_ZXR_DBPRO, .name = "zxr-dbpro" }, + { .id = QUIRK_R3DI, .name = "r3di" }, + { .id = QUIRK_R3D, .name = "r3d" }, + { .id = QUIRK_AE5, .name = "ae5" }, + { .id = QUIRK_AE7, .name = "ae7" }, + {} +}; + /* Output selection quirk info structures. */ #define MAX_QUIRK_MMIO_GPIO_SET_VALS 3 #define MAX_QUIRK_SCP_SET_VALS 2 @@ -9682,7 +9694,6 @@ static void dbpro_free(struct hda_codec *codec) kfree(codec->spec); } -#ifdef CONFIG_PM static int ca0132_suspend(struct hda_codec *codec) { struct ca0132_spec *spec = codec->spec; @@ -9690,7 +9701,6 @@ static int ca0132_suspend(struct hda_codec *codec) cancel_delayed_work_sync(&spec->unsol_hp_work); return 0; } -#endif static const struct hda_codec_ops ca0132_patch_ops = { .build_controls = ca0132_build_controls, @@ -9698,9 +9708,7 @@ static const struct hda_codec_ops ca0132_patch_ops = { .init = ca0132_init, .free = ca0132_free, .unsol_event = snd_hda_jack_unsol_event, -#ifdef CONFIG_PM .suspend = ca0132_suspend, -#endif }; static const struct hda_codec_ops dbpro_patch_ops = { @@ -9961,17 +9969,15 @@ static int ca0132_prepare_verbs(struct hda_codec *codec) */ static void sbz_detect_quirk(struct hda_codec *codec) { - struct ca0132_spec *spec = codec->spec; - switch (codec->core.subsystem_id) { case 0x11020033: - spec->quirk = QUIRK_ZXR; + codec->fixup_id = QUIRK_ZXR; break; case 0x1102003f: - spec->quirk = QUIRK_ZXR_DBPRO; + codec->fixup_id = QUIRK_ZXR_DBPRO; break; default: - spec->quirk = QUIRK_SBZ; + codec->fixup_id = QUIRK_SBZ; break; } } @@ -9980,7 +9986,6 @@ static int patch_ca0132(struct hda_codec *codec) { struct ca0132_spec *spec; int err; - const struct snd_pci_quirk *quirk; codec_dbg(codec, "patch_ca0132\n"); @@ -9991,11 +9996,7 @@ static int patch_ca0132(struct hda_codec *codec) spec->codec = codec; /* Detect codec quirk */ - quirk = snd_pci_quirk_lookup(codec->bus->pci, ca0132_quirks); - if (quirk) - spec->quirk = quirk->value; - else - spec->quirk = QUIRK_NONE; + snd_hda_pick_fixup(codec, ca0132_quirk_models, ca0132_quirks, NULL); if (ca0132_quirk(spec) == QUIRK_SBZ) sbz_detect_quirk(codec); @@ -10072,7 +10073,7 @@ static int patch_ca0132(struct hda_codec *codec) spec->mem_base = pci_iomap(codec->bus->pci, 2, 0xC20); if (spec->mem_base == NULL) { codec_warn(codec, "pci_iomap failed! Setting quirk to QUIRK_NONE."); - spec->quirk = QUIRK_NONE; + codec->fixup_id = QUIRK_NONE; } } #endif diff --git a/sound/pci/hda/patch_cirrus.c b/sound/pci/hda/patch_cirrus.c index 6807b4708a17..06e046214a41 100644 --- a/sound/pci/hda/patch_cirrus.c +++ b/sound/pci/hda/patch_cirrus.c @@ -385,7 +385,7 @@ static const struct hda_model_fixup cs420x_models[] = { {} }; -static const struct snd_pci_quirk cs420x_fixup_tbl[] = { +static const struct hda_quirk cs420x_fixup_tbl[] = { SND_PCI_QUIRK(0x10de, 0x0ac0, "MacBookPro 5,3", CS420X_MBP53), SND_PCI_QUIRK(0x10de, 0x0d94, "MacBookAir 3,1(2)", CS420X_MBP55), SND_PCI_QUIRK(0x10de, 0xcb79, "MacBookPro 5,5", CS420X_MBP55), @@ -634,13 +634,13 @@ static const struct hda_model_fixup cs4208_models[] = { {} }; -static const struct snd_pci_quirk cs4208_fixup_tbl[] = { +static const struct hda_quirk cs4208_fixup_tbl[] = { SND_PCI_QUIRK_VENDOR(0x106b, "Apple", CS4208_MAC_AUTO), {} /* terminator */ }; /* codec SSID matching */ -static const struct snd_pci_quirk cs4208_mac_fixup_tbl[] = { +static const struct hda_quirk cs4208_mac_fixup_tbl[] = { SND_PCI_QUIRK(0x106b, 0x5e00, "MacBookPro 11,2", CS4208_MBP11), SND_PCI_QUIRK(0x106b, 0x6c00, "MacMini 7,1", CS4208_MACMINI), SND_PCI_QUIRK(0x106b, 0x7100, "MacBookAir 6,1", CS4208_MBA6), @@ -818,7 +818,7 @@ static const struct hda_model_fixup cs421x_models[] = { {} }; -static const struct snd_pci_quirk cs421x_fixup_tbl[] = { +static const struct hda_quirk cs421x_fixup_tbl[] = { /* Test Intel board + CDB2410 */ SND_PCI_QUIRK(0x8086, 0x5001, "DP45SG/CDB4210", CS421X_CDB4210), {} /* terminator */ @@ -1128,7 +1128,6 @@ static int cs421x_parse_auto_config(struct hda_codec *codec) return 0; } -#ifdef CONFIG_PM /* * Manage PDREF, when transitioning to D3hot * (DAC,ADC) -> D3, PDREF=1, AFG->D3 @@ -1153,7 +1152,6 @@ static int cs421x_suspend(struct hda_codec *codec) return 0; } -#endif static const struct hda_codec_ops cs421x_patch_ops = { .build_controls = snd_hda_gen_build_controls, @@ -1161,9 +1159,7 @@ static const struct hda_codec_ops cs421x_patch_ops = { .init = cs421x_init, .free = cs_free, .unsol_event = snd_hda_jack_unsol_event, -#ifdef CONFIG_PM .suspend = cs421x_suspend, -#endif }; static int patch_cs4210(struct hda_codec *codec) diff --git a/sound/pci/hda/patch_cmedia.c b/sound/pci/hda/patch_cmedia.c index 2ddd33f8dd6c..fe946d407830 100644 --- a/sound/pci/hda/patch_cmedia.c +++ b/sound/pci/hda/patch_cmedia.c @@ -17,10 +17,231 @@ #include "hda_jack.h" #include "hda_generic.h" +/* CM9825 Offset Definitions */ + +#define CM9825_VERB_SET_HPF_1 0x781 +#define CM9825_VERB_SET_HPF_2 0x785 +#define CM9825_VERB_SET_PLL 0x7a0 +#define CM9825_VERB_SET_NEG 0x7a1 +#define CM9825_VERB_SET_ADCL 0x7a2 +#define CM9825_VERB_SET_DACL 0x7a3 +#define CM9825_VERB_SET_MBIAS 0x7a4 +#define CM9825_VERB_SET_VNEG 0x7a8 +#define CM9825_VERB_SET_D2S 0x7a9 +#define CM9825_VERB_SET_DACTRL 0x7aa +#define CM9825_VERB_SET_PDNEG 0x7ac +#define CM9825_VERB_SET_VDO 0x7ad +#define CM9825_VERB_SET_CDALR 0x7b0 +#define CM9825_VERB_SET_MTCBA 0x7b1 +#define CM9825_VERB_SET_OTP 0x7b2 +#define CM9825_VERB_SET_OCP 0x7b3 +#define CM9825_VERB_SET_GAD 0x7b4 +#define CM9825_VERB_SET_TMOD 0x7b5 +#define CM9825_VERB_SET_SNR 0x7b6 + struct cmi_spec { struct hda_gen_spec gen; + const struct hda_verb *chip_d0_verbs; + const struct hda_verb *chip_d3_verbs; + const struct hda_verb *chip_hp_present_verbs; + const struct hda_verb *chip_hp_remove_verbs; + struct hda_codec *codec; + struct delayed_work unsol_hp_work; + int quirk; +}; + +static const struct hda_verb cm9825_std_d3_verbs[] = { + /* chip sleep verbs */ + {0x43, CM9825_VERB_SET_D2S, 0x62}, /* depop */ + {0x43, CM9825_VERB_SET_PLL, 0x01}, /* PLL set */ + {0x43, CM9825_VERB_SET_NEG, 0xc2}, /* NEG set */ + {0x43, CM9825_VERB_SET_ADCL, 0x00}, /* ADC */ + {0x43, CM9825_VERB_SET_DACL, 0x02}, /* DACL */ + {0x43, CM9825_VERB_SET_VNEG, 0x50}, /* VOL NEG */ + {0x43, CM9825_VERB_SET_MBIAS, 0x00}, /* MBIAS */ + {0x43, CM9825_VERB_SET_PDNEG, 0x04}, /* SEL OSC */ + {0x43, CM9825_VERB_SET_CDALR, 0xf6}, /* Class D */ + {0x43, CM9825_VERB_SET_OTP, 0xcd}, /* OTP set */ + {} +}; + +static const struct hda_verb cm9825_std_d0_verbs[] = { + /* chip init verbs */ + {0x34, AC_VERB_SET_EAPD_BTLENABLE, 0x02}, /* EAPD set */ + {0x43, CM9825_VERB_SET_SNR, 0x30}, /* SNR set */ + {0x43, CM9825_VERB_SET_PLL, 0x00}, /* PLL set */ + {0x43, CM9825_VERB_SET_ADCL, 0x00}, /* ADC */ + {0x43, CM9825_VERB_SET_DACL, 0x02}, /* DACL */ + {0x43, CM9825_VERB_SET_MBIAS, 0x00}, /* MBIAS */ + {0x43, CM9825_VERB_SET_VNEG, 0x56}, /* VOL NEG */ + {0x43, CM9825_VERB_SET_D2S, 0x62}, /* depop */ + {0x43, CM9825_VERB_SET_DACTRL, 0x00}, /* DACTRL set */ + {0x43, CM9825_VERB_SET_PDNEG, 0x0c}, /* SEL OSC */ + {0x43, CM9825_VERB_SET_VDO, 0x80}, /* VDO set */ + {0x43, CM9825_VERB_SET_CDALR, 0xf4}, /* Class D */ + {0x43, CM9825_VERB_SET_OTP, 0xcd}, /* OTP set */ + {0x43, CM9825_VERB_SET_MTCBA, 0x61}, /* SR set */ + {0x43, CM9825_VERB_SET_OCP, 0x33}, /* OTP set */ + {0x43, CM9825_VERB_SET_GAD, 0x07}, /* ADC -3db */ + {0x43, CM9825_VERB_SET_TMOD, 0x26}, /* Class D clk */ + {0x3C, AC_VERB_SET_AMP_GAIN_MUTE | + AC_AMP_SET_OUTPUT | AC_AMP_SET_RIGHT, 0x2d}, /* Gain set */ + {0x3C, AC_VERB_SET_AMP_GAIN_MUTE | + AC_AMP_SET_OUTPUT | AC_AMP_SET_LEFT, 0x2d}, /* Gain set */ + {0x43, CM9825_VERB_SET_HPF_1, 0x40}, /* HPF set */ + {0x43, CM9825_VERB_SET_HPF_2, 0x40}, /* HPF set */ + {} }; +static const struct hda_verb cm9825_hp_present_verbs[] = { + {0x42, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00}, /* PIN off */ + {0x43, CM9825_VERB_SET_ADCL, 0x88}, /* ADC */ + {0x43, CM9825_VERB_SET_DACL, 0xaa}, /* DACL */ + {0x43, CM9825_VERB_SET_MBIAS, 0x10}, /* MBIAS */ + {0x43, CM9825_VERB_SET_D2S, 0xf2}, /* depop */ + {0x43, CM9825_VERB_SET_DACTRL, 0x00}, /* DACTRL set */ + {0x43, CM9825_VERB_SET_VDO, 0xc4}, /* VDO set */ + {} +}; + +static const struct hda_verb cm9825_hp_remove_verbs[] = { + {0x43, CM9825_VERB_SET_ADCL, 0x00}, /* ADC */ + {0x43, CM9825_VERB_SET_DACL, 0x56}, /* DACL */ + {0x43, CM9825_VERB_SET_MBIAS, 0x00}, /* MBIAS */ + {0x43, CM9825_VERB_SET_D2S, 0x62}, /* depop */ + {0x43, CM9825_VERB_SET_DACTRL, 0xe0}, /* DACTRL set */ + {0x43, CM9825_VERB_SET_VDO, 0x80}, /* VDO set */ + {0x42, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, /* PIN on */ + {} +}; + +static void cm9825_unsol_hp_delayed(struct work_struct *work) +{ + struct cmi_spec *spec = + container_of(to_delayed_work(work), struct cmi_spec, unsol_hp_work); + struct hda_jack_tbl *jack; + hda_nid_t hp_pin = spec->gen.autocfg.hp_pins[0]; + bool hp_jack_plugin = false; + int err = 0; + + hp_jack_plugin = snd_hda_jack_detect(spec->codec, hp_pin); + + codec_dbg(spec->codec, "hp_jack_plugin %d, hp_pin 0x%X\n", + (int)hp_jack_plugin, hp_pin); + + if (!hp_jack_plugin) { + err = + snd_hda_codec_write(spec->codec, 0x42, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40); + if (err) + codec_dbg(spec->codec, "codec_write err %d\n", err); + + snd_hda_sequence_write(spec->codec, spec->chip_hp_remove_verbs); + } else { + snd_hda_sequence_write(spec->codec, + spec->chip_hp_present_verbs); + } + + jack = snd_hda_jack_tbl_get(spec->codec, hp_pin); + if (jack) { + jack->block_report = 0; + snd_hda_jack_report_sync(spec->codec); + } +} + +static void hp_callback(struct hda_codec *codec, struct hda_jack_callback *cb) +{ + struct cmi_spec *spec = codec->spec; + struct hda_jack_tbl *tbl; + + /* Delay enabling the HP amp, to let the mic-detection + * state machine run. + */ + + codec_dbg(spec->codec, "cb->nid 0x%X\n", cb->nid); + + tbl = snd_hda_jack_tbl_get(codec, cb->nid); + if (tbl) + tbl->block_report = 1; + schedule_delayed_work(&spec->unsol_hp_work, msecs_to_jiffies(200)); +} + +static void cm9825_setup_unsol(struct hda_codec *codec) +{ + struct cmi_spec *spec = codec->spec; + + hda_nid_t hp_pin = spec->gen.autocfg.hp_pins[0]; + + snd_hda_jack_detect_enable_callback(codec, hp_pin, hp_callback); +} + +static int cm9825_init(struct hda_codec *codec) +{ + snd_hda_gen_init(codec); + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_INIT); + + return 0; +} + +static void cm9825_free(struct hda_codec *codec) +{ + struct cmi_spec *spec = codec->spec; + + cancel_delayed_work_sync(&spec->unsol_hp_work); + snd_hda_gen_free(codec); +} + +static int cm9825_suspend(struct hda_codec *codec) +{ + struct cmi_spec *spec = codec->spec; + + cancel_delayed_work_sync(&spec->unsol_hp_work); + + snd_hda_sequence_write(codec, spec->chip_d3_verbs); + + return 0; +} + +static int cm9825_resume(struct hda_codec *codec) +{ + struct cmi_spec *spec = codec->spec; + hda_nid_t hp_pin = 0; + bool hp_jack_plugin = false; + int err; + + err = + snd_hda_codec_write(spec->codec, 0x42, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00); + if (err) + codec_dbg(codec, "codec_write err %d\n", err); + + msleep(150); /* for depop noise */ + + codec->patch_ops.init(codec); + + hp_pin = spec->gen.autocfg.hp_pins[0]; + hp_jack_plugin = snd_hda_jack_detect(spec->codec, hp_pin); + + codec_dbg(spec->codec, "hp_jack_plugin %d, hp_pin 0x%X\n", + (int)hp_jack_plugin, hp_pin); + + if (!hp_jack_plugin) { + err = + snd_hda_codec_write(spec->codec, 0x42, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40); + + if (err) + codec_dbg(codec, "codec_write err %d\n", err); + + snd_hda_sequence_write(codec, cm9825_hp_remove_verbs); + } + + snd_hda_regmap_sync(codec); + hda_call_check_power_status(codec, 0x01); + + return 0; +} + /* * stuff for auto-parser */ @@ -32,6 +253,53 @@ static const struct hda_codec_ops cmi_auto_patch_ops = { .unsol_event = snd_hda_jack_unsol_event, }; +static int patch_cm9825(struct hda_codec *codec) +{ + struct cmi_spec *spec; + struct auto_pin_cfg *cfg; + int err; + + spec = kzalloc(sizeof(*spec), GFP_KERNEL); + if (spec == NULL) + return -ENOMEM; + + INIT_DELAYED_WORK(&spec->unsol_hp_work, cm9825_unsol_hp_delayed); + codec->spec = spec; + spec->codec = codec; + codec->patch_ops = cmi_auto_patch_ops; + codec->patch_ops.init = cm9825_init; + codec->patch_ops.suspend = cm9825_suspend; + codec->patch_ops.resume = cm9825_resume; + codec->patch_ops.free = cm9825_free; + codec->patch_ops.check_power_status = snd_hda_gen_check_power_status; + cfg = &spec->gen.autocfg; + snd_hda_gen_spec_init(&spec->gen); + spec->chip_d0_verbs = cm9825_std_d0_verbs; + spec->chip_d3_verbs = cm9825_std_d3_verbs; + spec->chip_hp_present_verbs = cm9825_hp_present_verbs; + spec->chip_hp_remove_verbs = cm9825_hp_remove_verbs; + + snd_hda_sequence_write(codec, spec->chip_d0_verbs); + + err = snd_hda_parse_pin_defcfg(codec, cfg, NULL, 0); + if (err < 0) + goto error; + err = snd_hda_gen_parse_auto_config(codec, cfg); + if (err < 0) + goto error; + + cm9825_setup_unsol(codec); + + return 0; + + error: + cm9825_free(codec); + + codec_info(codec, "Enter err %d\n", err); + + return err; +} + static int patch_cmi9880(struct hda_codec *codec) { struct cmi_spec *spec; @@ -113,6 +381,7 @@ static const struct hda_device_id snd_hda_id_cmedia[] = { HDA_CODEC_ENTRY(0x13f68888, "CMI8888", patch_cmi8888), HDA_CODEC_ENTRY(0x13f69880, "CMI9880", patch_cmi9880), HDA_CODEC_ENTRY(0x434d4980, "CMI9880", patch_cmi9880), + HDA_CODEC_ENTRY(0x13f69825, "CM9825", patch_cm9825), {} /* terminator */ }; MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_cmedia); diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index e8209178d87b..34874039ad45 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -21,12 +21,6 @@ #include "hda_jack.h" #include "hda_generic.h" -enum { - CX_HEADSET_NOPRESENT = 0, - CX_HEADSET_PARTPRESENT, - CX_HEADSET_ALLPRESENT, -}; - struct conexant_spec { struct hda_gen_spec gen; @@ -48,7 +42,6 @@ struct conexant_spec { unsigned int gpio_led; unsigned int gpio_mute_led_mask; unsigned int gpio_mic_led_mask; - unsigned int headset_present_flag; bool is_cx8070_sn6140; }; @@ -173,18 +166,18 @@ static void cxt_init_gpio_led(struct hda_codec *codec) static void cx_fixup_headset_recog(struct hda_codec *codec) { - unsigned int mic_persent; + unsigned int mic_present; /* fix some headset type recognize fail issue, such as EDIFIER headset */ - /* set micbiasd output current comparator threshold from 66% to 55%. */ + /* set micbias output current comparator threshold from 66% to 55%. */ snd_hda_codec_write(codec, 0x1c, 0, 0x320, 0x010); - /* set OFF voltage for DFET from -1.2V to -0.8V, set headset micbias registor + /* set OFF voltage for DFET from -1.2V to -0.8V, set headset micbias register * value adjustment trim from 2.2K ohms to 2.0K ohms. */ snd_hda_codec_write(codec, 0x1c, 0, 0x3b0, 0xe10); /* fix reboot headset type recognize fail issue */ - mic_persent = snd_hda_codec_read(codec, 0x19, 0, AC_VERB_GET_PIN_SENSE, 0x0); - if (mic_persent & AC_PINSENSE_PRESENCE) + mic_present = snd_hda_codec_read(codec, 0x19, 0, AC_VERB_GET_PIN_SENSE, 0x0); + if (mic_present & AC_PINSENSE_PRESENCE) /* enable headset mic VREF */ snd_hda_codec_write(codec, 0x19, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24); else @@ -250,68 +243,35 @@ static void cx_process_headset_plugin(struct hda_codec *codec) } } -static void cx_update_headset_mic_vref(struct hda_codec *codec, unsigned int res) +static void cx_update_headset_mic_vref(struct hda_codec *codec, struct hda_jack_callback *event) { - unsigned int phone_present, mic_persent, phone_tag, mic_tag; - struct conexant_spec *spec = codec->spec; + unsigned int mic_present; - /* In cx8070 and sn6140, the node 16 can only be config to headphone or disabled, - * the node 19 can only be config to microphone or disabled. - * Check hp&mic tag to process headset pulgin&plugout. + /* In cx8070 and sn6140, the node 16 can only be configured to headphone or disabled, + * the node 19 can only be configured to microphone or disabled. + * Check hp&mic tag to process headset plugin & plugout. */ - phone_tag = snd_hda_codec_read(codec, 0x16, 0, AC_VERB_GET_UNSOLICITED_RESPONSE, 0x0); - mic_tag = snd_hda_codec_read(codec, 0x19, 0, AC_VERB_GET_UNSOLICITED_RESPONSE, 0x0); - if ((phone_tag & (res >> AC_UNSOL_RES_TAG_SHIFT)) || - (mic_tag & (res >> AC_UNSOL_RES_TAG_SHIFT))) { - phone_present = snd_hda_codec_read(codec, 0x16, 0, AC_VERB_GET_PIN_SENSE, 0x0); - if (!(phone_present & AC_PINSENSE_PRESENCE)) {/* headphone plugout */ - spec->headset_present_flag = CX_HEADSET_NOPRESENT; - snd_hda_codec_write(codec, 0x19, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20); - return; - } - if (spec->headset_present_flag == CX_HEADSET_NOPRESENT) { - spec->headset_present_flag = CX_HEADSET_PARTPRESENT; - } else if (spec->headset_present_flag == CX_HEADSET_PARTPRESENT) { - mic_persent = snd_hda_codec_read(codec, 0x19, 0, - AC_VERB_GET_PIN_SENSE, 0x0); - /* headset is present */ - if ((phone_present & AC_PINSENSE_PRESENCE) && - (mic_persent & AC_PINSENSE_PRESENCE)) { - cx_process_headset_plugin(codec); - spec->headset_present_flag = CX_HEADSET_ALLPRESENT; - } - } - } -} - -static void cx_jack_unsol_event(struct hda_codec *codec, unsigned int res) -{ - struct conexant_spec *spec = codec->spec; - - if (spec->is_cx8070_sn6140) - cx_update_headset_mic_vref(codec, res); - - snd_hda_jack_unsol_event(codec, res); + mic_present = snd_hda_codec_read(codec, 0x19, 0, AC_VERB_GET_PIN_SENSE, 0x0); + if (!(mic_present & AC_PINSENSE_PRESENCE)) /* mic plugout */ + snd_hda_codec_write(codec, 0x19, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20); + else + cx_process_headset_plugin(codec); } -#ifdef CONFIG_PM static int cx_auto_suspend(struct hda_codec *codec) { cx_auto_shutdown(codec); return 0; } -#endif static const struct hda_codec_ops cx_auto_patch_ops = { .build_controls = snd_hda_gen_build_controls, .build_pcms = snd_hda_gen_build_pcms, .init = cx_auto_init, .free = cx_auto_free, - .unsol_event = cx_jack_unsol_event, -#ifdef CONFIG_PM + .unsol_event = snd_hda_jack_unsol_event, .suspend = cx_auto_suspend, .check_power_status = snd_hda_gen_check_power_status, -#endif }; /* @@ -331,6 +291,7 @@ enum { CXT_FIXUP_GPIO1, CXT_FIXUP_ASPIRE_DMIC, CXT_FIXUP_THINKPAD_ACPI, + CXT_FIXUP_LENOVO_XPAD_ACPI, CXT_FIXUP_OLPC_XO, CXT_FIXUP_CAP_MIX_AMP, CXT_FIXUP_TOSHIBA_P105, @@ -341,15 +302,21 @@ enum { CXT_FIXUP_HP_SPECTRE, CXT_FIXUP_HP_GATE_MIC, CXT_FIXUP_MUTE_LED_GPIO, + CXT_FIXUP_HP_ELITEONE_OUT_DIS, CXT_FIXUP_HP_ZBOOK_MUTE_LED, CXT_FIXUP_HEADSET_MIC, CXT_FIXUP_HP_MIC_NO_PRESENCE, CXT_PINCFG_SWS_JS201D, + CXT_PINCFG_TOP_SPEAKER, + CXT_FIXUP_HP_A_U, }; /* for hda_fixup_thinkpad_acpi() */ #include "thinkpad_helper.c" +/* for hda_fixup_ideapad_acpi() */ +#include "ideapad_hotkey_led_helper.c" + static void cxt_fixup_stereo_dmic(struct hda_codec *codec, const struct hda_fixup *fix, int action) { @@ -357,6 +324,19 @@ static void cxt_fixup_stereo_dmic(struct hda_codec *codec, spec->gen.inv_dmic_split = 1; } +/* fix widget control pin settings */ +static void cxt_fixup_update_pinctl(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + if (action == HDA_FIXUP_ACT_PROBE) { + /* Unset OUT_EN for this Node pin, leaving only HP_EN. + * This is the value stored in the codec register after + * the correct initialization of the previous windows boot. + */ + snd_hda_set_pin_ctl_cache(codec, 0x1d, AC_PINCTL_HP_EN); + } +} + static void cxt5066_increase_mic_boost(struct hda_codec *codec, const struct hda_fixup *fix, int action) { @@ -799,6 +779,18 @@ static void cxt_setup_mute_led(struct hda_codec *codec, } } +static void cxt_setup_gpio_unmute(struct hda_codec *codec, + unsigned int gpio_mute_mask) +{ + if (gpio_mute_mask) { + // set gpio data to 0. + snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, 0); + snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_MASK, gpio_mute_mask); + snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DIRECTION, gpio_mute_mask); + snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_STICKY_MASK, 0); + } +} + static void cxt_fixup_mute_led_gpio(struct hda_codec *codec, const struct hda_fixup *fix, int action) { @@ -813,6 +805,15 @@ static void cxt_fixup_hp_zbook_mute_led(struct hda_codec *codec, cxt_setup_mute_led(codec, 0x10, 0x20); } +static void cxt_fixup_hp_a_u(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + // Init vers in BIOS mute the spk/hp by set gpio high to avoid pop noise, + // so need to unmute once by clearing the gpio data when runs into the system. + if (action == HDA_FIXUP_ACT_INIT) + cxt_setup_gpio_unmute(codec, 0x2); +} + /* ThinkPad X200 & co with cxt5051 */ static const struct hda_pintbl cxt_pincfg_lenovo_x200[] = { { 0x16, 0x042140ff }, /* HP (seq# overridden) */ @@ -931,6 +932,12 @@ static const struct hda_fixup cxt_fixups[] = { .type = HDA_FIXUP_FUNC, .v.func = hda_fixup_thinkpad_acpi, }, + [CXT_FIXUP_LENOVO_XPAD_ACPI] = { + .type = HDA_FIXUP_FUNC, + .v.func = hda_fixup_ideapad_acpi, + .chained = true, + .chain_id = CXT_FIXUP_THINKPAD_ACPI, + }, [CXT_FIXUP_OLPC_XO] = { .type = HDA_FIXUP_FUNC, .v.func = cxt_fixup_olpc_xo, @@ -991,6 +998,10 @@ static const struct hda_fixup cxt_fixups[] = { .type = HDA_FIXUP_FUNC, .v.func = cxt_fixup_mute_led_gpio, }, + [CXT_FIXUP_HP_ELITEONE_OUT_DIS] = { + .type = HDA_FIXUP_FUNC, + .v.func = cxt_fixup_update_pinctl, + }, [CXT_FIXUP_HP_ZBOOK_MUTE_LED] = { .type = HDA_FIXUP_FUNC, .v.func = cxt_fixup_hp_zbook_mute_led, @@ -1012,9 +1023,20 @@ static const struct hda_fixup cxt_fixups[] = { .type = HDA_FIXUP_PINS, .v.pins = cxt_pincfg_sws_js201d, }, + [CXT_PINCFG_TOP_SPEAKER] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x1d, 0x82170111 }, + { } + }, + }, + [CXT_FIXUP_HP_A_U] = { + .type = HDA_FIXUP_FUNC, + .v.func = cxt_fixup_hp_a_u, + }, }; -static const struct snd_pci_quirk cxt5045_fixups[] = { +static const struct hda_quirk cxt5045_fixups[] = { SND_PCI_QUIRK(0x103c, 0x30d5, "HP 530", CXT_FIXUP_HP_530), SND_PCI_QUIRK(0x1179, 0xff31, "Toshiba P105", CXT_FIXUP_TOSHIBA_P105), /* HP, Packard Bell, Fujitsu-Siemens & Lenovo laptops have @@ -1034,7 +1056,7 @@ static const struct hda_model_fixup cxt5045_fixup_models[] = { {} }; -static const struct snd_pci_quirk cxt5047_fixups[] = { +static const struct hda_quirk cxt5047_fixups[] = { /* HP laptops have really bad sound over 0 dB on NID 0x10. */ SND_PCI_QUIRK_VENDOR(0x103c, "HP", CXT_FIXUP_CAP_MIX_AMP_5047), @@ -1046,7 +1068,7 @@ static const struct hda_model_fixup cxt5047_fixup_models[] = { {} }; -static const struct snd_pci_quirk cxt5051_fixups[] = { +static const struct hda_quirk cxt5051_fixups[] = { SND_PCI_QUIRK(0x103c, 0x360b, "Compaq CQ60", CXT_PINCFG_COMPAQ_CQ60), SND_PCI_QUIRK(0x17aa, 0x20f2, "Lenovo X200", CXT_PINCFG_LENOVO_X200), {} @@ -1057,7 +1079,7 @@ static const struct hda_model_fixup cxt5051_fixup_models[] = { {} }; -static const struct snd_pci_quirk cxt5066_fixups[] = { +static const struct hda_quirk cxt5066_fixups[] = { SND_PCI_QUIRK(0x1025, 0x0543, "Acer Aspire One 522", CXT_FIXUP_STEREO_DMIC), SND_PCI_QUIRK(0x1025, 0x054c, "Acer Aspire 3830TG", CXT_FIXUP_ASPIRE_DMIC), SND_PCI_QUIRK(0x1025, 0x054f, "Acer Aspire 4830T", CXT_FIXUP_ASPIRE_DMIC), @@ -1068,6 +1090,7 @@ static const struct snd_pci_quirk cxt5066_fixups[] = { SND_PCI_QUIRK(0x103c, 0x814f, "HP ZBook 15u G3", CXT_FIXUP_MUTE_LED_GPIO), SND_PCI_QUIRK(0x103c, 0x8174, "HP Spectre x360", CXT_FIXUP_HP_SPECTRE), SND_PCI_QUIRK(0x103c, 0x822e, "HP ProBook 440 G4", CXT_FIXUP_MUTE_LED_GPIO), + SND_PCI_QUIRK(0x103c, 0x8231, "HP ProBook 450 G4", CXT_FIXUP_MUTE_LED_GPIO), SND_PCI_QUIRK(0x103c, 0x828c, "HP EliteBook 840 G4", CXT_FIXUP_HP_DOCK), SND_PCI_QUIRK(0x103c, 0x8299, "HP 800 G3 SFF", CXT_FIXUP_HP_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x103c, 0x829a, "HP 800 G3 DM", CXT_FIXUP_HP_MIC_NO_PRESENCE), @@ -1077,6 +1100,7 @@ static const struct snd_pci_quirk cxt5066_fixups[] = { SND_PCI_QUIRK(0x103c, 0x83b2, "HP EliteBook 840 G5", CXT_FIXUP_HP_DOCK), SND_PCI_QUIRK(0x103c, 0x83b3, "HP EliteBook 830 G5", CXT_FIXUP_HP_DOCK), SND_PCI_QUIRK(0x103c, 0x83d3, "HP ProBook 640 G4", CXT_FIXUP_HP_DOCK), + SND_PCI_QUIRK(0x103c, 0x83e5, "HP EliteOne 1000 G2", CXT_FIXUP_HP_ELITEONE_OUT_DIS), SND_PCI_QUIRK(0x103c, 0x8402, "HP ProBook 645 G4", CXT_FIXUP_MUTE_LED_GPIO), SND_PCI_QUIRK(0x103c, 0x8427, "HP ZBook Studio G5", CXT_FIXUP_HP_ZBOOK_MUTE_LED), SND_PCI_QUIRK(0x103c, 0x844f, "HP ZBook Studio G5", CXT_FIXUP_HP_ZBOOK_MUTE_LED), @@ -1085,6 +1109,7 @@ static const struct snd_pci_quirk cxt5066_fixups[] = { SND_PCI_QUIRK(0x103c, 0x8457, "HP Z2 G4 mini", CXT_FIXUP_HP_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x103c, 0x8458, "HP Z2 G4 mini premium", CXT_FIXUP_HP_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1043, 0x138d, "Asus", CXT_FIXUP_HEADPHONE_MIC_PIN), + SND_PCI_QUIRK(0x14f1, 0x0252, "MBX-Z60MR100", CXT_FIXUP_HP_A_U), SND_PCI_QUIRK(0x14f1, 0x0265, "SWS JS201D", CXT_PINCFG_SWS_JS201D), SND_PCI_QUIRK(0x152d, 0x0833, "OLPC XO-1.5", CXT_FIXUP_OLPC_XO), SND_PCI_QUIRK(0x17aa, 0x20f2, "Lenovo T400", CXT_PINCFG_LENOVO_TP410), @@ -1105,9 +1130,11 @@ static const struct snd_pci_quirk cxt5066_fixups[] = { SND_PCI_QUIRK(0x17aa, 0x3977, "Lenovo IdeaPad U310", CXT_FIXUP_STEREO_DMIC), SND_PCI_QUIRK(0x17aa, 0x3978, "Lenovo G50-70", CXT_FIXUP_STEREO_DMIC), SND_PCI_QUIRK(0x17aa, 0x397b, "Lenovo S205", CXT_FIXUP_STEREO_DMIC), - SND_PCI_QUIRK_VENDOR(0x17aa, "Thinkpad", CXT_FIXUP_THINKPAD_ACPI), + SND_PCI_QUIRK_VENDOR(0x17aa, "Thinkpad/Ideapad", CXT_FIXUP_LENOVO_XPAD_ACPI), SND_PCI_QUIRK(0x1c06, 0x2011, "Lemote A1004", CXT_PINCFG_LEMOTE_A1004), SND_PCI_QUIRK(0x1c06, 0x2012, "Lemote A1205", CXT_PINCFG_LEMOTE_A1205), + HDA_CODEC_QUIRK(0x2782, 0x12c3, "Sirius Gen1", CXT_PINCFG_TOP_SPEAKER), + HDA_CODEC_QUIRK(0x2782, 0x12c5, "Sirius Gen2", CXT_PINCFG_TOP_SPEAKER), {} }; @@ -1117,6 +1144,7 @@ static const struct hda_model_fixup cxt5066_fixup_models[] = { { .id = CXT_FIXUP_HEADPHONE_MIC_PIN, .name = "headphone-mic-pin" }, { .id = CXT_PINCFG_LENOVO_TP410, .name = "tp410" }, { .id = CXT_FIXUP_THINKPAD_ACPI, .name = "thinkpad" }, + { .id = CXT_FIXUP_LENOVO_XPAD_ACPI, .name = "thinkpad-ideapad" }, { .id = CXT_PINCFG_LEMOTE_A1004, .name = "lemote-a1004" }, { .id = CXT_PINCFG_LEMOTE_A1205, .name = "lemote-a1205" }, { .id = CXT_FIXUP_OLPC_XO, .name = "olpc-xo" }, @@ -1127,6 +1155,8 @@ static const struct hda_model_fixup cxt5066_fixup_models[] = { { .id = CXT_FIXUP_HP_MIC_NO_PRESENCE, .name = "hp-mic-fix" }, { .id = CXT_PINCFG_LENOVO_NOTEBOOK, .name = "lenovo-20149" }, { .id = CXT_PINCFG_SWS_JS201D, .name = "sws-js201d" }, + { .id = CXT_PINCFG_TOP_SPEAKER, .name = "sirius-top-speaker" }, + { .id = CXT_FIXUP_HP_A_U, .name = "HP-U-support" }, {} }; @@ -1167,7 +1197,7 @@ static int patch_conexant_auto(struct hda_codec *codec) case 0x14f11f86: case 0x14f11f87: spec->is_cx8070_sn6140 = true; - spec->headset_present_flag = CX_HEADSET_NOPRESENT; + snd_hda_jack_detect_enable_callback(codec, 0x19, cx_update_headset_mic_vref); break; } diff --git a/sound/pci/hda/patch_cs8409-tables.c b/sound/pci/hda/patch_cs8409-tables.c index 36b411d1a960..09240138e087 100644 --- a/sound/pci/hda/patch_cs8409-tables.c +++ b/sound/pci/hda/patch_cs8409-tables.c @@ -121,7 +121,7 @@ static const struct cs8409_i2c_param cs42l42_init_reg_seq[] = { { CS42L42_MIXER_CHA_VOL, 0x3F }, { CS42L42_MIXER_CHB_VOL, 0x3F }, { CS42L42_MIXER_ADC_VOL, 0x3f }, - { CS42L42_HP_CTL, 0x03 }, + { CS42L42_HP_CTL, 0x0D }, { CS42L42_MIC_DET_CTL1, 0xB6 }, { CS42L42_TIPSENSE_CTL, 0xC2 }, { CS42L42_HS_CLAMP_DISABLE, 0x01 }, @@ -131,7 +131,7 @@ static const struct cs8409_i2c_param cs42l42_init_reg_seq[] = { { CS42L42_RSENSE_CTL3, 0x00 }, { CS42L42_TSENSE_CTL, 0x80 }, { CS42L42_HS_BIAS_CTL, 0xC0 }, - { CS42L42_PWR_CTL1, 0x02 }, + { CS42L42_PWR_CTL1, 0x02, 10000 }, { CS42L42_ADC_OVFL_INT_MASK, 0xff }, { CS42L42_MIXER_INT_MASK, 0xff }, { CS42L42_SRC_INT_MASK, 0xff }, @@ -315,7 +315,7 @@ static const struct cs8409_i2c_param dolphin_c0_init_reg_seq[] = { { CS42L42_ASP_TX_SZ_EN, 0x01 }, { CS42L42_PWR_CTL1, 0x0A }, { CS42L42_PWR_CTL2, 0x84 }, - { CS42L42_HP_CTL, 0x03 }, + { CS42L42_HP_CTL, 0x0D }, { CS42L42_MIXER_CHA_VOL, 0x3F }, { CS42L42_MIXER_CHB_VOL, 0x3F }, { CS42L42_MIXER_ADC_VOL, 0x3f }, @@ -328,7 +328,7 @@ static const struct cs8409_i2c_param dolphin_c0_init_reg_seq[] = { { CS42L42_RSENSE_CTL3, 0x00 }, { CS42L42_TSENSE_CTL, 0x80 }, { CS42L42_HS_BIAS_CTL, 0xC0 }, - { CS42L42_PWR_CTL1, 0x02 }, + { CS42L42_PWR_CTL1, 0x02, 10000 }, { CS42L42_ADC_OVFL_INT_MASK, 0xff }, { CS42L42_MIXER_INT_MASK, 0xff }, { CS42L42_SRC_INT_MASK, 0xff }, @@ -371,7 +371,7 @@ static const struct cs8409_i2c_param dolphin_c1_init_reg_seq[] = { { CS42L42_ASP_TX_SZ_EN, 0x00 }, { CS42L42_PWR_CTL1, 0x0E }, { CS42L42_PWR_CTL2, 0x84 }, - { CS42L42_HP_CTL, 0x01 }, + { CS42L42_HP_CTL, 0x0D }, { CS42L42_MIXER_CHA_VOL, 0x3F }, { CS42L42_MIXER_CHB_VOL, 0x3F }, { CS42L42_MIXER_ADC_VOL, 0x3f }, @@ -384,7 +384,7 @@ static const struct cs8409_i2c_param dolphin_c1_init_reg_seq[] = { { CS42L42_RSENSE_CTL3, 0x00 }, { CS42L42_TSENSE_CTL, 0x80 }, { CS42L42_HS_BIAS_CTL, 0xC0 }, - { CS42L42_PWR_CTL1, 0x06 }, + { CS42L42_PWR_CTL1, 0x06, 10000 }, { CS42L42_ADC_OVFL_INT_MASK, 0xff }, { CS42L42_MIXER_INT_MASK, 0xff }, { CS42L42_SRC_INT_MASK, 0xff }, @@ -473,7 +473,7 @@ struct sub_codec dolphin_cs42l42_1 = { * Arrays Used for all projects using CS8409 ******************************************************************************/ -const struct snd_pci_quirk cs8409_fixup_tbl[] = { +const struct hda_quirk cs8409_fixup_tbl[] = { SND_PCI_QUIRK(0x1028, 0x0A11, "Bullseye", CS8409_BULLSEYE), SND_PCI_QUIRK(0x1028, 0x0A12, "Bullseye", CS8409_BULLSEYE), SND_PCI_QUIRK(0x1028, 0x0A23, "Bullseye", CS8409_BULLSEYE), diff --git a/sound/pci/hda/patch_cs8409.c b/sound/pci/hda/patch_cs8409.c index e41316e2e983..e50006757a2c 100644 --- a/sound/pci/hda/patch_cs8409.c +++ b/sound/pci/hda/patch_cs8409.c @@ -346,6 +346,11 @@ static int cs8409_i2c_bulk_write(struct sub_codec *scodec, const struct cs8409_i if (cs8409_i2c_wait_complete(codec) < 0) goto error; + /* Certain use cases may require a delay + * after a write operation before proceeding. + */ + if (seq[i].delay) + fsleep(seq[i].delay); } mutex_unlock(&spec->i2c_mux); @@ -876,7 +881,7 @@ static void cs42l42_resume(struct sub_codec *cs42l42) { CS42L42_DET_INT_STATUS2, 0x00 }, { CS42L42_TSRS_PLUG_STATUS, 0x00 }, }; - int fsv_old, fsv_new; + unsigned int fsv; /* Bring CS42L42 out of Reset */ spec->gpio_data = snd_hda_codec_read(codec, CS8409_PIN_AFG, 0, AC_VERB_GET_GPIO_DATA, 0); @@ -888,18 +893,19 @@ static void cs42l42_resume(struct sub_codec *cs42l42) /* Initialize CS42L42 companion codec */ cs8409_i2c_bulk_write(cs42l42, cs42l42->init_seq, cs42l42->init_seq_num); - msleep(CS42L42_INIT_TIMEOUT_MS); /* Clear interrupts, by reading interrupt status registers */ cs8409_i2c_bulk_read(cs42l42, irq_regs, ARRAY_SIZE(irq_regs)); - fsv_old = cs8409_i2c_read(cs42l42, CS42L42_HP_CTL); - if (cs42l42->full_scale_vol == CS42L42_FULL_SCALE_VOL_0DB) - fsv_new = fsv_old & ~CS42L42_FULL_SCALE_VOL_MASK; - else - fsv_new = fsv_old & CS42L42_FULL_SCALE_VOL_MASK; - if (fsv_new != fsv_old) - cs8409_i2c_write(cs42l42, CS42L42_HP_CTL, fsv_new); + fsv = cs8409_i2c_read(cs42l42, CS42L42_HP_CTL); + if (cs42l42->full_scale_vol) { + // Set the full scale volume bit + fsv |= CS42L42_FULL_SCALE_VOL_MASK; + cs8409_i2c_write(cs42l42, CS42L42_HP_CTL, fsv); + } + // Unmute analog channels A and B + fsv = (fsv & ~CS42L42_ANA_MUTE_AB); + cs8409_i2c_write(cs42l42, CS42L42_HP_CTL, fsv); /* we have to explicitly allow unsol event handling even during the * resume phase so that the jack event is processed properly @@ -909,7 +915,6 @@ static void cs42l42_resume(struct sub_codec *cs42l42) cs42l42_enable_jack_detect(cs42l42); } -#ifdef CONFIG_PM static void cs42l42_suspend(struct sub_codec *cs42l42) { struct hda_codec *codec = cs42l42->codec; @@ -921,7 +926,7 @@ static void cs42l42_suspend(struct sub_codec *cs42l42) { CS42L42_MIXER_CHA_VOL, 0x3F }, { CS42L42_MIXER_ADC_VOL, 0x3F }, { CS42L42_MIXER_CHB_VOL, 0x3F }, - { CS42L42_HP_CTL, 0x0F }, + { CS42L42_HP_CTL, 0x0D }, { CS42L42_ASP_RX_DAI0_EN, 0x00 }, { CS42L42_ASP_CLK_CFG, 0x00 }, { CS42L42_PWR_CTL1, 0xFE }, @@ -948,7 +953,6 @@ static void cs42l42_suspend(struct sub_codec *cs42l42) spec->gpio_data &= ~cs42l42->reset_gpio; snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_DATA, spec->gpio_data); } -#endif static void cs8409_free(struct hda_codec *codec) { @@ -1003,7 +1007,6 @@ static void cs8409_cs42l42_jack_unsol_event(struct hda_codec *codec, unsigned in } } -#ifdef CONFIG_PM /* Manage PDREF, when transition to D3hot */ static int cs8409_cs42l42_suspend(struct hda_codec *codec) { @@ -1025,7 +1028,6 @@ static int cs8409_cs42l42_suspend(struct hda_codec *codec) return 0; } -#endif /* Vendor specific HW configuration * PLL, ASP, I2C, SPI, GPIOs, DMIC etc... @@ -1080,9 +1082,7 @@ static const struct hda_codec_ops cs8409_cs42l42_patch_ops = { .init = cs8409_init, .free = cs8409_free, .unsol_event = cs8409_cs42l42_jack_unsol_event, -#ifdef CONFIG_PM .suspend = cs8409_cs42l42_suspend, -#endif }; static int cs8409_cs42l42_exec_verb(struct hdac_device *dev, unsigned int cmd, unsigned int flags, @@ -1310,9 +1310,7 @@ static const struct hda_codec_ops cs8409_dolphin_patch_ops = { .init = cs8409_init, .free = cs8409_free, .unsol_event = dolphin_jack_unsol_event, -#ifdef CONFIG_PM .suspend = cs8409_cs42l42_suspend, -#endif }; static int dolphin_exec_verb(struct hdac_device *dev, unsigned int cmd, unsigned int flags, @@ -1411,8 +1409,9 @@ void dolphin_fixups(struct hda_codec *codec, const struct hda_fixup *fix, int ac kctrl = snd_hda_gen_add_kctl(&spec->gen, "Line Out Playback Volume", &cs42l42_dac_volume_mixer); /* Update Line Out kcontrol template */ - kctrl->private_value = HDA_COMPOSE_AMP_VAL_OFS(DOLPHIN_HP_PIN_NID, 3, CS8409_CODEC1, - HDA_OUTPUT, CS42L42_VOL_DAC) | HDA_AMP_VAL_MIN_MUTE; + if (kctrl) + kctrl->private_value = HDA_COMPOSE_AMP_VAL_OFS(DOLPHIN_HP_PIN_NID, 3, CS8409_CODEC1, + HDA_OUTPUT, CS42L42_VOL_DAC) | HDA_AMP_VAL_MIN_MUTE; cs8409_enable_ur(codec, 0); snd_hda_codec_set_name(codec, "CS8409/CS42L42"); break; diff --git a/sound/pci/hda/patch_cs8409.h b/sound/pci/hda/patch_cs8409.h index 937e9387abdc..e4bd2e12110b 100644 --- a/sound/pci/hda/patch_cs8409.h +++ b/sound/pci/hda/patch_cs8409.h @@ -229,10 +229,10 @@ enum cs8409_coefficient_index_registers { #define CS42L42_I2C_SLEEP_US (2000) #define CS42L42_PDN_TIMEOUT_US (250000) #define CS42L42_PDN_SLEEP_US (2000) -#define CS42L42_INIT_TIMEOUT_MS (45) +#define CS42L42_ANA_MUTE_AB (0x0C) #define CS42L42_FULL_SCALE_VOL_MASK (2) -#define CS42L42_FULL_SCALE_VOL_0DB (1) -#define CS42L42_FULL_SCALE_VOL_MINUS6DB (0) +#define CS42L42_FULL_SCALE_VOL_0DB (0) +#define CS42L42_FULL_SCALE_VOL_MINUS6DB (1) /* Dell BULLSEYE / WARLOCK / CYBORG Specific Definitions */ @@ -290,6 +290,7 @@ enum { struct cs8409_i2c_param { unsigned int addr; unsigned int value; + unsigned int delay; }; struct cs8409_cir_param { @@ -355,7 +356,7 @@ int cs42l42_volume_put(struct snd_kcontrol *kctrl, struct snd_ctl_elem_value *uc extern const struct hda_pcm_stream cs42l42_48k_pcm_analog_playback; extern const struct hda_pcm_stream cs42l42_48k_pcm_analog_capture; -extern const struct snd_pci_quirk cs8409_fixup_tbl[]; +extern const struct hda_quirk cs8409_fixup_tbl[]; extern const struct hda_model_fixup cs8409_models[]; extern const struct hda_fixup cs8409_fixups[]; extern const struct hda_verb cs8409_cs42l42_init_verbs[]; diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 495d63101186..643e0496b093 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -3,7 +3,7 @@ * * patch_hdmi.c - routines for HDMI/DisplayPort codecs * - * Copyright(c) 2008-2010 Intel Corporation. All rights reserved. + * Copyright(c) 2008-2010 Intel Corporation * Copyright (c) 2006 ATI Technologies Inc. * Copyright (c) 2008 NVIDIA Corp. All rights reserved. * Copyright (c) 2008 Wei Ni <wni@nvidia.com> @@ -1989,6 +1989,8 @@ static int hdmi_add_cvt(struct hda_codec *codec, hda_nid_t cvt_nid) } static const struct snd_pci_quirk force_connect_list[] = { + SND_PCI_QUIRK(0x103c, 0x83e2, "HP EliteDesk 800 G4", 1), + SND_PCI_QUIRK(0x103c, 0x83ef, "HP MP9 G4 Retail System AMS", 1), SND_PCI_QUIRK(0x103c, 0x870f, "HP", 1), SND_PCI_QUIRK(0x103c, 0x871a, "HP", 1), SND_PCI_QUIRK(0x103c, 0x8711, "HP", 1), @@ -2513,7 +2515,6 @@ static void generic_hdmi_free(struct hda_codec *codec) generic_spec_free(codec); } -#ifdef CONFIG_PM static int generic_hdmi_suspend(struct hda_codec *codec) { struct hdmi_spec *spec = codec->spec; @@ -2540,7 +2541,6 @@ static int generic_hdmi_resume(struct hda_codec *codec) } return 0; } -#endif static const struct hda_codec_ops generic_hdmi_patch_ops = { .init = generic_hdmi_init, @@ -2548,10 +2548,8 @@ static const struct hda_codec_ops generic_hdmi_patch_ops = { .build_pcms = generic_hdmi_build_pcms, .build_controls = generic_hdmi_build_controls, .unsol_event = hdmi_unsol_event, -#ifdef CONFIG_PM .suspend = generic_hdmi_suspend, .resume = generic_hdmi_resume, -#endif }; static const struct hdmi_ops generic_standard_hdmi_ops = { @@ -2952,7 +2950,6 @@ static void i915_pin_cvt_fixup(struct hda_codec *codec, } } -#ifdef CONFIG_PM static int i915_adlp_hdmi_suspend(struct hda_codec *codec) { struct hdmi_spec *spec = codec->spec; @@ -3032,7 +3029,6 @@ static int i915_adlp_hdmi_resume(struct hda_codec *codec) return res; } -#endif /* precondition and allocation for Intel codecs */ static int alloc_intel_hdmi(struct hda_codec *codec) @@ -3167,10 +3163,8 @@ static int patch_i915_adlp_hdmi(struct hda_codec *codec) if (spec->silent_stream_type) { spec->silent_stream_type = SILENT_STREAM_KAE; -#ifdef CONFIG_PM codec->patch_ops.resume = i915_adlp_hdmi_resume; codec->patch_ops.suspend = i915_adlp_hdmi_suspend; -#endif } } @@ -4642,8 +4636,10 @@ HDA_CODEC_ENTRY(0x8086281a, "Jasperlake HDMI", patch_i915_icl_hdmi), HDA_CODEC_ENTRY(0x8086281b, "Elkhartlake HDMI", patch_i915_icl_hdmi), HDA_CODEC_ENTRY(0x8086281c, "Alderlake-P HDMI", patch_i915_adlp_hdmi), HDA_CODEC_ENTRY(0x8086281d, "Meteor Lake HDMI", patch_i915_adlp_hdmi), +HDA_CODEC_ENTRY(0x8086281e, "Battlemage HDMI", patch_i915_adlp_hdmi), HDA_CODEC_ENTRY(0x8086281f, "Raptor Lake P HDMI", patch_i915_adlp_hdmi), HDA_CODEC_ENTRY(0x80862820, "Lunar Lake HDMI", patch_i915_adlp_hdmi), +HDA_CODEC_ENTRY(0x80862822, "Panther Lake HDMI", patch_i915_adlp_hdmi), HDA_CODEC_ENTRY(0x80862880, "CedarTrail HDMI", patch_generic_hdmi), HDA_CODEC_ENTRY(0x80862882, "Valleyview2 HDMI", patch_i915_byt_hdmi), HDA_CODEC_ENTRY(0x80862883, "Braswell HDMI", patch_i915_byt_hdmi), diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index a1facdb98d9a..a84857a3c2bf 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -11,15 +11,18 @@ */ #include <linux/acpi.h> +#include <linux/cleanup.h> #include <linux/init.h> #include <linux/delay.h> #include <linux/slab.h> #include <linux/pci.h> #include <linux/dmi.h> #include <linux/module.h> +#include <linux/i2c.h> #include <linux/input.h> #include <linux/leds.h> #include <linux/ctype.h> +#include <linux/spi/spi.h> #include <sound/core.h> #include <sound/jack.h> #include <sound/hda_codec.h> @@ -109,9 +112,7 @@ struct alc_spec { /* hooks */ void (*init_hook)(struct hda_codec *codec); -#ifdef CONFIG_PM void (*power_hook)(struct hda_codec *codec); -#endif void (*shutup)(struct hda_codec *codec); int init_amp; @@ -124,6 +125,7 @@ struct alc_spec { unsigned int has_hs_key:1; unsigned int no_internal_mic_pin:1; unsigned int en_3kpull_low:1; + int num_speaker_amps; /* for PLL fix */ hda_nid_t pll_nid; @@ -133,8 +135,7 @@ struct alc_spec { u8 alc_mute_keycode_map[1]; /* component binding */ - struct component_match *match; - struct hda_component comps[HDA_MAX_COMPONENTS]; + struct hda_component_parent comps; }; /* @@ -472,6 +473,8 @@ static void alc_fill_eapd_coef(struct hda_codec *codec) break; case 0x10ec0234: case 0x10ec0274: + alc_write_coef_idx(codec, 0x6e, 0x0c25); + fallthrough; case 0x10ec0294: case 0x10ec0700: case 0x10ec0701: @@ -586,10 +589,14 @@ static void alc_shutup_pins(struct hda_codec *codec) switch (codec->core.vendor_id) { case 0x10ec0236: case 0x10ec0256: + case 0x10ec0257: case 0x19e58326: case 0x10ec0283: + case 0x10ec0285: case 0x10ec0286: + case 0x10ec0287: case 0x10ec0288: + case 0x10ec0295: case 0x10ec0298: alc_headset_mic_no_shutup(codec); break; @@ -883,9 +890,7 @@ static void alc_ssid_check(struct hda_codec *codec, const hda_nid_t *ports) } } -/* - */ - +/* inverted digital-mic */ static void alc_fixup_inv_dmic(struct hda_codec *codec, const struct hda_fixup *fix, int action) { @@ -921,6 +926,8 @@ static void alc_pre_init(struct hda_codec *codec) ((codec)->core.dev.power.power_state.event == PM_EVENT_RESUME) #define is_s4_resume(codec) \ ((codec)->core.dev.power.power_state.event == PM_EVENT_RESTORE) +#define is_s4_suspend(codec) \ + ((codec)->core.dev.power.power_state.event == PM_EVENT_FREEZE) static int alc_init(struct hda_codec *codec) { @@ -944,9 +951,19 @@ static int alc_init(struct hda_codec *codec) return 0; } -#define alc_free snd_hda_gen_free +/* forward declaration */ +static const struct component_master_ops comp_master_ops; + +static void alc_free(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + if (spec) + hda_component_manager_free(&spec->comps, &comp_master_ops); + + snd_hda_gen_free(codec); +} -#ifdef CONFIG_PM static inline void alc_shutup(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; @@ -985,7 +1002,6 @@ static int alc_resume(struct hda_codec *codec) hda_call_check_power_status(codec, 0x01); return 0; } -#endif /* */ @@ -995,11 +1011,9 @@ static const struct hda_codec_ops alc_patch_ops = { .init = alc_init, .free = alc_free, .unsol_event = snd_hda_jack_unsol_event, -#ifdef CONFIG_PM .resume = alc_resume, .suspend = alc_suspend, .check_power_status = snd_hda_gen_check_power_status, -#endif }; @@ -1551,7 +1565,7 @@ static const struct hda_fixup alc880_fixups[] = { }, }; -static const struct snd_pci_quirk alc880_fixup_tbl[] = { +static const struct hda_quirk alc880_fixup_tbl[] = { SND_PCI_QUIRK(0x1019, 0x0f69, "Coeus G610P", ALC880_FIXUP_W810), SND_PCI_QUIRK(0x1043, 0x10c3, "ASUS W5A", ALC880_FIXUP_ASUS_W5A), SND_PCI_QUIRK(0x1043, 0x1964, "ASUS Z71V", ALC880_FIXUP_Z71V), @@ -1860,7 +1874,7 @@ static const struct hda_fixup alc260_fixups[] = { }, }; -static const struct snd_pci_quirk alc260_fixup_tbl[] = { +static const struct hda_quirk alc260_fixup_tbl[] = { SND_PCI_QUIRK(0x1025, 0x007b, "Acer C20x", ALC260_FIXUP_GPIO1), SND_PCI_QUIRK(0x1025, 0x007f, "Acer Aspire 9500", ALC260_FIXUP_COEF), SND_PCI_QUIRK(0x1025, 0x008f, "Acer", ALC260_FIXUP_GPIO1), @@ -2552,7 +2566,7 @@ static const struct hda_fixup alc882_fixups[] = { }, }; -static const struct snd_pci_quirk alc882_fixup_tbl[] = { +static const struct hda_quirk alc882_fixup_tbl[] = { SND_PCI_QUIRK(0x1025, 0x006c, "Acer Aspire 9810", ALC883_FIXUP_ACER_EAPD), SND_PCI_QUIRK(0x1025, 0x0090, "Acer Aspire", ALC883_FIXUP_ACER_EAPD), SND_PCI_QUIRK(0x1025, 0x0107, "Acer Aspire", ALC883_FIXUP_ACER_EAPD), @@ -2646,6 +2660,7 @@ static const struct snd_pci_quirk alc882_fixup_tbl[] = { SND_PCI_QUIRK(0x1558, 0x65f1, "Clevo PC50HS", ALC1220_FIXUP_CLEVO_PB51ED_PINS), SND_PCI_QUIRK(0x1558, 0x65f5, "Clevo PD50PN[NRT]", ALC1220_FIXUP_CLEVO_PB51ED_PINS), SND_PCI_QUIRK(0x1558, 0x66a2, "Clevo PE60RNE", ALC1220_FIXUP_CLEVO_PB51ED_PINS), + SND_PCI_QUIRK(0x1558, 0x66a6, "Clevo PE60SN[CDE]-[GS]", ALC1220_FIXUP_CLEVO_PB51ED_PINS), SND_PCI_QUIRK(0x1558, 0x67d1, "Clevo PB71[ER][CDF]", ALC1220_FIXUP_CLEVO_PB51ED_PINS), SND_PCI_QUIRK(0x1558, 0x67e1, "Clevo PB71[DE][CDF]", ALC1220_FIXUP_CLEVO_PB51ED_PINS), SND_PCI_QUIRK(0x1558, 0x67e5, "Clevo PC70D[PRS](?:-D|-G)?", ALC1220_FIXUP_CLEVO_PB51ED_PINS), @@ -2895,7 +2910,7 @@ static const struct hda_fixup alc262_fixups[] = { }, }; -static const struct snd_pci_quirk alc262_fixup_tbl[] = { +static const struct hda_quirk alc262_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x170b, "HP Z200", ALC262_FIXUP_HP_Z200), SND_PCI_QUIRK(0x10cf, 0x1397, "Fujitsu Lifebook S7110", ALC262_FIXUP_FSC_S7110), SND_PCI_QUIRK(0x10cf, 0x142d, "Fujitsu Lifebook E8410", ALC262_FIXUP_BENQ), @@ -3056,7 +3071,7 @@ static const struct hda_model_fixup alc268_fixup_models[] = { {} }; -static const struct snd_pci_quirk alc268_fixup_tbl[] = { +static const struct hda_quirk alc268_fixup_tbl[] = { SND_PCI_QUIRK(0x1025, 0x0139, "Acer TravelMate 6293", ALC268_FIXUP_SPDIF), SND_PCI_QUIRK(0x1025, 0x015b, "Acer AOA 150 (ZG5)", ALC268_FIXUP_INV_DMIC), /* below is codec SSID since multiple Toshiba laptops have the @@ -3598,25 +3613,22 @@ static void alc256_init(struct hda_codec *codec) hp_pin_sense = snd_hda_jack_detect(codec, hp_pin); - if (hp_pin_sense) + if (hp_pin_sense) { msleep(2); + alc_update_coefex_idx(codec, 0x57, 0x04, 0x0007, 0x1); /* Low power */ - alc_update_coefex_idx(codec, 0x57, 0x04, 0x0007, 0x1); /* Low power */ - - snd_hda_codec_write(codec, hp_pin, 0, - AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); - - if (hp_pin_sense || spec->ultra_low_power) - msleep(85); - - snd_hda_codec_write(codec, hp_pin, 0, + snd_hda_codec_write(codec, hp_pin, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); - if (hp_pin_sense || spec->ultra_low_power) - msleep(100); + msleep(75); + + snd_hda_codec_write(codec, hp_pin, 0, + AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE); + msleep(75); + alc_update_coefex_idx(codec, 0x57, 0x04, 0x0007, 0x4); /* Hight power */ + } alc_update_coef_idx(codec, 0x46, 3 << 12, 0); - alc_update_coefex_idx(codec, 0x57, 0x04, 0x0007, 0x4); /* Hight power */ alc_update_coefex_idx(codec, 0x53, 0x02, 0x8000, 1 << 15); /* Clear bit */ alc_update_coefex_idx(codec, 0x53, 0x02, 0x8000, 0 << 15); /* @@ -3640,29 +3652,28 @@ static void alc256_shutup(struct hda_codec *codec) alc_update_coefex_idx(codec, 0x57, 0x04, 0x0007, 0x1); /* Low power */ hp_pin_sense = snd_hda_jack_detect(codec, hp_pin); - if (hp_pin_sense) + if (hp_pin_sense) { msleep(2); - snd_hda_codec_write(codec, hp_pin, 0, + snd_hda_codec_write(codec, hp_pin, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); - if (hp_pin_sense || spec->ultra_low_power) - msleep(85); + msleep(75); /* 3k pull low control for Headset jack. */ /* NOTE: call this before clearing the pin, otherwise codec stalls */ /* If disable 3k pulldown control for alc257, the Mic detection will not work correctly * when booting with headset plugged. So skip setting it for the codec alc257 */ - if (spec->en_3kpull_low) - alc_update_coef_idx(codec, 0x46, 0, 3 << 12); + if (spec->en_3kpull_low) + alc_update_coef_idx(codec, 0x46, 0, 3 << 12); - if (!spec->no_shutup_pins) - snd_hda_codec_write(codec, hp_pin, 0, + if (!spec->no_shutup_pins) + snd_hda_codec_write(codec, hp_pin, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0); - if (hp_pin_sense || spec->ultra_low_power) - msleep(100); + msleep(75); + } alc_auto_setup_eapd(codec, false); alc_shutup_pins(codec); @@ -3757,33 +3768,29 @@ static void alc225_init(struct hda_codec *codec) hp1_pin_sense = snd_hda_jack_detect(codec, hp_pin); hp2_pin_sense = snd_hda_jack_detect(codec, 0x16); - if (hp1_pin_sense || hp2_pin_sense) + if (hp1_pin_sense || hp2_pin_sense) { msleep(2); + alc_update_coefex_idx(codec, 0x57, 0x04, 0x0007, 0x1); /* Low power */ + + if (hp1_pin_sense) + snd_hda_codec_write(codec, hp_pin, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); + if (hp2_pin_sense) + snd_hda_codec_write(codec, 0x16, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); + msleep(75); - alc_update_coefex_idx(codec, 0x57, 0x04, 0x0007, 0x1); /* Low power */ - - if (hp1_pin_sense || spec->ultra_low_power) - snd_hda_codec_write(codec, hp_pin, 0, - AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); - if (hp2_pin_sense) - snd_hda_codec_write(codec, 0x16, 0, - AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); - - if (hp1_pin_sense || hp2_pin_sense || spec->ultra_low_power) - msleep(85); - - if (hp1_pin_sense || spec->ultra_low_power) - snd_hda_codec_write(codec, hp_pin, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); - if (hp2_pin_sense) - snd_hda_codec_write(codec, 0x16, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); - - if (hp1_pin_sense || hp2_pin_sense || spec->ultra_low_power) - msleep(100); + if (hp1_pin_sense) + snd_hda_codec_write(codec, hp_pin, 0, + AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE); + if (hp2_pin_sense) + snd_hda_codec_write(codec, 0x16, 0, + AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE); - alc_update_coef_idx(codec, 0x4a, 3 << 10, 0); - alc_update_coefex_idx(codec, 0x57, 0x04, 0x0007, 0x4); /* Hight power */ + msleep(75); + alc_update_coef_idx(codec, 0x4a, 3 << 10, 0); + alc_update_coefex_idx(codec, 0x57, 0x04, 0x0007, 0x4); /* Hight power */ + } } static void alc225_shutup(struct hda_codec *codec) @@ -3795,36 +3802,35 @@ static void alc225_shutup(struct hda_codec *codec) if (!hp_pin) hp_pin = 0x21; - alc_disable_headset_jack_key(codec); - /* 3k pull low control for Headset jack. */ - alc_update_coef_idx(codec, 0x4a, 0, 3 << 10); - hp1_pin_sense = snd_hda_jack_detect(codec, hp_pin); hp2_pin_sense = snd_hda_jack_detect(codec, 0x16); - if (hp1_pin_sense || hp2_pin_sense) + if (hp1_pin_sense || hp2_pin_sense) { + alc_disable_headset_jack_key(codec); + /* 3k pull low control for Headset jack. */ + alc_update_coef_idx(codec, 0x4a, 0, 3 << 10); msleep(2); - if (hp1_pin_sense || spec->ultra_low_power) - snd_hda_codec_write(codec, hp_pin, 0, - AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); - if (hp2_pin_sense) - snd_hda_codec_write(codec, 0x16, 0, - AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); - - if (hp1_pin_sense || hp2_pin_sense || spec->ultra_low_power) - msleep(85); + if (hp1_pin_sense) + snd_hda_codec_write(codec, hp_pin, 0, + AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); + if (hp2_pin_sense) + snd_hda_codec_write(codec, 0x16, 0, + AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); - if (hp1_pin_sense || spec->ultra_low_power) - snd_hda_codec_write(codec, hp_pin, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0); - if (hp2_pin_sense) - snd_hda_codec_write(codec, 0x16, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0); + msleep(75); - if (hp1_pin_sense || hp2_pin_sense || spec->ultra_low_power) - msleep(100); + if (hp1_pin_sense) + snd_hda_codec_write(codec, hp_pin, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0); + if (hp2_pin_sense) + snd_hda_codec_write(codec, 0x16, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0); + msleep(75); + alc_update_coef_idx(codec, 0x4a, 3 << 10, 0); + alc_enable_headset_jack_key(codec); + } alc_auto_setup_eapd(codec, false); alc_shutup_pins(codec); if (spec->ultra_low_power) { @@ -3835,9 +3841,79 @@ static void alc225_shutup(struct hda_codec *codec) alc_update_coef_idx(codec, 0x4a, 3<<4, 2<<4); msleep(30); } +} + +static void alc222_init(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + hda_nid_t hp_pin = alc_get_hp_pin(spec); + bool hp1_pin_sense, hp2_pin_sense; + + if (!hp_pin) + return; + + msleep(30); + + hp1_pin_sense = snd_hda_jack_detect(codec, hp_pin); + hp2_pin_sense = snd_hda_jack_detect(codec, 0x14); + + if (hp1_pin_sense || hp2_pin_sense) { + msleep(2); + + if (hp1_pin_sense) + snd_hda_codec_write(codec, hp_pin, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); + if (hp2_pin_sense) + snd_hda_codec_write(codec, 0x14, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); + msleep(75); + + if (hp1_pin_sense) + snd_hda_codec_write(codec, hp_pin, 0, + AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE); + if (hp2_pin_sense) + snd_hda_codec_write(codec, 0x14, 0, + AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE); + + msleep(75); + } +} - alc_update_coef_idx(codec, 0x4a, 3 << 10, 0); - alc_enable_headset_jack_key(codec); +static void alc222_shutup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + hda_nid_t hp_pin = alc_get_hp_pin(spec); + bool hp1_pin_sense, hp2_pin_sense; + + if (!hp_pin) + hp_pin = 0x21; + + hp1_pin_sense = snd_hda_jack_detect(codec, hp_pin); + hp2_pin_sense = snd_hda_jack_detect(codec, 0x14); + + if (hp1_pin_sense || hp2_pin_sense) { + msleep(2); + + if (hp1_pin_sense) + snd_hda_codec_write(codec, hp_pin, 0, + AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); + if (hp2_pin_sense) + snd_hda_codec_write(codec, 0x14, 0, + AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); + + msleep(75); + + if (hp1_pin_sense) + snd_hda_codec_write(codec, hp_pin, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0); + if (hp2_pin_sense) + snd_hda_codec_write(codec, 0x14, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0); + + msleep(75); + } + alc_auto_setup_eapd(codec, false); + alc_shutup_pins(codec); } static void alc_default_init(struct hda_codec *codec) @@ -3853,20 +3929,18 @@ static void alc_default_init(struct hda_codec *codec) hp_pin_sense = snd_hda_jack_detect(codec, hp_pin); - if (hp_pin_sense) + if (hp_pin_sense) { msleep(2); - snd_hda_codec_write(codec, hp_pin, 0, - AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); - - if (hp_pin_sense) - msleep(85); + snd_hda_codec_write(codec, hp_pin, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); - snd_hda_codec_write(codec, hp_pin, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); + msleep(75); - if (hp_pin_sense) - msleep(100); + snd_hda_codec_write(codec, hp_pin, 0, + AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE); + msleep(75); + } } static void alc_default_shutup(struct hda_codec *codec) @@ -3882,22 +3956,20 @@ static void alc_default_shutup(struct hda_codec *codec) hp_pin_sense = snd_hda_jack_detect(codec, hp_pin); - if (hp_pin_sense) + if (hp_pin_sense) { msleep(2); - snd_hda_codec_write(codec, hp_pin, 0, - AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); - - if (hp_pin_sense) - msleep(85); - - if (!spec->no_shutup_pins) snd_hda_codec_write(codec, hp_pin, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0); + AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); - if (hp_pin_sense) - msleep(100); + msleep(75); + if (!spec->no_shutup_pins) + snd_hda_codec_write(codec, hp_pin, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0); + + msleep(75); + } alc_auto_setup_eapd(codec, false); alc_shutup_pins(codec); } @@ -4039,7 +4111,6 @@ static void alc5505_dsp_init(struct hda_codec *codec) #define alc5505_dsp_resume(codec) alc5505_dsp_back_from_halt(codec) #endif -#ifdef CONFIG_PM static int alc269_suspend(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; @@ -4085,7 +4156,6 @@ static int alc269_resume(struct hda_codec *codec) return 0; } -#endif /* CONFIG_PM */ static void alc269_fixup_pincfg_no_hp_to_lineout(struct hda_codec *codec, const struct hda_fixup *fix, int action) @@ -4720,6 +4790,21 @@ static void alc236_fixup_hp_coef_micmute_led(struct hda_codec *codec, } } +static void alc295_fixup_hp_mute_led_coefbit11(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_coef.idx = 0xb; + spec->mute_led_coef.mask = 3 << 3; + spec->mute_led_coef.on = 1 << 3; + spec->mute_led_coef.off = 1 << 4; + snd_hda_gen_add_mute_led_cdev(codec, coef_mute_led_set); + } +} + static void alc285_fixup_hp_mute_led(struct hda_codec *codec, const struct hda_fixup *fix, int action) { @@ -4802,7 +4887,134 @@ static void alc298_fixup_samsung_amp(struct hda_codec *codec, } } -#if IS_REACHABLE(CONFIG_INPUT) +struct alc298_samsung_v2_amp_desc { + unsigned short nid; + int init_seq_size; + unsigned short init_seq[18][2]; +}; + +static const struct alc298_samsung_v2_amp_desc +alc298_samsung_v2_amp_desc_tbl[] = { + { 0x38, 18, { + { 0x23e1, 0x0000 }, { 0x2012, 0x006f }, { 0x2014, 0x0000 }, + { 0x201b, 0x0001 }, { 0x201d, 0x0001 }, { 0x201f, 0x00fe }, + { 0x2021, 0x0000 }, { 0x2022, 0x0010 }, { 0x203d, 0x0005 }, + { 0x203f, 0x0003 }, { 0x2050, 0x002c }, { 0x2076, 0x000e }, + { 0x207c, 0x004a }, { 0x2081, 0x0003 }, { 0x2399, 0x0003 }, + { 0x23a4, 0x00b5 }, { 0x23a5, 0x0001 }, { 0x23ba, 0x0094 } + }}, + { 0x39, 18, { + { 0x23e1, 0x0000 }, { 0x2012, 0x006f }, { 0x2014, 0x0000 }, + { 0x201b, 0x0002 }, { 0x201d, 0x0002 }, { 0x201f, 0x00fd }, + { 0x2021, 0x0001 }, { 0x2022, 0x0010 }, { 0x203d, 0x0005 }, + { 0x203f, 0x0003 }, { 0x2050, 0x002c }, { 0x2076, 0x000e }, + { 0x207c, 0x004a }, { 0x2081, 0x0003 }, { 0x2399, 0x0003 }, + { 0x23a4, 0x00b5 }, { 0x23a5, 0x0001 }, { 0x23ba, 0x0094 } + }}, + { 0x3c, 15, { + { 0x23e1, 0x0000 }, { 0x2012, 0x006f }, { 0x2014, 0x0000 }, + { 0x201b, 0x0001 }, { 0x201d, 0x0001 }, { 0x201f, 0x00fe }, + { 0x2021, 0x0000 }, { 0x2022, 0x0010 }, { 0x203d, 0x0005 }, + { 0x203f, 0x0003 }, { 0x2050, 0x002c }, { 0x2076, 0x000e }, + { 0x207c, 0x004a }, { 0x2081, 0x0003 }, { 0x23ba, 0x008d } + }}, + { 0x3d, 15, { + { 0x23e1, 0x0000 }, { 0x2012, 0x006f }, { 0x2014, 0x0000 }, + { 0x201b, 0x0002 }, { 0x201d, 0x0002 }, { 0x201f, 0x00fd }, + { 0x2021, 0x0001 }, { 0x2022, 0x0010 }, { 0x203d, 0x0005 }, + { 0x203f, 0x0003 }, { 0x2050, 0x002c }, { 0x2076, 0x000e }, + { 0x207c, 0x004a }, { 0x2081, 0x0003 }, { 0x23ba, 0x008d } + }} +}; + +static void alc298_samsung_v2_enable_amps(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + static const unsigned short enable_seq[][2] = { + { 0x203a, 0x0081 }, { 0x23ff, 0x0001 }, + }; + int i, j; + + for (i = 0; i < spec->num_speaker_amps; i++) { + alc_write_coef_idx(codec, 0x22, alc298_samsung_v2_amp_desc_tbl[i].nid); + for (j = 0; j < ARRAY_SIZE(enable_seq); j++) + alc298_samsung_write_coef_pack(codec, enable_seq[j]); + codec_dbg(codec, "alc298_samsung_v2: Enabled speaker amp 0x%02x\n", + alc298_samsung_v2_amp_desc_tbl[i].nid); + } +} + +static void alc298_samsung_v2_disable_amps(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + static const unsigned short disable_seq[][2] = { + { 0x23ff, 0x0000 }, { 0x203a, 0x0080 }, + }; + int i, j; + + for (i = 0; i < spec->num_speaker_amps; i++) { + alc_write_coef_idx(codec, 0x22, alc298_samsung_v2_amp_desc_tbl[i].nid); + for (j = 0; j < ARRAY_SIZE(disable_seq); j++) + alc298_samsung_write_coef_pack(codec, disable_seq[j]); + codec_dbg(codec, "alc298_samsung_v2: Disabled speaker amp 0x%02x\n", + alc298_samsung_v2_amp_desc_tbl[i].nid); + } +} + +static void alc298_samsung_v2_playback_hook(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream, + int action) +{ + /* Dynamically enable/disable speaker amps before and after playback */ + if (action == HDA_GEN_PCM_ACT_OPEN) + alc298_samsung_v2_enable_amps(codec); + if (action == HDA_GEN_PCM_ACT_CLOSE) + alc298_samsung_v2_disable_amps(codec); +} + +static void alc298_samsung_v2_init_amps(struct hda_codec *codec, + int num_speaker_amps) +{ + struct alc_spec *spec = codec->spec; + int i, j; + + /* Set spec's num_speaker_amps before doing anything else */ + spec->num_speaker_amps = num_speaker_amps; + + /* Disable speaker amps before init to prevent any physical damage */ + alc298_samsung_v2_disable_amps(codec); + + /* Initialize the speaker amps */ + for (i = 0; i < spec->num_speaker_amps; i++) { + alc_write_coef_idx(codec, 0x22, alc298_samsung_v2_amp_desc_tbl[i].nid); + for (j = 0; j < alc298_samsung_v2_amp_desc_tbl[i].init_seq_size; j++) { + alc298_samsung_write_coef_pack(codec, + alc298_samsung_v2_amp_desc_tbl[i].init_seq[j]); + } + alc_write_coef_idx(codec, 0x89, 0x0); + codec_dbg(codec, "alc298_samsung_v2: Initialized speaker amp 0x%02x\n", + alc298_samsung_v2_amp_desc_tbl[i].nid); + } + + /* register hook to enable speaker amps only when they are needed */ + spec->gen.pcm_playback_hook = alc298_samsung_v2_playback_hook; +} + +static void alc298_fixup_samsung_amp_v2_2_amps(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + if (action == HDA_FIXUP_ACT_PROBE) + alc298_samsung_v2_init_amps(codec, 2); +} + +static void alc298_fixup_samsung_amp_v2_4_amps(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + if (action == HDA_FIXUP_ACT_PROBE) + alc298_samsung_v2_init_amps(codec, 4); +} + static void gpio2_mic_hotkey_event(struct hda_codec *codec, struct hda_jack_callback *event) { @@ -4911,10 +5123,6 @@ static void alc233_fixup_lenovo_line2_mic_hotkey(struct hda_codec *codec, spec->kb_dev = NULL; } } -#else /* INPUT */ -#define alc280_fixup_hp_gpio2_mic_hotkey NULL -#define alc233_fixup_lenovo_line2_mic_hotkey NULL -#endif /* INPUT */ static void alc269_fixup_hp_line1_mic1_led(struct hda_codec *codec, const struct hda_fixup *fix, int action) @@ -4928,6 +5136,40 @@ static void alc269_fixup_hp_line1_mic1_led(struct hda_codec *codec, } } +static void alc233_fixup_lenovo_low_en_micmute_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->micmute_led_polarity = 1; + alc233_fixup_lenovo_line2_mic_hotkey(codec, fix, action); +} + +static void alc_hp_mute_disable(struct hda_codec *codec, unsigned int delay) +{ + if (delay <= 0) + delay = 75; + snd_hda_codec_write(codec, 0x21, 0, + AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); + msleep(delay); + snd_hda_codec_write(codec, 0x21, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0); + msleep(delay); +} + +static void alc_hp_enable_unmute(struct hda_codec *codec, unsigned int delay) +{ + if (delay <= 0) + delay = 75; + snd_hda_codec_write(codec, 0x21, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); + msleep(delay); + snd_hda_codec_write(codec, 0x21, 0, + AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE); + msleep(delay); +} + static const struct coef_fw alc225_pre_hsmode[] = { UPDATE_COEF(0x4a, 1<<8, 0), UPDATE_COEFEX(0x57, 0x05, 1<<14, 0), @@ -5029,6 +5271,7 @@ static void alc_headset_mode_unplugged(struct hda_codec *codec) case 0x10ec0236: case 0x10ec0256: case 0x19e58326: + alc_hp_mute_disable(codec, 75); alc_process_coef_fw(codec, coef0256); break; case 0x10ec0234: @@ -5063,6 +5306,7 @@ static void alc_headset_mode_unplugged(struct hda_codec *codec) case 0x10ec0295: case 0x10ec0289: case 0x10ec0299: + alc_hp_mute_disable(codec, 75); alc_process_coef_fw(codec, alc225_pre_hsmode); alc_process_coef_fw(codec, coef0225); break; @@ -5288,6 +5532,7 @@ static void alc_headset_mode_default(struct hda_codec *codec) case 0x10ec0299: alc_process_coef_fw(codec, alc225_pre_hsmode); alc_process_coef_fw(codec, coef0225); + alc_hp_enable_unmute(codec, 75); break; case 0x10ec0255: alc_process_coef_fw(codec, coef0255); @@ -5300,6 +5545,7 @@ static void alc_headset_mode_default(struct hda_codec *codec) alc_write_coef_idx(codec, 0x45, 0xc089); msleep(50); alc_process_coef_fw(codec, coef0256); + alc_hp_enable_unmute(codec, 75); break; case 0x10ec0234: case 0x10ec0274: @@ -5397,6 +5643,7 @@ static void alc_headset_mode_ctia(struct hda_codec *codec) case 0x10ec0256: case 0x19e58326: alc_process_coef_fw(codec, coef0256); + alc_hp_enable_unmute(codec, 75); break; case 0x10ec0234: case 0x10ec0274: @@ -5445,6 +5692,7 @@ static void alc_headset_mode_ctia(struct hda_codec *codec) alc_process_coef_fw(codec, coef0225_2); else alc_process_coef_fw(codec, coef0225_1); + alc_hp_enable_unmute(codec, 75); break; case 0x10ec0867: alc_update_coefex_idx(codec, 0x57, 0x5, 1<<14, 0); @@ -5512,6 +5760,7 @@ static void alc_headset_mode_omtp(struct hda_codec *codec) case 0x10ec0256: case 0x19e58326: alc_process_coef_fw(codec, coef0256); + alc_hp_enable_unmute(codec, 75); break; case 0x10ec0234: case 0x10ec0274: @@ -5549,6 +5798,7 @@ static void alc_headset_mode_omtp(struct hda_codec *codec) case 0x10ec0289: case 0x10ec0299: alc_process_coef_fw(codec, coef0225); + alc_hp_enable_unmute(codec, 75); break; } codec_dbg(codec, "Headset jack set to Nokia-style headset mode.\n"); @@ -5617,25 +5867,21 @@ static void alc_determine_headset_type(struct hda_codec *codec) alc_write_coef_idx(codec, 0x06, 0x6104); alc_write_coefex_idx(codec, 0x57, 0x3, 0x09a3); - snd_hda_codec_write(codec, 0x21, 0, - AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); - msleep(80); - snd_hda_codec_write(codec, 0x21, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0); - alc_process_coef_fw(codec, coef0255); msleep(300); val = alc_read_coef_idx(codec, 0x46); is_ctia = (val & 0x0070) == 0x0070; - + if (!is_ctia) { + alc_write_coef_idx(codec, 0x45, 0xe089); + msleep(100); + val = alc_read_coef_idx(codec, 0x46); + if ((val & 0x0070) == 0x0070) + is_ctia = false; + else + is_ctia = true; + } alc_write_coefex_idx(codec, 0x57, 0x3, 0x0da3); alc_update_coefex_idx(codec, 0x57, 0x5, 1<<14, 0); - - snd_hda_codec_write(codec, 0x21, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); - msleep(80); - snd_hda_codec_write(codec, 0x21, 0, - AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE); break; case 0x10ec0234: case 0x10ec0274: @@ -5712,12 +5958,6 @@ static void alc_determine_headset_type(struct hda_codec *codec) case 0x10ec0295: case 0x10ec0289: case 0x10ec0299: - snd_hda_codec_write(codec, 0x21, 0, - AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); - msleep(80); - snd_hda_codec_write(codec, 0x21, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0); - alc_process_coef_fw(codec, alc225_pre_hsmode); alc_update_coef_idx(codec, 0x67, 0xf000, 0x1000); val = alc_read_coef_idx(codec, 0x45); @@ -5734,15 +5974,19 @@ static void alc_determine_headset_type(struct hda_codec *codec) val = alc_read_coef_idx(codec, 0x46); is_ctia = (val & 0x00f0) == 0x00f0; } + if (!is_ctia) { + alc_update_coef_idx(codec, 0x45, 0x3f<<10, 0x38<<10); + alc_update_coef_idx(codec, 0x49, 3<<8, 1<<8); + msleep(100); + val = alc_read_coef_idx(codec, 0x46); + if ((val & 0x00f0) == 0x00f0) + is_ctia = false; + else + is_ctia = true; + } alc_update_coef_idx(codec, 0x4a, 7<<6, 7<<6); alc_update_coef_idx(codec, 0x4a, 3<<4, 3<<4); alc_update_coef_idx(codec, 0x67, 0xf000, 0x3000); - - snd_hda_codec_write(codec, 0x21, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); - msleep(80); - snd_hda_codec_write(codec, 0x21, 0, - AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE); break; case 0x10ec0867: is_ctia = true; @@ -5750,7 +5994,7 @@ static void alc_determine_headset_type(struct hda_codec *codec) } codec_dbg(codec, "Headset jack detected iPhone-style headset: %s\n", - is_ctia ? "yes" : "no"); + str_yes_no(is_ctia)); spec->current_headset_type = is_ctia ? ALC_HEADSET_TYPE_CTIA : ALC_HEADSET_TYPE_OMTP; } @@ -6339,6 +6583,16 @@ static void alc285_fixup_speaker2_to_dac1(struct hda_codec *codec, } } +/* disable DAC3 (0x06) selection on NID 0x15 - share Speaker/Bass Speaker DAC 0x03 */ +static void alc294_fixup_bass_speaker_15(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + if (action == HDA_FIXUP_ACT_PRE_PROBE) { + static const hda_nid_t conn[] = { 0x02, 0x03 }; + snd_hda_override_conn_list(codec, 0x15, ARRAY_SIZE(conn), conn); + } +} + /* Hook to update amp GPIO4 for automute */ static void alc280_hp_gpio4_automute_hook(struct hda_codec *codec, struct hda_jack_callback *jack) @@ -6478,10 +6732,8 @@ static void alc289_fixup_asus_ga401(struct hda_codec *codec, }; struct alc_spec *spec = codec->spec; - if (action == HDA_FIXUP_ACT_PRE_PROBE) { + if (action == HDA_FIXUP_ACT_PRE_PROBE) spec->gen.preferred_dacs = preferred_pairs; - spec->gen.obey_preferred_dacs = 1; - } } /* The DAC of NID 0x3 will introduce click/pop noise on headphones, so invalidate it */ @@ -6533,6 +6785,20 @@ static void alc295_fixup_chromebook(struct hda_codec *codec, } } +static void alc256_fixup_chromebook(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct alc_spec *spec = codec->spec; + + switch (action) { + case HDA_FIXUP_ACT_PRE_PROBE: + spec->gen.suppress_auto_mute = 1; + spec->gen.suppress_auto_mic = 1; + spec->en_3kpull_low = false; + break; + } +} + static void alc_fixup_disable_mic_vref(struct hda_codec *codec, const struct hda_fixup *fix, int action) { @@ -6696,6 +6962,60 @@ static void alc285_fixup_hp_spectre_x360(struct hda_codec *codec, } } +static void alc285_fixup_hp_envy_x360(struct hda_codec *codec, + const struct hda_fixup *fix, + int action) +{ + static const struct coef_fw coefs[] = { + WRITE_COEF(0x08, 0x6a0c), WRITE_COEF(0x0d, 0xa023), + WRITE_COEF(0x10, 0x0320), WRITE_COEF(0x1a, 0x8c03), + WRITE_COEF(0x25, 0x1800), WRITE_COEF(0x26, 0x003a), + WRITE_COEF(0x28, 0x1dfe), WRITE_COEF(0x29, 0xb014), + WRITE_COEF(0x2b, 0x1dfe), WRITE_COEF(0x37, 0xfe15), + WRITE_COEF(0x38, 0x7909), WRITE_COEF(0x45, 0xd489), + WRITE_COEF(0x46, 0x00f4), WRITE_COEF(0x4a, 0x21e0), + WRITE_COEF(0x66, 0x03f0), WRITE_COEF(0x67, 0x1000), + WRITE_COEF(0x6e, 0x1005), { } + }; + + static const struct hda_pintbl pincfgs[] = { + { 0x12, 0xb7a60130 }, /* Internal microphone*/ + { 0x14, 0x90170150 }, /* B&O soundbar speakers */ + { 0x17, 0x90170153 }, /* Side speakers */ + { 0x19, 0x03a11040 }, /* Headset microphone */ + { } + }; + + switch (action) { + case HDA_FIXUP_ACT_PRE_PROBE: + snd_hda_apply_pincfgs(codec, pincfgs); + + /* Fixes volume control problem for side speakers */ + alc295_fixup_disable_dac3(codec, fix, action); + + /* Fixes no sound from headset speaker */ + snd_hda_codec_amp_stereo(codec, 0x21, HDA_OUTPUT, 0, -1, 0); + + /* Auto-enable headset mic when plugged */ + snd_hda_jack_set_gating_jack(codec, 0x19, 0x21); + + /* Headset mic volume enhancement */ + snd_hda_codec_set_pin_target(codec, 0x19, PIN_VREF50); + break; + case HDA_FIXUP_ACT_INIT: + alc_process_coef_fw(codec, coefs); + break; + case HDA_FIXUP_ACT_BUILD: + rename_ctl(codec, "Bass Speaker Playback Volume", + "B&O-Tuned Playback Volume"); + rename_ctl(codec, "Front Playback Switch", + "B&O Soundbar Playback Switch"); + rename_ctl(codec, "Bass Speaker Playback Switch", + "Side Speaker Playback Switch"); + break; + } +} + /* for hda_fixup_thinkpad_acpi() */ #include "thinkpad_helper.c" @@ -6706,6 +7026,15 @@ static void alc_fixup_thinkpad_acpi(struct hda_codec *codec, hda_fixup_thinkpad_acpi(codec, fix, action); } +/* for hda_fixup_ideapad_acpi() */ +#include "ideapad_hotkey_led_helper.c" + +static void alc_fixup_ideapad_acpi(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + hda_fixup_ideapad_acpi(codec, fix, action); +} + /* Fixup for Lenovo Legion 15IMHg05 speaker output on headset removal. */ static void alc287_fixup_legion_15imhg05_speakers(struct hda_codec *codec, const struct hda_fixup *fix, @@ -6720,91 +7049,29 @@ static void alc287_fixup_legion_15imhg05_speakers(struct hda_codec *codec, } } -#ifdef CONFIG_ACPI static void comp_acpi_device_notify(acpi_handle handle, u32 event, void *data) { struct hda_codec *cdc = data; struct alc_spec *spec = cdc->spec; - int i; codec_info(cdc, "ACPI Notification %d\n", event); - for (i = 0; i < HDA_MAX_COMPONENTS; i++) { - if (spec->comps[i].dev && spec->comps[i].acpi_notify) - spec->comps[i].acpi_notify(acpi_device_handle(spec->comps[i].adev), event, - spec->comps[i].dev); - } -} - -static int comp_bind_acpi(struct device *dev) -{ - struct hda_codec *cdc = dev_to_hda_codec(dev); - struct alc_spec *spec = cdc->spec; - bool support_notifications = false; - struct acpi_device *adev; - int ret; - int i; - - adev = spec->comps[0].adev; - if (!acpi_device_handle(adev)) - return 0; - - for (i = 0; i < HDA_MAX_COMPONENTS; i++) - support_notifications = support_notifications || - spec->comps[i].acpi_notifications_supported; - - if (support_notifications) { - ret = acpi_install_notify_handler(adev->handle, ACPI_DEVICE_NOTIFY, - comp_acpi_device_notify, cdc); - if (ret < 0) { - codec_warn(cdc, "Failed to install notify handler: %d\n", ret); - return 0; - } - - codec_dbg(cdc, "Notify handler installed\n"); - } - - return 0; + hda_component_acpi_device_notify(&spec->comps, handle, event, data); } -static void comp_unbind_acpi(struct device *dev) -{ - struct hda_codec *cdc = dev_to_hda_codec(dev); - struct alc_spec *spec = cdc->spec; - struct acpi_device *adev; - int ret; - - adev = spec->comps[0].adev; - if (!acpi_device_handle(adev)) - return; - - ret = acpi_remove_notify_handler(adev->handle, ACPI_DEVICE_NOTIFY, - comp_acpi_device_notify); - if (ret < 0) - codec_warn(cdc, "Failed to uninstall notify handler: %d\n", ret); -} -#else -static int comp_bind_acpi(struct device *dev) -{ - return 0; -} - -static void comp_unbind_acpi(struct device *dev) -{ -} -#endif - static int comp_bind(struct device *dev) { struct hda_codec *cdc = dev_to_hda_codec(dev); struct alc_spec *spec = cdc->spec; int ret; - ret = component_bind_all(dev, spec->comps); + ret = hda_component_manager_bind(cdc, &spec->comps); if (ret) return ret; - return comp_bind_acpi(dev); + return hda_component_manager_bind_acpi_notifications(cdc, + &spec->comps, + comp_acpi_device_notify, cdc); } static void comp_unbind(struct device *dev) @@ -6812,8 +7079,8 @@ static void comp_unbind(struct device *dev) struct hda_codec *cdc = dev_to_hda_codec(dev); struct alc_spec *spec = cdc->spec; - comp_unbind_acpi(dev); - component_unbind_all(dev, spec->comps); + hda_component_manager_unbind_acpi_notifications(cdc, &spec->comps, comp_acpi_device_notify); + hda_component_manager_unbind(cdc, &spec->comps); } static const struct component_master_ops comp_master_ops = { @@ -6825,179 +7092,161 @@ static void comp_generic_playback_hook(struct hda_pcm_stream *hinfo, struct hda_ struct snd_pcm_substream *sub, int action) { struct alc_spec *spec = cdc->spec; - int i; - for (i = 0; i < HDA_MAX_COMPONENTS; i++) { - if (spec->comps[i].dev && spec->comps[i].pre_playback_hook) - spec->comps[i].pre_playback_hook(spec->comps[i].dev, action); - } - for (i = 0; i < HDA_MAX_COMPONENTS; i++) { - if (spec->comps[i].dev && spec->comps[i].playback_hook) - spec->comps[i].playback_hook(spec->comps[i].dev, action); - } - for (i = 0; i < HDA_MAX_COMPONENTS; i++) { - if (spec->comps[i].dev && spec->comps[i].post_playback_hook) - spec->comps[i].post_playback_hook(spec->comps[i].dev, action); - } + hda_component_manager_playback_hook(&spec->comps, action); } -struct scodec_dev_name { - const char *bus; - const char *hid; - int index; -}; - -/* match the device name in a slightly relaxed manner */ -static int comp_match_cs35l41_dev_name(struct device *dev, void *data) +static void comp_generic_fixup(struct hda_codec *cdc, int action, const char *bus, + const char *hid, const char *match_str, int count) { - struct scodec_dev_name *p = data; - const char *d = dev_name(dev); - int n = strlen(p->bus); - char tmp[32]; - - /* check the bus name */ - if (strncmp(d, p->bus, n)) - return 0; - /* skip the bus number */ - if (isdigit(d[n])) - n++; - /* the rest must be exact matching */ - snprintf(tmp, sizeof(tmp), "-%s:00-cs35l41-hda.%d", p->hid, p->index); - return !strcmp(d + n, tmp); -} - -static int comp_match_tas2781_dev_name(struct device *dev, - void *data) -{ - struct scodec_dev_name *p = data; - const char *d = dev_name(dev); - int n = strlen(p->bus); - char tmp[32]; - - /* check the bus name */ - if (strncmp(d, p->bus, n)) - return 0; - /* skip the bus number */ - if (isdigit(d[n])) - n++; - /* the rest must be exact matching */ - snprintf(tmp, sizeof(tmp), "-%s:00", p->hid); - - return !strcmp(d + n, tmp); -} - -static void cs35l41_generic_fixup(struct hda_codec *cdc, int action, const char *bus, - const char *hid, int count) -{ - struct device *dev = hda_codec_dev(cdc); struct alc_spec *spec = cdc->spec; - struct scodec_dev_name *rec; - int ret, i; + int ret; switch (action) { case HDA_FIXUP_ACT_PRE_PROBE: - for (i = 0; i < count; i++) { - rec = devm_kmalloc(dev, sizeof(*rec), GFP_KERNEL); - if (!rec) - return; - rec->bus = bus; - rec->hid = hid; - rec->index = i; - spec->comps[i].codec = cdc; - component_match_add(dev, &spec->match, - comp_match_cs35l41_dev_name, rec); - } - ret = component_master_add_with_match(dev, &comp_master_ops, spec->match); + ret = hda_component_manager_init(cdc, &spec->comps, count, bus, hid, + match_str, &comp_master_ops); if (ret) - codec_err(cdc, "Fail to register component aggregator %d\n", ret); - else - spec->gen.pcm_playback_hook = comp_generic_playback_hook; + return; + + spec->gen.pcm_playback_hook = comp_generic_playback_hook; break; case HDA_FIXUP_ACT_FREE: - component_master_del(dev, &comp_master_ops); + hda_component_manager_free(&spec->comps, &comp_master_ops); break; } } -static void tas2781_generic_fixup(struct hda_codec *cdc, int action, - const char *bus, const char *hid) +static void find_cirrus_companion_amps(struct hda_codec *cdc) { struct device *dev = hda_codec_dev(cdc); - struct alc_spec *spec = cdc->spec; - struct scodec_dev_name *rec; - int ret; + struct acpi_device *adev; + struct fwnode_handle *fwnode __free(fwnode_handle) = NULL; + const char *bus = NULL; + static const struct { + const char *hid; + const char *name; + } acpi_ids[] = {{ "CSC3554", "cs35l54-hda" }, + { "CSC3556", "cs35l56-hda" }, + { "CSC3557", "cs35l57-hda" }}; + char *match; + int i, count = 0, count_devindex = 0; + + for (i = 0; i < ARRAY_SIZE(acpi_ids); ++i) { + adev = acpi_dev_get_first_match_dev(acpi_ids[i].hid, NULL, -1); + if (adev) + break; + } + if (!adev) { + codec_dbg(cdc, "Did not find ACPI entry for a Cirrus Amp\n"); + return; + } - switch (action) { - case HDA_FIXUP_ACT_PRE_PROBE: - rec = devm_kmalloc(dev, sizeof(*rec), GFP_KERNEL); - if (!rec) - return; - rec->bus = bus; - rec->hid = hid; - rec->index = 0; - spec->comps[0].codec = cdc; - component_match_add(dev, &spec->match, - comp_match_tas2781_dev_name, rec); - ret = component_master_add_with_match(dev, &comp_master_ops, - spec->match); - if (ret) - codec_err(cdc, - "Fail to register component aggregator %d\n", - ret); - else - spec->gen.pcm_playback_hook = - comp_generic_playback_hook; - break; - case HDA_FIXUP_ACT_FREE: - component_master_del(dev, &comp_master_ops); - break; + count = i2c_acpi_client_count(adev); + if (count > 0) { + bus = "i2c"; + } else { + count = acpi_spi_count_resources(adev); + if (count > 0) + bus = "spi"; + } + + fwnode = fwnode_handle_get(acpi_fwnode_handle(adev)); + acpi_dev_put(adev); + + if (!bus) { + codec_err(cdc, "Did not find any buses for %s\n", acpi_ids[i].hid); + return; + } + + if (!fwnode) { + codec_err(cdc, "Could not get fwnode for %s\n", acpi_ids[i].hid); + return; } + + /* + * When available the cirrus,dev-index property is an accurate + * count of the amps in a system and is used in preference to + * the count of bus devices that can contain additional address + * alias entries. + */ + count_devindex = fwnode_property_count_u32(fwnode, "cirrus,dev-index"); + if (count_devindex > 0) + count = count_devindex; + + match = devm_kasprintf(dev, GFP_KERNEL, "-%%s:00-%s.%%d", acpi_ids[i].name); + if (!match) + return; + codec_info(cdc, "Found %d %s on %s (%s)\n", count, acpi_ids[i].hid, bus, match); + comp_generic_fixup(cdc, HDA_FIXUP_ACT_PRE_PROBE, bus, acpi_ids[i].hid, match, count); } static void cs35l41_fixup_i2c_two(struct hda_codec *cdc, const struct hda_fixup *fix, int action) { - cs35l41_generic_fixup(cdc, action, "i2c", "CSC3551", 2); + comp_generic_fixup(cdc, action, "i2c", "CSC3551", "-%s:00-cs35l41-hda.%d", 2); } static void cs35l41_fixup_i2c_four(struct hda_codec *cdc, const struct hda_fixup *fix, int action) { - cs35l41_generic_fixup(cdc, action, "i2c", "CSC3551", 4); + comp_generic_fixup(cdc, action, "i2c", "CSC3551", "-%s:00-cs35l41-hda.%d", 4); } static void cs35l41_fixup_spi_two(struct hda_codec *codec, const struct hda_fixup *fix, int action) { - cs35l41_generic_fixup(codec, action, "spi", "CSC3551", 2); + comp_generic_fixup(codec, action, "spi", "CSC3551", "-%s:00-cs35l41-hda.%d", 2); } static void cs35l41_fixup_spi_four(struct hda_codec *codec, const struct hda_fixup *fix, int action) { - cs35l41_generic_fixup(codec, action, "spi", "CSC3551", 4); + comp_generic_fixup(codec, action, "spi", "CSC3551", "-%s:00-cs35l41-hda.%d", 4); } static void alc287_fixup_legion_16achg6_speakers(struct hda_codec *cdc, const struct hda_fixup *fix, int action) { - cs35l41_generic_fixup(cdc, action, "i2c", "CLSA0100", 2); + comp_generic_fixup(cdc, action, "i2c", "CLSA0100", "-%s:00-cs35l41-hda.%d", 2); } static void alc287_fixup_legion_16ithg6_speakers(struct hda_codec *cdc, const struct hda_fixup *fix, int action) { - cs35l41_generic_fixup(cdc, action, "i2c", "CLSA0101", 2); + comp_generic_fixup(cdc, action, "i2c", "CLSA0101", "-%s:00-cs35l41-hda.%d", 2); +} + +static void alc285_fixup_asus_ga403u(struct hda_codec *cdc, const struct hda_fixup *fix, int action) +{ + /* + * The same SSID has been re-used in different hardware, they have + * different codecs and the newer GA403U has a ALC285. + */ + if (cdc->core.vendor_id != 0x10ec0285) + alc_fixup_inv_dmic(cdc, fix, action); } static void tas2781_fixup_i2c(struct hda_codec *cdc, const struct hda_fixup *fix, int action) { - tas2781_generic_fixup(cdc, action, "i2c", "TIAS2781"); + comp_generic_fixup(cdc, action, "i2c", "TIAS2781", "-%s:00", 1); +} + +static void tas2781_fixup_spi(struct hda_codec *cdc, const struct hda_fixup *fix, int action) +{ + comp_generic_fixup(cdc, action, "spi", "TXNW2781", "-%s:00-tas2781-hda.%d", 2); } static void yoga7_14arb7_fixup_i2c(struct hda_codec *cdc, const struct hda_fixup *fix, int action) { - tas2781_generic_fixup(cdc, action, "i2c", "INT8866"); + comp_generic_fixup(cdc, action, "i2c", "INT8866", "-%s:00", 1); +} + +static void alc256_fixup_acer_sfg16_micmute_led(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + alc_fixup_hp_gpio_led(codec, action, 0, 0x04); } + /* for alc295_fixup_hp_top_speakers */ #include "hp_x360_helper.c" @@ -7064,6 +7313,25 @@ static void alc256_fixup_mic_no_presence_and_resume(struct hda_codec *codec, } } +static void alc256_decrease_headphone_amp_val(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + u32 caps; + u8 nsteps, offs; + + if (action != HDA_FIXUP_ACT_PRE_PROBE) + return; + + caps = query_amp_caps(codec, 0x3, HDA_OUTPUT); + nsteps = ((caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT) - 10; + offs = ((caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT) - 10; + caps &= ~AC_AMPCAP_NUM_STEPS & ~AC_AMPCAP_OFFSET; + caps |= (nsteps << AC_AMPCAP_NUM_STEPS_SHIFT) | (offs << AC_AMPCAP_OFFSET_SHIFT); + + if (snd_hda_override_amp_caps(codec, 0x3, HDA_OUTPUT, caps)) + codec_warn(codec, "failed to override amp caps for NID 0x3\n"); +} + static void alc_fixup_dell4_mic_no_presence_quiet(struct hda_codec *codec, const struct hda_fixup *fix, int action) @@ -7204,6 +7472,135 @@ static void alc_fixup_headset_mic(struct hda_codec *codec, } } +static void alc245_fixup_hp_spectre_x360_eu0xxx(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + /* + * The Pin Complex 0x14 for the treble speakers is wrongly reported as + * unconnected. + * The Pin Complex 0x17 for the bass speakers has the lowest association + * and sequence values so shift it up a bit to squeeze 0x14 in. + */ + static const struct hda_pintbl pincfgs[] = { + { 0x14, 0x90170110 }, // top/treble + { 0x17, 0x90170111 }, // bottom/bass + { } + }; + + /* + * Force DAC 0x02 for the bass speakers 0x17. + */ + static const hda_nid_t conn[] = { 0x02 }; + + switch (action) { + case HDA_FIXUP_ACT_PRE_PROBE: + snd_hda_apply_pincfgs(codec, pincfgs); + snd_hda_override_conn_list(codec, 0x17, ARRAY_SIZE(conn), conn); + break; + } + + cs35l41_fixup_i2c_two(codec, fix, action); + alc245_fixup_hp_mute_led_coefbit(codec, fix, action); + alc245_fixup_hp_gpio_led(codec, fix, action); +} + +/* some changes for Spectre x360 16, 2024 model */ +static void alc245_fixup_hp_spectre_x360_16_aa0xxx(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + /* + * The Pin Complex 0x14 for the treble speakers is wrongly reported as + * unconnected. + * The Pin Complex 0x17 for the bass speakers has the lowest association + * and sequence values so shift it up a bit to squeeze 0x14 in. + */ + struct alc_spec *spec = codec->spec; + static const struct hda_pintbl pincfgs[] = { + { 0x14, 0x90170110 }, // top/treble + { 0x17, 0x90170111 }, // bottom/bass + { } + }; + + /* + * Force DAC 0x02 for the bass speakers 0x17. + */ + static const hda_nid_t conn[] = { 0x02 }; + + switch (action) { + case HDA_FIXUP_ACT_PRE_PROBE: + /* needed for amp of back speakers */ + spec->gpio_mask |= 0x01; + spec->gpio_dir |= 0x01; + snd_hda_apply_pincfgs(codec, pincfgs); + snd_hda_override_conn_list(codec, 0x17, ARRAY_SIZE(conn), conn); + break; + case HDA_FIXUP_ACT_INIT: + /* need to toggle GPIO to enable the amp of back speakers */ + alc_update_gpio_data(codec, 0x01, true); + msleep(100); + alc_update_gpio_data(codec, 0x01, false); + break; + } + + cs35l41_fixup_i2c_two(codec, fix, action); + alc245_fixup_hp_mute_led_coefbit(codec, fix, action); + alc245_fixup_hp_gpio_led(codec, fix, action); +} + +/* + * ALC287 PCM hooks + */ +static void alc287_alc1318_playback_pcm_hook(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream, + int action) +{ + switch (action) { + case HDA_GEN_PCM_ACT_OPEN: + alc_write_coefex_idx(codec, 0x5a, 0x00, 0x954f); /* write gpio3 to high */ + break; + case HDA_GEN_PCM_ACT_CLOSE: + alc_write_coefex_idx(codec, 0x5a, 0x00, 0x554f); /* write gpio3 as default value */ + break; + } +} + +static void alc287_s4_power_gpio3_default(struct hda_codec *codec) +{ + if (is_s4_suspend(codec)) { + alc_write_coefex_idx(codec, 0x5a, 0x00, 0x554f); /* write gpio3 as default value */ + } +} + +static void alc287_fixup_lenovo_thinkpad_with_alc1318(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct alc_spec *spec = codec->spec; + static const struct coef_fw coefs[] = { + WRITE_COEF(0x24, 0x0013), WRITE_COEF(0x25, 0x0000), WRITE_COEF(0x26, 0xC300), + WRITE_COEF(0x28, 0x0001), WRITE_COEF(0x29, 0xb023), + WRITE_COEF(0x24, 0x0013), WRITE_COEF(0x25, 0x0000), WRITE_COEF(0x26, 0xC301), + WRITE_COEF(0x28, 0x0001), WRITE_COEF(0x29, 0xb023), + }; + + if (action != HDA_FIXUP_ACT_PRE_PROBE) + return; + alc_update_coef_idx(codec, 0x10, 1<<11, 1<<11); + alc_process_coef_fw(codec, coefs); + spec->power_hook = alc287_s4_power_gpio3_default; + spec->gen.pcm_playback_hook = alc287_alc1318_playback_pcm_hook; +} + +/* + * Clear COEF 0x0d (PCBEEP passthrough) bit 0x40 where BIOS sets it wrongly + * at PM resume + */ +static void alc283_fixup_dell_hp_resume(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + if (action == HDA_FIXUP_ACT_INIT) + alc_write_coef_idx(codec, 0xd, 0x2800); +} enum { ALC269_FIXUP_GPIO2, @@ -7245,6 +7642,7 @@ enum { ALC286_FIXUP_SONY_MIC_NO_PRESENCE, ALC269_FIXUP_PINCFG_NO_HP_TO_LINEOUT, ALC269_FIXUP_DELL1_MIC_NO_PRESENCE, + ALC269_FIXUP_DELL1_LIMIT_INT_MIC_BOOST, ALC269_FIXUP_DELL2_MIC_NO_PRESENCE, ALC269_FIXUP_DELL3_MIC_NO_PRESENCE, ALC269_FIXUP_DELL4_MIC_NO_PRESENCE, @@ -7273,12 +7671,17 @@ enum { ALC290_FIXUP_MONO_SPEAKERS_HSJACK, ALC290_FIXUP_SUBWOOFER, ALC290_FIXUP_SUBWOOFER_HSJACK, + ALC295_FIXUP_HP_MUTE_LED_COEFBIT11, ALC269_FIXUP_THINKPAD_ACPI, + ALC269_FIXUP_LENOVO_XPAD_ACPI, ALC269_FIXUP_DMIC_THINKPAD_ACPI, + ALC269VB_FIXUP_INFINIX_ZERO_BOOK_13, + ALC269VC_FIXUP_INFINIX_Y4_MAX, ALC269VB_FIXUP_CHUWI_COREBOOK_XPRO, ALC255_FIXUP_ACER_MIC_NO_PRESENCE, ALC255_FIXUP_ASUS_MIC_NO_PRESENCE, ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, + ALC255_FIXUP_DELL1_LIMIT_INT_MIC_BOOST, ALC255_FIXUP_DELL2_MIC_NO_PRESENCE, ALC255_FIXUP_HEADSET_MODE, ALC255_FIXUP_HEADSET_MODE_NO_HP_MIC, @@ -7297,6 +7700,7 @@ enum { ALC280_FIXUP_HP_9480M, ALC245_FIXUP_HP_X360_AMP, ALC285_FIXUP_HP_SPECTRE_X360_EB1, + ALC285_FIXUP_HP_ENVY_X360, ALC288_FIXUP_DELL_HEADSET_MODE, ALC288_FIXUP_DELL1_MIC_NO_PRESENCE, ALC288_FIXUP_DELL_XPS_13, @@ -7311,6 +7715,7 @@ enum { ALC275_FIXUP_DELL_XPS, ALC293_FIXUP_LENOVO_SPK_NOISE, ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY, + ALC233_FIXUP_LENOVO_L2MH_LOW_ENLED, ALC255_FIXUP_DELL_SPK_NOISE, ALC225_FIXUP_DISABLE_MIC_VREF, ALC225_FIXUP_DELL1_MIC_NO_PRESENCE, @@ -7368,6 +7773,7 @@ enum { ALC286_FIXUP_ACER_AIO_HEADSET_MIC, ALC256_FIXUP_ASUS_HEADSET_MIC, ALC256_FIXUP_ASUS_MIC_NO_PRESENCE, + ALC255_FIXUP_PREDATOR_SUBWOOFER, ALC299_FIXUP_PREDATOR_SPK, ALC256_FIXUP_MEDION_HEADSET_NO_PRESENCE, ALC289_FIXUP_DELL_SPK1, @@ -7399,7 +7805,10 @@ enum { ALC236_FIXUP_HP_GPIO_LED, ALC236_FIXUP_HP_MUTE_LED, ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF, + ALC236_FIXUP_LENOVO_INV_DMIC, ALC298_FIXUP_SAMSUNG_AMP, + ALC298_FIXUP_SAMSUNG_AMP_V2_2_AMPS, + ALC298_FIXUP_SAMSUNG_AMP_V2_4_AMPS, ALC298_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET, ALC256_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET, ALC295_FIXUP_ASUS_MIC_NO_PRESENCE, @@ -7423,6 +7832,7 @@ enum { ALC274_FIXUP_HP_MIC, ALC274_FIXUP_HP_HEADSET_MIC, ALC274_FIXUP_HP_ENVY_GPIO, + ALC274_FIXUP_ASUS_ZEN_AIO_27, ALC256_FIXUP_ASUS_HPE, ALC285_FIXUP_THINKPAD_NO_BASS_SPK_HEADSET_JACK, ALC287_FIXUP_HP_GPIO_LED, @@ -7445,7 +7855,6 @@ enum { ALC287_FIXUP_LEGION_15IMHG05_AUTOMUTE, ALC287_FIXUP_YOGA7_14ITL_SPEAKERS, ALC298_FIXUP_LENOVO_C940_DUET7, - ALC287_FIXUP_LENOVO_14IRP8_DUETITL, ALC287_FIXUP_13S_GEN2_SPEAKERS, ALC256_FIXUP_SET_COEF_DEFAULTS, ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE, @@ -7466,10 +7875,12 @@ enum { ALC287_FIXUP_LEGION_16ITHG6, ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK, ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN, + ALC287_FIXUP_YOGA9_14IMH9_BASS_SPK_PIN, ALC295_FIXUP_DELL_INSPIRON_TOP_SPEAKERS, ALC236_FIXUP_DELL_DUAL_CODECS, ALC287_FIXUP_CS35L41_I2C_2_THINKPAD_ACPI, ALC287_FIXUP_TAS2781_I2C, + ALC245_FIXUP_TAS2781_SPI_2, ALC287_FIXUP_YOGA7_14ARB7_I2C, ALC245_FIXUP_HP_MUTE_LED_COEFBIT, ALC245_FIXUP_HP_X360_MUTE_LEDS, @@ -7478,6 +7889,22 @@ enum { ALC2XX_FIXUP_HEADSET_MIC, ALC289_FIXUP_DELL_CS35L41_SPI_2, ALC294_FIXUP_CS35L41_I2C_2, + ALC256_FIXUP_ACER_SFG16_MICMUTE_LED, + ALC256_FIXUP_HEADPHONE_AMP_VOL, + ALC245_FIXUP_HP_SPECTRE_X360_EU0XXX, + ALC245_FIXUP_HP_SPECTRE_X360_16_AA0XXX, + ALC285_FIXUP_ASUS_GA403U, + ALC285_FIXUP_ASUS_GA403U_HEADSET_MIC, + ALC285_FIXUP_ASUS_GA403U_I2C_SPEAKER2_TO_DAC1, + ALC285_FIXUP_ASUS_GU605_SPI_2_HEADSET_MIC, + ALC285_FIXUP_ASUS_GU605_SPI_SPEAKER2_TO_DAC1, + ALC287_FIXUP_LENOVO_THKPAD_WH_ALC1318, + ALC256_FIXUP_CHROME_BOOK, + ALC245_FIXUP_CLEVO_NOISY_MIC, + ALC269_FIXUP_VAIO_VJFH52_MIC_NO_PRESENCE, + ALC233_FIXUP_MEDION_MTL_SPK, + ALC294_FIXUP_BASS_SPEAKER_15, + ALC283_FIXUP_DELL_HP_RESUME, }; /* A special fixup for Lenovo C940 and Yoga Duet 7; @@ -7497,26 +7924,6 @@ static void alc298_fixup_lenovo_c940_duet7(struct hda_codec *codec, __snd_hda_apply_fixup(codec, id, action, 0); } -/* A special fixup for Lenovo Slim/Yoga Pro 9 14IRP8 and Yoga DuetITL 2021; - * 14IRP8 PCI SSID will mistakenly be matched with the DuetITL codec SSID, - * so we need to apply a different fixup in this case. The only DuetITL codec - * SSID reported so far is the 17aa:3802 while the 14IRP8 has the 17aa:38be - * and 17aa:38bf. If it weren't for the PCI SSID, the 14IRP8 models would - * have matched correctly by their codecs. - */ -static void alc287_fixup_lenovo_14irp8_duetitl(struct hda_codec *codec, - const struct hda_fixup *fix, - int action) -{ - int id; - - if (codec->core.subsystem_id == 0x17aa3802) - id = ALC287_FIXUP_YOGA7_14ITL_SPEAKERS; /* DuetITL */ - else - id = ALC287_FIXUP_TAS2781_I2C; /* 14IRP8 */ - __snd_hda_apply_fixup(codec, id, action, 0); -} - static const struct hda_fixup alc269_fixups[] = { [ALC269_FIXUP_GPIO2] = { .type = HDA_FIXUP_FUNC, @@ -7651,6 +8058,25 @@ static const struct hda_fixup alc269_fixups[] = { .type = HDA_FIXUP_FUNC, .v.func = alc269_fixup_pincfg_U7x7_headset_mic, }, + [ALC269VB_FIXUP_INFINIX_ZERO_BOOK_13] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x14, 0x90170151 }, /* use as internal speaker (LFE) */ + { 0x1b, 0x90170152 }, /* use as internal speaker (back) */ + { } + }, + .chained = true, + .chain_id = ALC269_FIXUP_LIMIT_INT_MIC_BOOST + }, + [ALC269VC_FIXUP_INFINIX_Y4_MAX] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x1b, 0x90170150 }, /* use as internal speaker */ + { } + }, + .chained = true, + .chain_id = ALC269_FIXUP_LIMIT_INT_MIC_BOOST + }, [ALC269VB_FIXUP_CHUWI_COREBOOK_XPRO] = { .type = HDA_FIXUP_PINS, .v.pins = (const struct hda_pintbl[]) { @@ -7769,6 +8195,12 @@ static const struct hda_fixup alc269_fixups[] = { .chained = true, .chain_id = ALC269_FIXUP_HEADSET_MODE }, + [ALC269_FIXUP_DELL1_LIMIT_INT_MIC_BOOST] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc269_fixup_limit_int_mic_boost, + .chained = true, + .chain_id = ALC269_FIXUP_DELL1_MIC_NO_PRESENCE + }, [ALC269_FIXUP_DELL2_MIC_NO_PRESENCE] = { .type = HDA_FIXUP_PINS, .v.pins = (const struct hda_pintbl[]) { @@ -8015,6 +8447,12 @@ static const struct hda_fixup alc269_fixups[] = { .chained = true, .chain_id = ALC269_FIXUP_SKU_IGNORE, }, + [ALC269_FIXUP_LENOVO_XPAD_ACPI] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_ideapad_acpi, + .chained = true, + .chain_id = ALC269_FIXUP_THINKPAD_ACPI, + }, [ALC269_FIXUP_DMIC_THINKPAD_ACPI] = { .type = HDA_FIXUP_FUNC, .v.func = alc_fixup_inv_dmic, @@ -8049,6 +8487,12 @@ static const struct hda_fixup alc269_fixups[] = { .chained = true, .chain_id = ALC255_FIXUP_HEADSET_MODE }, + [ALC255_FIXUP_DELL1_LIMIT_INT_MIC_BOOST] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc269_fixup_limit_int_mic_boost, + .chained = true, + .chain_id = ALC255_FIXUP_DELL1_MIC_NO_PRESENCE + }, [ALC255_FIXUP_DELL2_MIC_NO_PRESENCE] = { .type = HDA_FIXUP_PINS, .v.pins = (const struct hda_pintbl[]) { @@ -8266,6 +8710,10 @@ static const struct hda_fixup alc269_fixups[] = { .type = HDA_FIXUP_FUNC, .v.func = alc233_fixup_lenovo_line2_mic_hotkey, }, + [ALC233_FIXUP_LENOVO_L2MH_LOW_ENLED] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc233_fixup_lenovo_low_en_micmute_led, + }, [ALC233_FIXUP_INTEL_NUC8_DMIC] = { .type = HDA_FIXUP_FUNC, .v.func = alc_fixup_inv_dmic, @@ -8715,6 +9163,13 @@ static const struct hda_fixup alc269_fixups[] = { .chained = true, .chain_id = ALC256_FIXUP_ASUS_HEADSET_MODE }, + [ALC255_FIXUP_PREDATOR_SUBWOOFER] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x17, 0x90170151 }, /* use as internal speaker (LFE) */ + { 0x1b, 0x90170152 } /* use as internal speaker (back) */ + } + }, [ALC299_FIXUP_PREDATOR_SPK] = { .type = HDA_FIXUP_PINS, .v.pins = (const struct hda_pintbl[]) { @@ -8956,12 +9411,30 @@ static const struct hda_fixup alc269_fixups[] = { .type = HDA_FIXUP_FUNC, .v.func = alc236_fixup_hp_mute_led_micmute_vref, }, + [ALC236_FIXUP_LENOVO_INV_DMIC] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_inv_dmic, + .chained = true, + .chain_id = ALC283_FIXUP_INT_MIC, + }, + [ALC295_FIXUP_HP_MUTE_LED_COEFBIT11] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc295_fixup_hp_mute_led_coefbit11, + }, [ALC298_FIXUP_SAMSUNG_AMP] = { .type = HDA_FIXUP_FUNC, .v.func = alc298_fixup_samsung_amp, .chained = true, .chain_id = ALC298_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET }, + [ALC298_FIXUP_SAMSUNG_AMP_V2_2_AMPS] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc298_fixup_samsung_amp_v2_2_amps + }, + [ALC298_FIXUP_SAMSUNG_AMP_V2_4_AMPS] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc298_fixup_samsung_amp_v2_4_amps + }, [ALC298_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET] = { .type = HDA_FIXUP_VERBS, .v.verbs = (const struct hda_verb[]) { @@ -9172,6 +9645,26 @@ static const struct hda_fixup alc269_fixups[] = { .type = HDA_FIXUP_FUNC, .v.func = alc274_fixup_hp_envy_gpio, }, + [ALC274_FIXUP_ASUS_ZEN_AIO_27] = { + .type = HDA_FIXUP_VERBS, + .v.verbs = (const struct hda_verb[]) { + { 0x20, AC_VERB_SET_COEF_INDEX, 0x10 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0xc420 }, + { 0x20, AC_VERB_SET_COEF_INDEX, 0x40 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x8800 }, + { 0x20, AC_VERB_SET_COEF_INDEX, 0x49 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x0249 }, + { 0x20, AC_VERB_SET_COEF_INDEX, 0x4a }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x202b }, + { 0x20, AC_VERB_SET_COEF_INDEX, 0x62 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0xa007 }, + { 0x20, AC_VERB_SET_COEF_INDEX, 0x6b }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x5060 }, + {} + }, + .chained = true, + .chain_id = ALC2XX_FIXUP_HEADSET_MIC, + }, [ALC256_FIXUP_ASUS_HPE] = { .type = HDA_FIXUP_VERBS, .v.verbs = (const struct hda_verb[]) { @@ -9273,6 +9766,12 @@ static const struct hda_fixup alc269_fixups[] = { .type = HDA_FIXUP_FUNC, .v.func = alc285_fixup_hp_spectre_x360_eb1 }, + [ALC285_FIXUP_HP_ENVY_X360] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc285_fixup_hp_envy_x360, + .chained = true, + .chain_id = ALC285_FIXUP_HP_GPIO_AMP_INIT, + }, [ALC287_FIXUP_IDEAPAD_BASS_SPK_AMP] = { .type = HDA_FIXUP_FUNC, .v.func = alc285_fixup_ideapad_s740_coef, @@ -9401,10 +9900,6 @@ static const struct hda_fixup alc269_fixups[] = { .type = HDA_FIXUP_FUNC, .v.func = alc298_fixup_lenovo_c940_duet7, }, - [ALC287_FIXUP_LENOVO_14IRP8_DUETITL] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc287_fixup_lenovo_14irp8_duetitl, - }, [ALC287_FIXUP_13S_GEN2_SPEAKERS] = { .type = HDA_FIXUP_VERBS, .v.verbs = (const struct hda_verb[]) { @@ -9589,6 +10084,12 @@ static const struct hda_fixup alc269_fixups[] = { .chained = true, .chain_id = ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK, }, + [ALC287_FIXUP_YOGA9_14IMH9_BASS_SPK_PIN] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc287_fixup_yoga9_14iap7_bass_spk_pin, + .chained = true, + .chain_id = ALC287_FIXUP_CS35L41_I2C_2, + }, [ALC295_FIXUP_DELL_INSPIRON_TOP_SPEAKERS] = { .type = HDA_FIXUP_FUNC, .v.func = alc295_fixup_dell_inspiron_top_speakers, @@ -9613,6 +10114,12 @@ static const struct hda_fixup alc269_fixups[] = { .chained = true, .chain_id = ALC285_FIXUP_THINKPAD_HEADSET_JACK, }, + [ALC245_FIXUP_TAS2781_SPI_2] = { + .type = HDA_FIXUP_FUNC, + .v.func = tas2781_fixup_spi, + .chained = true, + .chain_id = ALC285_FIXUP_HP_GPIO_LED, + }, [ALC287_FIXUP_YOGA7_14ARB7_I2C] = { .type = HDA_FIXUP_FUNC, .v.func = yoga7_14arb7_fixup_i2c, @@ -9655,9 +10162,102 @@ static const struct hda_fixup alc269_fixups[] = { .type = HDA_FIXUP_FUNC, .v.func = cs35l41_fixup_i2c_two, }, + [ALC256_FIXUP_ACER_SFG16_MICMUTE_LED] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc256_fixup_acer_sfg16_micmute_led, + }, + [ALC256_FIXUP_HEADPHONE_AMP_VOL] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc256_decrease_headphone_amp_val, + }, + [ALC245_FIXUP_HP_SPECTRE_X360_EU0XXX] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc245_fixup_hp_spectre_x360_eu0xxx, + }, + [ALC245_FIXUP_HP_SPECTRE_X360_16_AA0XXX] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc245_fixup_hp_spectre_x360_16_aa0xxx, + }, + [ALC285_FIXUP_ASUS_GA403U] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc285_fixup_asus_ga403u, + }, + [ALC285_FIXUP_ASUS_GA403U_HEADSET_MIC] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x19, 0x03a11050 }, + { 0x1b, 0x03a11c30 }, + { } + }, + .chained = true, + .chain_id = ALC285_FIXUP_ASUS_GA403U_I2C_SPEAKER2_TO_DAC1 + }, + [ALC285_FIXUP_ASUS_GU605_SPI_SPEAKER2_TO_DAC1] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc285_fixup_speaker2_to_dac1, + .chained = true, + .chain_id = ALC285_FIXUP_ASUS_GU605_SPI_2_HEADSET_MIC, + }, + [ALC285_FIXUP_ASUS_GU605_SPI_2_HEADSET_MIC] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x19, 0x03a11050 }, + { 0x1b, 0x03a11c30 }, + { } + }, + }, + [ALC285_FIXUP_ASUS_GA403U_I2C_SPEAKER2_TO_DAC1] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc285_fixup_speaker2_to_dac1, + .chained = true, + .chain_id = ALC285_FIXUP_ASUS_GA403U, + }, + [ALC287_FIXUP_LENOVO_THKPAD_WH_ALC1318] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc287_fixup_lenovo_thinkpad_with_alc1318, + .chained = true, + .chain_id = ALC269_FIXUP_THINKPAD_ACPI + }, + [ALC256_FIXUP_CHROME_BOOK] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc256_fixup_chromebook, + .chained = true, + .chain_id = ALC225_FIXUP_HEADSET_JACK + }, + [ALC245_FIXUP_CLEVO_NOISY_MIC] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc269_fixup_limit_int_mic_boost, + .chained = true, + .chain_id = ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE, + }, + [ALC269_FIXUP_VAIO_VJFH52_MIC_NO_PRESENCE] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x19, 0x03a1113c }, /* use as headset mic, without its own jack detect */ + { 0x1b, 0x20a11040 }, /* dock mic */ + { } + }, + .chained = true, + .chain_id = ALC269_FIXUP_LIMIT_INT_MIC_BOOST + }, + [ALC233_FIXUP_MEDION_MTL_SPK] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x1b, 0x90170110 }, + { } + }, + }, + [ALC294_FIXUP_BASS_SPEAKER_15] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc294_fixup_bass_speaker_15, + }, + [ALC283_FIXUP_DELL_HP_RESUME] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc283_fixup_dell_hp_resume, + }, }; -static const struct snd_pci_quirk alc269_fixup_tbl[] = { +static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1025, 0x0283, "Acer TravelMate 8371", ALC269_FIXUP_INV_DMIC), SND_PCI_QUIRK(0x1025, 0x029b, "Acer 1810TZ", ALC269_FIXUP_INV_DMIC), SND_PCI_QUIRK(0x1025, 0x0349, "Acer AOD260", ALC269_FIXUP_INV_DMIC), @@ -9670,6 +10270,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1025, 0x079b, "Acer Aspire V5-573G", ALC282_FIXUP_ASPIRE_V5_PINS), SND_PCI_QUIRK(0x1025, 0x080d, "Acer Aspire V5-122P", ALC269_FIXUP_ASPIRE_HEADSET_MIC), SND_PCI_QUIRK(0x1025, 0x0840, "Acer Aspire E1", ALC269VB_FIXUP_ASPIRE_E1_COEF), + SND_PCI_QUIRK(0x1025, 0x100c, "Acer Aspire E5-574G", ALC255_FIXUP_ACER_LIMIT_INT_MIC_BOOST), SND_PCI_QUIRK(0x1025, 0x101c, "Acer Veriton N2510G", ALC269_FIXUP_LIFEBOOK), SND_PCI_QUIRK(0x1025, 0x102b, "Acer Aspire C24-860", ALC286_FIXUP_ACER_AIO_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1025, 0x1065, "Acer Aspire C20-820", ALC269VC_FIXUP_ACER_HEADSET_MIC), @@ -9679,6 +10280,8 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1025, 0x110e, "Acer Aspire ES1-432", ALC255_FIXUP_ACER_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1025, 0x1166, "Acer Veriton N4640G", ALC269_FIXUP_LIFEBOOK), SND_PCI_QUIRK(0x1025, 0x1167, "Acer Veriton N6640G", ALC269_FIXUP_LIFEBOOK), + SND_PCI_QUIRK(0x1025, 0x1177, "Acer Predator G9-593", ALC255_FIXUP_PREDATOR_SUBWOOFER), + SND_PCI_QUIRK(0x1025, 0x1178, "Acer Predator G9-593", ALC255_FIXUP_PREDATOR_SUBWOOFER), SND_PCI_QUIRK(0x1025, 0x1246, "Acer Predator Helios 500", ALC299_FIXUP_PREDATOR_SPK), SND_PCI_QUIRK(0x1025, 0x1247, "Acer vCopperbox", ALC269VC_FIXUP_ACER_VCOPPERBOX_PINS), SND_PCI_QUIRK(0x1025, 0x1248, "Acer Veriton N4660G", ALC269VC_FIXUP_ACER_MIC_NO_PRESENCE), @@ -9693,11 +10296,14 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1025, 0x1308, "Acer Aspire Z24-890", ALC286_FIXUP_ACER_AIO_HEADSET_MIC), SND_PCI_QUIRK(0x1025, 0x132a, "Acer TravelMate B114-21", ALC233_FIXUP_ACER_HEADSET_MIC), SND_PCI_QUIRK(0x1025, 0x1330, "Acer TravelMate X514-51T", ALC255_FIXUP_ACER_HEADSET_MIC), + SND_PCI_QUIRK(0x1025, 0x1360, "Acer Aspire A115", ALC255_FIXUP_ACER_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1025, 0x141f, "Acer Spin SP513-54N", ALC255_FIXUP_ACER_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1025, 0x142b, "Acer Swift SF314-42", ALC255_FIXUP_ACER_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1025, 0x1430, "Acer TravelMate B311R-31", ALC256_FIXUP_ACER_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1025, 0x1466, "Acer Aspire A515-56", ALC255_FIXUP_ACER_HEADPHONE_AND_MIC), SND_PCI_QUIRK(0x1025, 0x1534, "Acer Predator PH315-54", ALC255_FIXUP_ACER_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1025, 0x159c, "Acer Nitro 5 AN515-58", ALC2XX_FIXUP_HEADSET_MIC), + SND_PCI_QUIRK(0x1025, 0x169a, "Acer Swift SFG16", ALC256_FIXUP_ACER_SFG16_MICMUTE_LED), SND_PCI_QUIRK(0x1028, 0x0470, "Dell M101z", ALC269_FIXUP_DELL_M101Z), SND_PCI_QUIRK(0x1028, 0x053c, "Dell Latitude E5430", ALC292_FIXUP_DELL_E7X), SND_PCI_QUIRK(0x1028, 0x054b, "Dell XPS one 2710", ALC275_FIXUP_DELL_XPS), @@ -9709,6 +10315,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1028, 0x05f4, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x05f5, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x05f6, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1028, 0x0604, "Dell Venue 11 Pro 7130", ALC283_FIXUP_DELL_HP_RESUME), SND_PCI_QUIRK(0x1028, 0x0615, "Dell Vostro 5470", ALC290_FIXUP_SUBWOOFER_HSJACK), SND_PCI_QUIRK(0x1028, 0x0616, "Dell Vostro 5470", ALC290_FIXUP_SUBWOOFER_HSJACK), SND_PCI_QUIRK(0x1028, 0x062c, "Dell Latitude E5550", ALC292_FIXUP_DELL_E7X), @@ -9774,6 +10381,8 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1028, 0x0c1e, "Dell Precision 3540", ALC236_FIXUP_DELL_DUAL_CODECS), SND_PCI_QUIRK(0x1028, 0x0c28, "Dell Inspiron 16 Plus 7630", ALC295_FIXUP_DELL_INSPIRON_TOP_SPEAKERS), SND_PCI_QUIRK(0x1028, 0x0c4d, "Dell", ALC287_FIXUP_CS35L41_I2C_4), + SND_PCI_QUIRK(0x1028, 0x0c94, "Dell Polaris 3 metal", ALC287_FIXUP_TAS2781_I2C), + SND_PCI_QUIRK(0x1028, 0x0c96, "Dell Polaris 2in1", ALC287_FIXUP_TAS2781_I2C), SND_PCI_QUIRK(0x1028, 0x0cbd, "Dell Oasis 13 CS MTL-U", ALC289_FIXUP_DELL_CS35L41_SPI_2), SND_PCI_QUIRK(0x1028, 0x0cbe, "Dell Oasis 13 2-IN-1 MTL-U", ALC289_FIXUP_DELL_CS35L41_SPI_2), SND_PCI_QUIRK(0x1028, 0x0cbf, "Dell Oasis 13 Low Weight MTU-L", ALC289_FIXUP_DELL_CS35L41_SPI_2), @@ -9856,14 +10465,18 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x83b9, "HP Spectre x360", ALC269_FIXUP_HP_MUTE_LED_MIC3), SND_PCI_QUIRK(0x103c, 0x841c, "HP Pavilion 15-CK0xx", ALC269_FIXUP_HP_MUTE_LED_MIC3), SND_PCI_QUIRK(0x103c, 0x8497, "HP Envy x360", ALC269_FIXUP_HP_MUTE_LED_MIC3), + SND_PCI_QUIRK(0x103c, 0x84a6, "HP 250 G7 Notebook PC", ALC269_FIXUP_HP_LINE1_MIC1_LED), SND_PCI_QUIRK(0x103c, 0x84ae, "HP 15-db0403ng", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), SND_PCI_QUIRK(0x103c, 0x84da, "HP OMEN dc0019-ur", ALC295_FIXUP_HP_OMEN), SND_PCI_QUIRK(0x103c, 0x84e7, "HP Pavilion 15", ALC269_FIXUP_HP_MUTE_LED_MIC3), SND_PCI_QUIRK(0x103c, 0x8519, "HP Spectre x360 15-df0xxx", ALC285_FIXUP_HP_SPECTRE_X360), SND_PCI_QUIRK(0x103c, 0x8537, "HP ProBook 440 G6", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), + SND_PCI_QUIRK(0x103c, 0x85c6, "HP Pavilion x360 Convertible 14-dy1xxx", ALC295_FIXUP_HP_MUTE_LED_COEFBIT11), + SND_PCI_QUIRK(0x103c, 0x85de, "HP Envy x360 13-ar0xxx", ALC285_FIXUP_HP_ENVY_X360), SND_PCI_QUIRK(0x103c, 0x860f, "HP ZBook 15 G6", ALC285_FIXUP_HP_GPIO_AMP_INIT), SND_PCI_QUIRK(0x103c, 0x861f, "HP Elite Dragonfly G1", ALC285_FIXUP_HP_GPIO_AMP_INIT), SND_PCI_QUIRK(0x103c, 0x869d, "HP", ALC236_FIXUP_HP_MUTE_LED), + SND_PCI_QUIRK(0x103c, 0x86c1, "HP Laptop 15-da3001TU", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), SND_PCI_QUIRK(0x103c, 0x86c7, "HP Envy AiO 32", ALC274_FIXUP_HP_ENVY_GPIO), SND_PCI_QUIRK(0x103c, 0x86e7, "HP Spectre x360 15-eb0xxx", ALC285_FIXUP_HP_SPECTRE_X360_EB1), SND_PCI_QUIRK(0x103c, 0x86e8, "HP Spectre x360 15-eb0xxx", ALC285_FIXUP_HP_SPECTRE_X360_EB1), @@ -9889,6 +10502,8 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x8788, "HP OMEN 15", ALC285_FIXUP_HP_MUTE_LED), SND_PCI_QUIRK(0x103c, 0x87b7, "HP Laptop 14-fq0xxx", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), SND_PCI_QUIRK(0x103c, 0x87c8, "HP", ALC287_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x87d3, "HP Laptop 15-gw0xxx", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), + SND_PCI_QUIRK(0x103c, 0x87df, "HP ProBook 430 G8 Notebook PC", ALC236_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x87e5, "HP ProBook 440 G8 Notebook PC", ALC236_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x87e7, "HP ProBook 450 G8 Notebook PC", ALC236_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x87f1, "HP ProBook 630 G8 Notebook PC", ALC236_FIXUP_HP_GPIO_LED), @@ -9897,6 +10512,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x87f5, "HP", ALC287_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x87f6, "HP Spectre x360 14", ALC245_FIXUP_HP_X360_AMP), SND_PCI_QUIRK(0x103c, 0x87f7, "HP Spectre x360 14", ALC245_FIXUP_HP_X360_AMP), + SND_PCI_QUIRK(0x103c, 0x87fd, "HP Laptop 14-dq2xxx", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), SND_PCI_QUIRK(0x103c, 0x87fe, "HP Laptop 15s-fq2xxx", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), SND_PCI_QUIRK(0x103c, 0x8805, "HP ProBook 650 G8 Notebook PC", ALC236_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x880d, "HP EliteBook 830 G8 Notebook PC", ALC285_FIXUP_HP_GPIO_LED), @@ -9913,12 +10529,14 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x8870, "HP ZBook Fury 15.6 Inch G8 Mobile Workstation PC", ALC285_FIXUP_HP_GPIO_AMP_INIT), SND_PCI_QUIRK(0x103c, 0x8873, "HP ZBook Studio 15.6 Inch G8 Mobile Workstation PC", ALC285_FIXUP_HP_GPIO_AMP_INIT), SND_PCI_QUIRK(0x103c, 0x887a, "HP Laptop 15s-eq2xxx", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), + SND_PCI_QUIRK(0x103c, 0x887c, "HP Laptop 14s-fq1xxx", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), SND_PCI_QUIRK(0x103c, 0x888a, "HP ENVY x360 Convertible 15-eu0xxx", ALC245_FIXUP_HP_X360_MUTE_LEDS), SND_PCI_QUIRK(0x103c, 0x888d, "HP ZBook Power 15.6 inch G8 Mobile Workstation PC", ALC236_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8895, "HP EliteBook 855 G8 Notebook PC", ALC285_FIXUP_HP_SPEAKERS_MICMUTE_LED), SND_PCI_QUIRK(0x103c, 0x8896, "HP EliteBook 855 G8 Notebook PC", ALC285_FIXUP_HP_MUTE_LED), SND_PCI_QUIRK(0x103c, 0x8898, "HP EliteBook 845 G8 Notebook PC", ALC285_FIXUP_HP_LIMIT_INT_MIC_BOOST), SND_PCI_QUIRK(0x103c, 0x88d0, "HP Pavilion 15-eh1xxx (mainboard 88D0)", ALC287_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x88dd, "HP Pavilion 15z-ec200", ALC285_FIXUP_HP_MUTE_LED), SND_PCI_QUIRK(0x103c, 0x8902, "HP OMEN 16", ALC285_FIXUP_HP_MUTE_LED), SND_PCI_QUIRK(0x103c, 0x890e, "HP 255 G8 Notebook PC", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), SND_PCI_QUIRK(0x103c, 0x8919, "HP Pavilion Aero Laptop 13-be0xxx", ALC287_FIXUP_HP_GPIO_LED), @@ -9947,9 +10565,21 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x89c6, "Zbook Fury 17 G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x89ca, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), SND_PCI_QUIRK(0x103c, 0x89d3, "HP EliteBook 645 G9 (MB 89D2)", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), + SND_PCI_QUIRK(0x103c, 0x89e7, "HP Elite x2 G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8a0f, "HP Pavilion 14-ec1xxx", ALC287_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8a20, "HP Laptop 15s-fq5xxx", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), SND_PCI_QUIRK(0x103c, 0x8a25, "HP Victus 16-d1xxx (MB 8A25)", ALC245_FIXUP_HP_MUTE_LED_COEFBIT), + SND_PCI_QUIRK(0x103c, 0x8a28, "HP Envy 13", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8a29, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8a2a, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8a2b, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8a2c, "HP Envy 16", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8a2d, "HP Envy 16", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8a2e, "HP Envy 16", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8a30, "HP Envy 17", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8a31, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8a6e, "HP EDNA 360", ALC287_FIXUP_CS35L41_I2C_4), + SND_PCI_QUIRK(0x103c, 0x8a74, "HP ProBook 440 G8 Notebook PC", ALC236_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8a78, "HP Dev One", ALC285_FIXUP_HP_LIMIT_INT_MIC_BOOST), SND_PCI_QUIRK(0x103c, 0x8aa0, "HP ProBook 440 G9 (MB 8A9E)", ALC236_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8aa3, "HP ProBook 450 G9 (MB 8AA1)", ALC236_FIXUP_HP_GPIO_LED), @@ -9959,8 +10589,10 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x8abb, "HP ZBook Firefly 14 G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8ad1, "HP EliteBook 840 14 inch G9 Notebook PC", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8ad2, "HP EliteBook 860 16 inch G9 Notebook PC", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8ad8, "HP 800 G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8b0f, "HP Elite mt645 G7 Mobile Thin Client U81", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), SND_PCI_QUIRK(0x103c, 0x8b2f, "HP 255 15.6 inch G10 Notebook PC", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), + SND_PCI_QUIRK(0x103c, 0x8b3a, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x103c, 0x8b3f, "HP mt440 Mobile Thin Client U91", ALC236_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8b42, "HP", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8b43, "HP", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), @@ -9971,6 +10603,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x8b59, "HP Elite mt645 G7 Mobile Thin Client U89", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), SND_PCI_QUIRK(0x103c, 0x8b5d, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), SND_PCI_QUIRK(0x103c, 0x8b5e, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), + SND_PCI_QUIRK(0x103c, 0x8b5f, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), SND_PCI_QUIRK(0x103c, 0x8b63, "HP Elite Dragonfly 13.5 inch G4", ALC245_FIXUP_CS35L41_SPI_4_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8b65, "HP ProBook 455 15.6 inch G10 Notebook PC", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), SND_PCI_QUIRK(0x103c, 0x8b66, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), @@ -9988,16 +10621,56 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x8b92, "HP", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8b96, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), SND_PCI_QUIRK(0x103c, 0x8b97, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), + SND_PCI_QUIRK(0x103c, 0x8bb3, "HP Slim OMEN", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8bb4, "HP Slim OMEN", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8bdd, "HP Envy 17", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8bde, "HP Envy 17", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8bdf, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8be0, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8be1, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8be2, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8be3, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8be5, "HP Envy 16", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8be6, "HP Envy 16", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8be7, "HP Envy 17", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8be8, "HP Envy 17", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8be9, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x103c, 0x8bf0, "HP", ALC236_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8c15, "HP Spectre x360 2-in-1 Laptop 14-eu0xxx", ALC245_FIXUP_HP_SPECTRE_X360_EU0XXX), + SND_PCI_QUIRK(0x103c, 0x8c16, "HP Spectre x360 2-in-1 Laptop 16-aa0xxx", ALC245_FIXUP_HP_SPECTRE_X360_16_AA0XXX), + SND_PCI_QUIRK(0x103c, 0x8c17, "HP Spectre 16", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8c21, "HP Pavilion Plus Laptop 14-ey0XXX", ALC245_FIXUP_HP_X360_MUTE_LEDS), + SND_PCI_QUIRK(0x103c, 0x8c30, "HP Victus 15-fb1xxx", ALC245_FIXUP_HP_MUTE_LED_COEFBIT), SND_PCI_QUIRK(0x103c, 0x8c46, "HP EliteBook 830 G11", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8c47, "HP EliteBook 840 G11", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8c48, "HP EliteBook 860 G11", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8c49, "HP Elite x360 830 2-in-1 G11", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8c4d, "HP Omen", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8c4e, "HP Omen", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8c4f, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8c50, "HP Envy 17", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8c51, "HP Envy 17", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8c52, "HP EliteBook 1040 G11", ALC285_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8c53, "HP Elite x360 1040 2-in-1 G11", ALC285_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8c66, "HP Envy 16", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8c67, "HP Envy 17", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8c68, "HP Envy 17", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8c6a, "HP Envy 16", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x103c, 0x8c70, "HP EliteBook 835 G11", ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8c71, "HP EliteBook 845 G11", ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8c72, "HP EliteBook 865 G11", ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8c7b, "HP ProBook 445 G11", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), + SND_PCI_QUIRK(0x103c, 0x8c7c, "HP ProBook 445 G11", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), + SND_PCI_QUIRK(0x103c, 0x8c7d, "HP ProBook 465 G11", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), + SND_PCI_QUIRK(0x103c, 0x8c7e, "HP ProBook 465 G11", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), + SND_PCI_QUIRK(0x103c, 0x8c7f, "HP EliteBook 645 G11", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), + SND_PCI_QUIRK(0x103c, 0x8c80, "HP EliteBook 645 G11", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), + SND_PCI_QUIRK(0x103c, 0x8c81, "HP EliteBook 665 G11", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), + SND_PCI_QUIRK(0x103c, 0x8c89, "HP ProBook 460 G11", ALC236_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8c8a, "HP EliteBook 630", ALC236_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8c8c, "HP EliteBook 660", ALC236_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8c8d, "HP ProBook 440 G11", ALC236_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8c8e, "HP ProBook 460 G11", ALC236_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8c90, "HP EliteBook 640", ALC236_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8c91, "HP EliteBook 660", ALC236_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8c96, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), @@ -10006,28 +10679,53 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x8ca2, "HP ZBook Power", ALC236_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8ca4, "HP ZBook Fury", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8ca7, "HP ZBook Fury", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8caf, "HP Elite mt645 G8 Mobile Thin Client", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), + SND_PCI_QUIRK(0x103c, 0x8cbd, "HP Pavilion Aero Laptop 13-bg0xxx", ALC245_FIXUP_HP_X360_MUTE_LEDS), + SND_PCI_QUIRK(0x103c, 0x8cdd, "HP Spectre", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8cde, "HP Spectre", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8cdf, "HP SnowWhite", ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8ce0, "HP SnowWhite", ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8cf5, "HP ZBook Studio 16", ALC245_FIXUP_CS35L41_SPI_4_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8d01, "HP ZBook Power 14 G12", ALC285_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8d84, "HP EliteBook X G1i", ALC285_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8d91, "HP ZBook Firefly 14 G12", ALC285_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8d92, "HP ZBook Firefly 16 G12", ALC285_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8de8, "HP Gemtree", ALC245_FIXUP_TAS2781_SPI_2), + SND_PCI_QUIRK(0x103c, 0x8de9, "HP Gemtree", ALC245_FIXUP_TAS2781_SPI_2), + SND_PCI_QUIRK(0x103c, 0x8e18, "HP ZBook Firefly 14 G12A", ALC285_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8e19, "HP ZBook Firefly 14 G12A", ALC285_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8e1a, "HP ZBook Firefly 14 G12A", ALC285_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x1043, 0x103e, "ASUS X540SA", ALC256_FIXUP_ASUS_MIC), SND_PCI_QUIRK(0x1043, 0x103f, "ASUS TX300", ALC282_FIXUP_ASUS_TX300), + SND_PCI_QUIRK(0x1043, 0x1054, "ASUS G614FH/FM/FP", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x1043, 0x106d, "Asus K53BE", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), + SND_PCI_QUIRK(0x1043, 0x1074, "ASUS G614PH/PM/PP", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x1043, 0x10a1, "ASUS UX391UA", ALC294_FIXUP_ASUS_SPK), + SND_PCI_QUIRK(0x1043, 0x10a4, "ASUS TP3407SA", ALC287_FIXUP_TAS2781_I2C), SND_PCI_QUIRK(0x1043, 0x10c0, "ASUS X540SA", ALC256_FIXUP_ASUS_MIC), SND_PCI_QUIRK(0x1043, 0x10d0, "ASUS X540LA/X540LJ", ALC255_FIXUP_ASUS_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1043, 0x10d3, "ASUS K6500ZC", ALC294_FIXUP_ASUS_SPK), + SND_PCI_QUIRK(0x1043, 0x1154, "ASUS TP3607SH", ALC287_FIXUP_TAS2781_I2C), SND_PCI_QUIRK(0x1043, 0x115d, "Asus 1015E", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), + SND_PCI_QUIRK(0x1043, 0x1194, "ASUS UM3406KA", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x1043, 0x11c0, "ASUS X556UR", ALC255_FIXUP_ASUS_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1043, 0x1204, "ASUS Strix G615JHR_JMR_JPR", ALC287_FIXUP_TAS2781_I2C), + SND_PCI_QUIRK(0x1043, 0x1214, "ASUS Strix G615LH_LM_LP", ALC287_FIXUP_TAS2781_I2C), SND_PCI_QUIRK(0x1043, 0x125e, "ASUS Q524UQK", ALC255_FIXUP_ASUS_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1043, 0x1271, "ASUS X430UN", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1043, 0x1290, "ASUS X441SA", ALC233_FIXUP_EAPD_COEF_AND_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1043, 0x1294, "ASUS B3405CVA", ALC245_FIXUP_CS35L41_SPI_2), SND_PCI_QUIRK(0x1043, 0x12a0, "ASUS X441UV", ALC233_FIXUP_EAPD_COEF_AND_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1043, 0x12a3, "Asus N7691ZM", ALC269_FIXUP_ASUS_N7601ZM), SND_PCI_QUIRK(0x1043, 0x12af, "ASUS UX582ZS", ALC245_FIXUP_CS35L41_SPI_2), + SND_PCI_QUIRK(0x1043, 0x12b4, "ASUS B3405CCA / P3405CCA", ALC245_FIXUP_CS35L41_SPI_2), SND_PCI_QUIRK(0x1043, 0x12e0, "ASUS X541SA", ALC256_FIXUP_ASUS_MIC), SND_PCI_QUIRK(0x1043, 0x12f0, "ASUS X541UV", ALC256_FIXUP_ASUS_MIC), SND_PCI_QUIRK(0x1043, 0x1313, "Asus K42JZ", ALC269VB_FIXUP_ASUS_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1043, 0x13b0, "ASUS Z550SA", ALC256_FIXUP_ASUS_MIC), SND_PCI_QUIRK(0x1043, 0x1427, "Asus Zenbook UX31E", ALC269VB_FIXUP_ASUS_ZENBOOK), SND_PCI_QUIRK(0x1043, 0x1433, "ASUS GX650PY/PZ/PV/PU/PYV/PZV/PIV/PVV", ALC285_FIXUP_ASUS_I2C_HEADSET_MIC), + SND_PCI_QUIRK(0x1043, 0x1460, "Asus VivoBook 15", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1043, 0x1463, "Asus GA402X/GA402N", ALC285_FIXUP_ASUS_I2C_HEADSET_MIC), SND_PCI_QUIRK(0x1043, 0x1473, "ASUS GU604VI/VC/VE/VG/VJ/VQ/VU/VV/VY/VZ", ALC285_FIXUP_ASUS_HEADSET_MIC), SND_PCI_QUIRK(0x1043, 0x1483, "ASUS GU603VQ/VU/VV/VJ/VI", ALC285_FIXUP_ASUS_HEADSET_MIC), @@ -10060,12 +10758,11 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1043, 0x19ce, "ASUS B9450FA", ALC294_FIXUP_ASUS_HPE), SND_PCI_QUIRK(0x1043, 0x19e1, "ASUS UX581LV", ALC295_FIXUP_ASUS_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1043, 0x1a13, "Asus G73Jw", ALC269_FIXUP_ASUS_G73JW), - SND_PCI_QUIRK(0x1043, 0x1a30, "ASUS X705UD", ALC256_FIXUP_ASUS_MIC), SND_PCI_QUIRK(0x1043, 0x1a63, "ASUS UX3405MA", ALC245_FIXUP_CS35L41_SPI_2), SND_PCI_QUIRK(0x1043, 0x1a83, "ASUS UM5302LA", ALC294_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x1043, 0x1a8f, "ASUS UX582ZS", ALC245_FIXUP_CS35L41_SPI_2), SND_PCI_QUIRK(0x1043, 0x1b11, "ASUS UX431DA", ALC294_FIXUP_ASUS_COEF_1B), - SND_PCI_QUIRK(0x1043, 0x1b13, "Asus U41SV", ALC269_FIXUP_INV_DMIC), + SND_PCI_QUIRK(0x1043, 0x1b13, "ASUS U41SV/GA403U", ALC285_FIXUP_ASUS_GA403U_HEADSET_MIC), SND_PCI_QUIRK(0x1043, 0x1b93, "ASUS G614JVR/JIR", ALC245_FIXUP_CS35L41_SPI_2), SND_PCI_QUIRK(0x1043, 0x1bbd, "ASUS Z550MA", ALC255_FIXUP_ASUS_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1043, 0x1c03, "ASUS UM3406HA", ALC287_FIXUP_CS35L41_I2C_2), @@ -10073,6 +10770,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1043, 0x1c33, "ASUS UX5304MA", ALC245_FIXUP_CS35L41_SPI_2), SND_PCI_QUIRK(0x1043, 0x1c43, "ASUS UX8406MA", ALC245_FIXUP_CS35L41_SPI_2), SND_PCI_QUIRK(0x1043, 0x1c62, "ASUS GU603", ALC289_FIXUP_ASUS_GA401), + SND_PCI_QUIRK(0x1043, 0x1c63, "ASUS GU605M", ALC285_FIXUP_ASUS_GU605_SPI_SPEAKER2_TO_DAC1), SND_PCI_QUIRK(0x1043, 0x1c92, "ASUS ROG Strix G15", ALC285_FIXUP_ASUS_G533Z_PINS), SND_PCI_QUIRK(0x1043, 0x1c9f, "ASUS G614JU/JV/JI", ALC285_FIXUP_ASUS_HEADSET_MIC), SND_PCI_QUIRK(0x1043, 0x1caf, "ASUS G634JY/JZ/JI/JG", ALC285_FIXUP_ASUS_SPI_REAR_SPEAKERS), @@ -10084,25 +10782,55 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1043, 0x1d42, "ASUS Zephyrus G14 2022", ALC289_FIXUP_ASUS_GA401), SND_PCI_QUIRK(0x1043, 0x1d4e, "ASUS TM420", ALC256_FIXUP_ASUS_HPE), SND_PCI_QUIRK(0x1043, 0x1da2, "ASUS UP6502ZA/ZD", ALC245_FIXUP_CS35L41_SPI_2), + SND_PCI_QUIRK(0x1043, 0x1df3, "ASUS UM5606WA", ALC294_FIXUP_BASS_SPEAKER_15), SND_PCI_QUIRK(0x1043, 0x1e02, "ASUS UX3402ZA", ALC245_FIXUP_CS35L41_SPI_2), SND_PCI_QUIRK(0x1043, 0x1e11, "ASUS Zephyrus G15", ALC289_FIXUP_ASUS_GA502), SND_PCI_QUIRK(0x1043, 0x1e12, "ASUS UM3402", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x1043, 0x1e1f, "ASUS Vivobook 15 X1504VAP", ALC2XX_FIXUP_HEADSET_MIC), SND_PCI_QUIRK(0x1043, 0x1e51, "ASUS Zephyrus M15", ALC294_FIXUP_ASUS_GU502_PINS), SND_PCI_QUIRK(0x1043, 0x1e5e, "ASUS ROG Strix G513", ALC294_FIXUP_ASUS_G513_PINS), + SND_PCI_QUIRK(0x1043, 0x1e63, "ASUS H7606W", ALC285_FIXUP_ASUS_GU605_SPI_SPEAKER2_TO_DAC1), + SND_PCI_QUIRK(0x1043, 0x1e83, "ASUS GA605W", ALC285_FIXUP_ASUS_GU605_SPI_SPEAKER2_TO_DAC1), SND_PCI_QUIRK(0x1043, 0x1e8e, "ASUS Zephyrus G15", ALC289_FIXUP_ASUS_GA401), + SND_PCI_QUIRK(0x1043, 0x1eb3, "ASUS Ally RCLA72", ALC287_FIXUP_TAS2781_I2C), + SND_PCI_QUIRK(0x1043, 0x1ed3, "ASUS HN7306W", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x1043, 0x1ee2, "ASUS UM6702RA/RC", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x1043, 0x1c52, "ASUS Zephyrus G15 2022", ALC289_FIXUP_ASUS_GA401), SND_PCI_QUIRK(0x1043, 0x1f11, "ASUS Zephyrus G14", ALC289_FIXUP_ASUS_GA401), SND_PCI_QUIRK(0x1043, 0x1f12, "ASUS UM5302", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x1043, 0x1f1f, "ASUS H7604JI/JV/J3D", ALC245_FIXUP_CS35L41_SPI_2), SND_PCI_QUIRK(0x1043, 0x1f62, "ASUS UX7602ZM", ALC245_FIXUP_CS35L41_SPI_2), + SND_PCI_QUIRK(0x1043, 0x1f63, "ASUS P5405CSA", ALC245_FIXUP_CS35L41_SPI_2), SND_PCI_QUIRK(0x1043, 0x1f92, "ASUS ROG Flow X16", ALC289_FIXUP_ASUS_GA401), + SND_PCI_QUIRK(0x1043, 0x1fb3, "ASUS ROG Flow Z13 GZ302EA", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x1043, 0x3011, "ASUS B5605CVA", ALC245_FIXUP_CS35L41_SPI_2), SND_PCI_QUIRK(0x1043, 0x3030, "ASUS ZN270IE", ALC256_FIXUP_ASUS_AIO_GPIO2), - SND_PCI_QUIRK(0x1043, 0x3a20, "ASUS G614JZR", ALC245_FIXUP_CS35L41_SPI_2), - SND_PCI_QUIRK(0x1043, 0x3a30, "ASUS G814JVR/JIR", ALC245_FIXUP_CS35L41_SPI_2), - SND_PCI_QUIRK(0x1043, 0x3a40, "ASUS G814JZR", ALC245_FIXUP_CS35L41_SPI_2), - SND_PCI_QUIRK(0x1043, 0x3a50, "ASUS G834JYR/JZR", ALC245_FIXUP_CS35L41_SPI_2), - SND_PCI_QUIRK(0x1043, 0x3a60, "ASUS G634JYR/JZR", ALC245_FIXUP_CS35L41_SPI_2), + SND_PCI_QUIRK(0x1043, 0x3061, "ASUS B3405CCA", ALC245_FIXUP_CS35L41_SPI_2), + SND_PCI_QUIRK(0x1043, 0x3071, "ASUS B5405CCA", ALC245_FIXUP_CS35L41_SPI_2), + SND_PCI_QUIRK(0x1043, 0x30c1, "ASUS B3605CCA / P3605CCA", ALC245_FIXUP_CS35L41_SPI_2), + SND_PCI_QUIRK(0x1043, 0x30d1, "ASUS B5405CCA", ALC245_FIXUP_CS35L41_SPI_2), + SND_PCI_QUIRK(0x1043, 0x30e1, "ASUS B5605CCA", ALC245_FIXUP_CS35L41_SPI_2), + SND_PCI_QUIRK(0x1043, 0x31d0, "ASUS Zen AIO 27 Z272SD_A272SD", ALC274_FIXUP_ASUS_ZEN_AIO_27), + SND_PCI_QUIRK(0x1043, 0x31e1, "ASUS B5605CCA", ALC245_FIXUP_CS35L41_SPI_2), + SND_PCI_QUIRK(0x1043, 0x31f1, "ASUS B3605CCA", ALC245_FIXUP_CS35L41_SPI_2), + SND_PCI_QUIRK(0x1043, 0x3a20, "ASUS G614JZR", ALC285_FIXUP_ASUS_SPI_REAR_SPEAKERS), + SND_PCI_QUIRK(0x1043, 0x3a30, "ASUS G814JVR/JIR", ALC285_FIXUP_ASUS_SPI_REAR_SPEAKERS), + SND_PCI_QUIRK(0x1043, 0x3a40, "ASUS G814JZR", ALC285_FIXUP_ASUS_SPI_REAR_SPEAKERS), + SND_PCI_QUIRK(0x1043, 0x3a50, "ASUS G834JYR/JZR", ALC285_FIXUP_ASUS_SPI_REAR_SPEAKERS), + SND_PCI_QUIRK(0x1043, 0x3a60, "ASUS G634JYR/JZR", ALC285_FIXUP_ASUS_SPI_REAR_SPEAKERS), + SND_PCI_QUIRK(0x1043, 0x3d78, "ASUS GA603KH", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x1043, 0x3d88, "ASUS GA603KM", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x1043, 0x3e00, "ASUS G814FH/FM/FP", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x1043, 0x3e20, "ASUS G814PH/PM/PP", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x1043, 0x3e30, "ASUS TP3607SA", ALC287_FIXUP_TAS2781_I2C), + SND_PCI_QUIRK(0x1043, 0x3ee0, "ASUS Strix G815_JHR_JMR_JPR", ALC287_FIXUP_TAS2781_I2C), + SND_PCI_QUIRK(0x1043, 0x3ef0, "ASUS Strix G635LR_LW_LX", ALC287_FIXUP_TAS2781_I2C), + SND_PCI_QUIRK(0x1043, 0x3f00, "ASUS Strix G815LH_LM_LP", ALC287_FIXUP_TAS2781_I2C), + SND_PCI_QUIRK(0x1043, 0x3f10, "ASUS Strix G835LR_LW_LX", ALC287_FIXUP_TAS2781_I2C), + SND_PCI_QUIRK(0x1043, 0x3f20, "ASUS Strix G615LR_LW", ALC287_FIXUP_TAS2781_I2C), + SND_PCI_QUIRK(0x1043, 0x3f30, "ASUS Strix G815LR_LW", ALC287_FIXUP_TAS2781_I2C), + SND_PCI_QUIRK(0x1043, 0x3fd0, "ASUS B3605CVA", ALC245_FIXUP_CS35L41_SPI_2), + SND_PCI_QUIRK(0x1043, 0x3ff0, "ASUS B5405CVA", ALC245_FIXUP_CS35L41_SPI_2), SND_PCI_QUIRK(0x1043, 0x831a, "ASUS P901", ALC269_FIXUP_STEREO_DMIC), SND_PCI_QUIRK(0x1043, 0x834a, "ASUS S101", ALC269_FIXUP_STEREO_DMIC), SND_PCI_QUIRK(0x1043, 0x8398, "ASUS P1005", ALC269_FIXUP_STEREO_DMIC), @@ -10122,35 +10850,49 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x10cf, 0x1845, "Lifebook U904", ALC269_FIXUP_LIFEBOOK_EXTMIC), SND_PCI_QUIRK(0x10ec, 0x10f2, "Intel Reference board", ALC700_FIXUP_INTEL_REFERENCE), SND_PCI_QUIRK(0x10ec, 0x118c, "Medion EE4254 MD62100", ALC256_FIXUP_MEDION_HEADSET_NO_PRESENCE), + SND_PCI_QUIRK(0x10ec, 0x119e, "Positivo SU C1400", ALC269_FIXUP_ASPIRE_HEADSET_MIC), + SND_PCI_QUIRK(0x10ec, 0x11bc, "VAIO VJFE-IL", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), SND_PCI_QUIRK(0x10ec, 0x1230, "Intel Reference board", ALC295_FIXUP_CHROME_BOOK), SND_PCI_QUIRK(0x10ec, 0x124c, "Intel Reference board", ALC295_FIXUP_CHROME_BOOK), SND_PCI_QUIRK(0x10ec, 0x1252, "Intel Reference board", ALC295_FIXUP_CHROME_BOOK), SND_PCI_QUIRK(0x10ec, 0x1254, "Intel Reference board", ALC295_FIXUP_CHROME_BOOK), SND_PCI_QUIRK(0x10ec, 0x12cc, "Intel Reference board", ALC295_FIXUP_CHROME_BOOK), - SND_PCI_QUIRK(0x10f7, 0x8338, "Panasonic CF-SZ6", ALC269_FIXUP_HEADSET_MODE), + SND_PCI_QUIRK(0x10ec, 0x12f6, "Intel Reference board", ALC295_FIXUP_CHROME_BOOK), + SND_PCI_QUIRK(0x10f7, 0x8338, "Panasonic CF-SZ6", ALC269_FIXUP_ASPIRE_HEADSET_MIC), SND_PCI_QUIRK(0x144d, 0xc109, "Samsung Ativ book 9 (NP900X3G)", ALC269_FIXUP_INV_DMIC), SND_PCI_QUIRK(0x144d, 0xc169, "Samsung Notebook 9 Pen (NP930SBE-K01US)", ALC298_FIXUP_SAMSUNG_AMP), SND_PCI_QUIRK(0x144d, 0xc176, "Samsung Notebook 9 Pro (NP930MBE-K04US)", ALC298_FIXUP_SAMSUNG_AMP), SND_PCI_QUIRK(0x144d, 0xc189, "Samsung Galaxy Flex Book (NT950QCG-X716)", ALC298_FIXUP_SAMSUNG_AMP), SND_PCI_QUIRK(0x144d, 0xc18a, "Samsung Galaxy Book Ion (NP930XCJ-K01US)", ALC298_FIXUP_SAMSUNG_AMP), SND_PCI_QUIRK(0x144d, 0xc1a3, "Samsung Galaxy Book Pro (NP935XDB-KC1SE)", ALC298_FIXUP_SAMSUNG_AMP), + SND_PCI_QUIRK(0x144d, 0xc1a4, "Samsung Galaxy Book Pro 360 (NT935QBD)", ALC298_FIXUP_SAMSUNG_AMP), SND_PCI_QUIRK(0x144d, 0xc1a6, "Samsung Galaxy Book Pro 360 (NP930QBD)", ALC298_FIXUP_SAMSUNG_AMP), SND_PCI_QUIRK(0x144d, 0xc740, "Samsung Ativ book 8 (NP870Z5G)", ALC269_FIXUP_ATIV_BOOK_8), SND_PCI_QUIRK(0x144d, 0xc812, "Samsung Notebook Pen S (NT950SBE-X58)", ALC298_FIXUP_SAMSUNG_AMP), SND_PCI_QUIRK(0x144d, 0xc830, "Samsung Galaxy Book Ion (NT950XCJ-X716A)", ALC298_FIXUP_SAMSUNG_AMP), SND_PCI_QUIRK(0x144d, 0xc832, "Samsung Galaxy Book Flex Alpha (NP730QCJ)", ALC256_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET), SND_PCI_QUIRK(0x144d, 0xca03, "Samsung Galaxy Book2 Pro 360 (NP930QED)", ALC298_FIXUP_SAMSUNG_AMP), + SND_PCI_QUIRK(0x144d, 0xca06, "Samsung Galaxy Book3 360 (NP730QFG)", ALC298_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET), SND_PCI_QUIRK(0x144d, 0xc868, "Samsung Galaxy Book2 Pro (NP930XED)", ALC298_FIXUP_SAMSUNG_AMP), + SND_PCI_QUIRK(0x144d, 0xc870, "Samsung Galaxy Book2 Pro (NP950XED)", ALC298_FIXUP_SAMSUNG_AMP_V2_2_AMPS), + SND_PCI_QUIRK(0x144d, 0xc872, "Samsung Galaxy Book2 Pro (NP950XEE)", ALC298_FIXUP_SAMSUNG_AMP_V2_2_AMPS), + SND_PCI_QUIRK(0x144d, 0xc886, "Samsung Galaxy Book3 Pro (NP964XFG)", ALC298_FIXUP_SAMSUNG_AMP_V2_4_AMPS), + SND_PCI_QUIRK(0x144d, 0xc1ca, "Samsung Galaxy Book3 Pro 360 (NP960QFG)", ALC298_FIXUP_SAMSUNG_AMP_V2_4_AMPS), + SND_PCI_QUIRK(0x144d, 0xc1cc, "Samsung Galaxy Book3 Ultra (NT960XFH)", ALC298_FIXUP_SAMSUNG_AMP_V2_4_AMPS), SND_PCI_QUIRK(0x1458, 0xfa53, "Gigabyte BXBT-2807", ALC283_FIXUP_HEADSET_MIC), SND_PCI_QUIRK(0x1462, 0xb120, "MSI Cubi MS-B120", ALC283_FIXUP_HEADSET_MIC), SND_PCI_QUIRK(0x1462, 0xb171, "Cubi N 8GL (MS-B171)", ALC283_FIXUP_HEADSET_MIC), SND_PCI_QUIRK(0x152d, 0x1082, "Quanta NL3", ALC269_FIXUP_LIFEBOOK), + SND_PCI_QUIRK(0x152d, 0x1262, "Huawei NBLB-WAX9N", ALC2XX_FIXUP_HEADSET_MIC), + SND_PCI_QUIRK(0x1558, 0x0353, "Clevo V35[05]SN[CDE]Q", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1558, 0x1323, "Clevo N130ZU", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1558, 0x1325, "Clevo N15[01][CW]U", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1558, 0x1401, "Clevo L140[CZ]U", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1558, 0x1403, "Clevo N140CU", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1558, 0x1404, "Clevo N150CU", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1558, 0x14a1, "Clevo L141MU", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x2624, "Clevo L240TU", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x28c1, "Clevo V370VND", ALC2XX_FIXUP_HEADSET_MIC), SND_PCI_QUIRK(0x1558, 0x4018, "Clevo NV40M[BE]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1558, 0x4019, "Clevo NV40MZ", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1558, 0x4020, "Clevo NV40MB", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), @@ -10212,9 +10954,12 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1558, 0x961d, "Clevo N960S[CDF]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1558, 0x971d, "Clevo N970T[CDF]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1558, 0xa500, "Clevo NL5[03]RU", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0xa554, "VAIO VJFH52", ALC269_FIXUP_VAIO_VJFH52_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1558, 0xa600, "Clevo NL50NU", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1558, 0xa650, "Clevo NP[567]0SN[CD]", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1558, 0xa671, "Clevo NP70SN[CDE]", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0xa741, "Clevo V54x_6x_TNE", ALC245_FIXUP_CLEVO_NOISY_MIC), + SND_PCI_QUIRK(0x1558, 0xa763, "Clevo V54x_6x_TU", ALC245_FIXUP_CLEVO_NOISY_MIC), SND_PCI_QUIRK(0x1558, 0xb018, "Clevo NP50D[BE]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1558, 0xb019, "Clevo NH77D[BE]Q", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1558, 0xb022, "Clevo NH77D[DC][QW]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), @@ -10248,6 +10993,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x222e, "Thinkpad", ALC298_FIXUP_TPT470_DOCK), SND_PCI_QUIRK(0x17aa, 0x2231, "Thinkpad T560", ALC292_FIXUP_TPT460), SND_PCI_QUIRK(0x17aa, 0x2233, "Thinkpad", ALC292_FIXUP_TPT460), + SND_PCI_QUIRK(0x17aa, 0x2234, "Thinkpad ICE-1", ALC287_FIXUP_TAS2781_I2C), SND_PCI_QUIRK(0x17aa, 0x2245, "Thinkpad T470", ALC298_FIXUP_TPT470_DOCK), SND_PCI_QUIRK(0x17aa, 0x2246, "Thinkpad", ALC298_FIXUP_TPT470_DOCK), SND_PCI_QUIRK(0x17aa, 0x2247, "Thinkpad", ALC298_FIXUP_TPT470_DOCK), @@ -10268,6 +11014,9 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x2318, "Thinkpad Z13 Gen2", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD), SND_PCI_QUIRK(0x17aa, 0x2319, "Thinkpad Z16 Gen2", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD), SND_PCI_QUIRK(0x17aa, 0x231a, "Thinkpad Z16 Gen2", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD), + SND_PCI_QUIRK(0x17aa, 0x231e, "Thinkpad", ALC287_FIXUP_LENOVO_THKPAD_WH_ALC1318), + SND_PCI_QUIRK(0x17aa, 0x231f, "Thinkpad", ALC287_FIXUP_LENOVO_THKPAD_WH_ALC1318), + SND_PCI_QUIRK(0x17aa, 0x2326, "Hera2", ALC287_FIXUP_TAS2781_I2C), SND_PCI_QUIRK(0x17aa, 0x30bb, "ThinkCentre AIO", ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY), SND_PCI_QUIRK(0x17aa, 0x30e2, "ThinkCentre AIO", ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY), SND_PCI_QUIRK(0x17aa, 0x310c, "ThinkCentre Station", ALC294_FIXUP_LENOVO_MIC_LOCATION), @@ -10280,11 +11029,16 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x3178, "ThinkCentre Station", ALC283_FIXUP_HEADSET_MIC), SND_PCI_QUIRK(0x17aa, 0x31af, "ThinkCentre Station", ALC623_FIXUP_LENOVO_THINKSTATION_P340), SND_PCI_QUIRK(0x17aa, 0x334b, "Lenovo ThinkCentre M70 Gen5", ALC283_FIXUP_HEADSET_MIC), + SND_PCI_QUIRK(0x17aa, 0x3384, "ThinkCentre M90a PRO", ALC233_FIXUP_LENOVO_L2MH_LOW_ENLED), + SND_PCI_QUIRK(0x17aa, 0x3386, "ThinkCentre M90a Gen6", ALC233_FIXUP_LENOVO_L2MH_LOW_ENLED), + SND_PCI_QUIRK(0x17aa, 0x3387, "ThinkCentre M70a Gen6", ALC233_FIXUP_LENOVO_L2MH_LOW_ENLED), SND_PCI_QUIRK(0x17aa, 0x3801, "Lenovo Yoga9 14IAP7", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN), - SND_PCI_QUIRK(0x17aa, 0x3802, "Lenovo Yoga Pro 9 14IRP8 / DuetITL 2021", ALC287_FIXUP_LENOVO_14IRP8_DUETITL), + HDA_CODEC_QUIRK(0x17aa, 0x3802, "DuetITL 2021", ALC287_FIXUP_YOGA7_14ITL_SPEAKERS), + SND_PCI_QUIRK(0x17aa, 0x3802, "Lenovo Yoga Pro 9 14IRP8", ALC287_FIXUP_TAS2781_I2C), SND_PCI_QUIRK(0x17aa, 0x3813, "Legion 7i 15IMHG05", ALC287_FIXUP_LEGION_15IMHG05_SPEAKERS), SND_PCI_QUIRK(0x17aa, 0x3818, "Lenovo C940 / Yoga Duet 7", ALC298_FIXUP_LENOVO_C940_DUET7), SND_PCI_QUIRK(0x17aa, 0x3819, "Lenovo 13s Gen2 ITL", ALC287_FIXUP_13S_GEN2_SPEAKERS), + HDA_CODEC_QUIRK(0x17aa, 0x3820, "IdeaPad 330-17IKB 81DM", ALC269_FIXUP_ASPIRE_HEADSET_MIC), SND_PCI_QUIRK(0x17aa, 0x3820, "Yoga Duet 7 13ITL6", ALC287_FIXUP_YOGA7_14ITL_SPEAKERS), SND_PCI_QUIRK(0x17aa, 0x3824, "Legion Y9000X 2020", ALC285_FIXUP_LEGION_Y9000X_SPEAKERS), SND_PCI_QUIRK(0x17aa, 0x3827, "Ideapad S740", ALC285_FIXUP_IDEAPAD_S740_COEF), @@ -10296,31 +11050,62 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x3852, "Lenovo Yoga 7 14ITL5", ALC287_FIXUP_YOGA7_14ITL_SPEAKERS), SND_PCI_QUIRK(0x17aa, 0x3853, "Lenovo Yoga 7 15ITL5", ALC287_FIXUP_YOGA7_14ITL_SPEAKERS), SND_PCI_QUIRK(0x17aa, 0x3855, "Legion 7 16ITHG6", ALC287_FIXUP_LEGION_16ITHG6), + SND_PCI_QUIRK(0x17aa, 0x3865, "Lenovo 13X", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x17aa, 0x3866, "Lenovo 13X", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x17aa, 0x3869, "Lenovo Yoga7 14IAL7", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN), - SND_PCI_QUIRK(0x17aa, 0x386f, "Legion 7i 16IAX7", ALC287_FIXUP_CS35L41_I2C_2), + HDA_CODEC_QUIRK(0x17aa, 0x386e, "Legion Y9000X 2022 IAH7", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x17aa, 0x386e, "Yoga Pro 7 14ARP8", ALC285_FIXUP_SPEAKER2_TO_DAC1), + HDA_CODEC_QUIRK(0x17aa, 0x38a8, "Legion Pro 7 16ARX8H", ALC287_FIXUP_TAS2781_I2C), /* this must match before PCI SSID 17aa:386f below */ + SND_PCI_QUIRK(0x17aa, 0x386f, "Legion Pro 7i 16IAX7", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x17aa, 0x3870, "Lenovo Yoga 7 14ARB7", ALC287_FIXUP_YOGA7_14ARB7_I2C), + SND_PCI_QUIRK(0x17aa, 0x3877, "Lenovo Legion 7 Slim 16ARHA7", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x17aa, 0x3878, "Lenovo Legion 7 Slim 16ARHA7", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x17aa, 0x387d, "Yoga S780-16 pro Quad AAC", ALC287_FIXUP_TAS2781_I2C), SND_PCI_QUIRK(0x17aa, 0x387e, "Yoga S780-16 pro Quad YC", ALC287_FIXUP_TAS2781_I2C), + SND_PCI_QUIRK(0x17aa, 0x387f, "Yoga S780-16 pro dual LX", ALC287_FIXUP_TAS2781_I2C), + SND_PCI_QUIRK(0x17aa, 0x3880, "Yoga S780-16 pro dual YC", ALC287_FIXUP_TAS2781_I2C), SND_PCI_QUIRK(0x17aa, 0x3881, "YB9 dual power mode2 YC", ALC287_FIXUP_TAS2781_I2C), SND_PCI_QUIRK(0x17aa, 0x3882, "Lenovo Yoga Pro 7 14APH8", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN), SND_PCI_QUIRK(0x17aa, 0x3884, "Y780 YG DUAL", ALC287_FIXUP_TAS2781_I2C), SND_PCI_QUIRK(0x17aa, 0x3886, "Y780 VECO DUAL", ALC287_FIXUP_TAS2781_I2C), + SND_PCI_QUIRK(0x17aa, 0x3891, "Lenovo Yoga Pro 7 14AHP9", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN), + SND_PCI_QUIRK(0x17aa, 0x38a5, "Y580P AMD dual", ALC287_FIXUP_TAS2781_I2C), SND_PCI_QUIRK(0x17aa, 0x38a7, "Y780P AMD YG dual", ALC287_FIXUP_TAS2781_I2C), SND_PCI_QUIRK(0x17aa, 0x38a8, "Y780P AMD VECO dual", ALC287_FIXUP_TAS2781_I2C), - SND_PCI_QUIRK(0x17aa, 0x38a9, "Thinkbook 16P", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x17aa, 0x38ab, "Thinkbook 16P", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x17aa, 0x38a9, "Thinkbook 16P", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD), + SND_PCI_QUIRK(0x17aa, 0x38ab, "Thinkbook 16P", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD), SND_PCI_QUIRK(0x17aa, 0x38b4, "Legion Slim 7 16IRH8", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x17aa, 0x38b5, "Legion Slim 7 16IRH8", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x17aa, 0x38b6, "Legion Slim 7 16APH8", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x17aa, 0x38b7, "Legion Slim 7 16APH8", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x17aa, 0x38b8, "Yoga S780-14.5 proX AMD YC Dual", ALC287_FIXUP_TAS2781_I2C), + SND_PCI_QUIRK(0x17aa, 0x38b9, "Yoga S780-14.5 proX AMD LX Dual", ALC287_FIXUP_TAS2781_I2C), SND_PCI_QUIRK(0x17aa, 0x38ba, "Yoga S780-14.5 Air AMD quad YC", ALC287_FIXUP_TAS2781_I2C), SND_PCI_QUIRK(0x17aa, 0x38bb, "Yoga S780-14.5 Air AMD quad AAC", ALC287_FIXUP_TAS2781_I2C), SND_PCI_QUIRK(0x17aa, 0x38be, "Yoga S980-14.5 proX YC Dual", ALC287_FIXUP_TAS2781_I2C), SND_PCI_QUIRK(0x17aa, 0x38bf, "Yoga S980-14.5 proX LX Dual", ALC287_FIXUP_TAS2781_I2C), SND_PCI_QUIRK(0x17aa, 0x38c3, "Y980 DUAL", ALC287_FIXUP_TAS2781_I2C), + SND_PCI_QUIRK(0x17aa, 0x38c7, "Thinkbook 13x Gen 4", ALC287_FIXUP_CS35L41_I2C_4), + SND_PCI_QUIRK(0x17aa, 0x38c8, "Thinkbook 13x Gen 4", ALC287_FIXUP_CS35L41_I2C_4), SND_PCI_QUIRK(0x17aa, 0x38cb, "Y790 YG DUAL", ALC287_FIXUP_TAS2781_I2C), SND_PCI_QUIRK(0x17aa, 0x38cd, "Y790 VECO DUAL", ALC287_FIXUP_TAS2781_I2C), + SND_PCI_QUIRK(0x17aa, 0x38d2, "Lenovo Yoga 9 14IMH9", ALC287_FIXUP_YOGA9_14IMH9_BASS_SPK_PIN), + SND_PCI_QUIRK(0x17aa, 0x38d3, "Yoga S990-16 Pro IMH YC Dual", ALC287_FIXUP_TAS2781_I2C), + SND_PCI_QUIRK(0x17aa, 0x38d4, "Yoga S990-16 Pro IMH VECO Dual", ALC287_FIXUP_TAS2781_I2C), + SND_PCI_QUIRK(0x17aa, 0x38d5, "Yoga S990-16 Pro IMH YC Quad", ALC287_FIXUP_TAS2781_I2C), + SND_PCI_QUIRK(0x17aa, 0x38d6, "Yoga S990-16 Pro IMH VECO Quad", ALC287_FIXUP_TAS2781_I2C), + SND_PCI_QUIRK(0x17aa, 0x38d7, "Lenovo Yoga 9 14IMH9", ALC287_FIXUP_YOGA9_14IMH9_BASS_SPK_PIN), + SND_PCI_QUIRK(0x17aa, 0x38df, "Yoga Y990 Intel YC Dual", ALC287_FIXUP_TAS2781_I2C), + SND_PCI_QUIRK(0x17aa, 0x38e0, "Yoga Y990 Intel VECO Dual", ALC287_FIXUP_TAS2781_I2C), + SND_PCI_QUIRK(0x17aa, 0x38f8, "Yoga Book 9i", ALC287_FIXUP_TAS2781_I2C), + SND_PCI_QUIRK(0x17aa, 0x38df, "Y990 YG DUAL", ALC287_FIXUP_TAS2781_I2C), + SND_PCI_QUIRK(0x17aa, 0x38f9, "Thinkbook 16P Gen5", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD), + SND_PCI_QUIRK(0x17aa, 0x38fa, "Thinkbook 16P Gen5", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD), + SND_PCI_QUIRK(0x17aa, 0x38fd, "ThinkBook plus Gen5 Hybrid", ALC287_FIXUP_TAS2781_I2C), SND_PCI_QUIRK(0x17aa, 0x3902, "Lenovo E50-80", ALC269_FIXUP_DMIC_THINKPAD_ACPI), + SND_PCI_QUIRK(0x17aa, 0x3913, "Lenovo 145", ALC236_FIXUP_LENOVO_INV_DMIC), + SND_PCI_QUIRK(0x17aa, 0x391f, "Yoga S990-16 pro Quad YC Quad", ALC287_FIXUP_TAS2781_I2C), + SND_PCI_QUIRK(0x17aa, 0x3920, "Yoga S990-16 pro Quad VECO Quad", ALC287_FIXUP_TAS2781_I2C), SND_PCI_QUIRK(0x17aa, 0x3977, "IdeaPad S210", ALC283_FIXUP_INT_MIC), SND_PCI_QUIRK(0x17aa, 0x3978, "Lenovo B50-70", ALC269_FIXUP_DMIC_THINKPAD_ACPI), SND_PCI_QUIRK(0x17aa, 0x3bf8, "Quanta FL1", ALC269_FIXUP_PCM_44K), @@ -10345,16 +11130,23 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x511f, "Thinkpad", ALC298_FIXUP_TPT470_DOCK), SND_PCI_QUIRK(0x17aa, 0x9e54, "LENOVO NB", ALC269_FIXUP_LENOVO_EAPD), SND_PCI_QUIRK(0x17aa, 0x9e56, "Lenovo ZhaoYang CF4620Z", ALC286_FIXUP_SONY_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1849, 0x0269, "Positivo Master C6400", ALC269VB_FIXUP_ASUS_ZENBOOK), SND_PCI_QUIRK(0x1849, 0x1233, "ASRock NUC Box 1100", ALC233_FIXUP_NO_AUDIO_JACK), SND_PCI_QUIRK(0x1849, 0xa233, "Positivo Master C6300", ALC269_FIXUP_HEADSET_MIC), + SND_PCI_QUIRK(0x1854, 0x0440, "LG CQ6", ALC256_FIXUP_HEADPHONE_AMP_VOL), + SND_PCI_QUIRK(0x1854, 0x0441, "LG CQ6 AIO", ALC256_FIXUP_HEADPHONE_AMP_VOL), + SND_PCI_QUIRK(0x1854, 0x0488, "LG gram 16 (16Z90R)", ALC298_FIXUP_SAMSUNG_AMP_V2_4_AMPS), + SND_PCI_QUIRK(0x1854, 0x048a, "LG gram 17 (17ZD90R)", ALC298_FIXUP_SAMSUNG_AMP_V2_4_AMPS), SND_PCI_QUIRK(0x19e5, 0x3204, "Huawei MACH-WX9", ALC256_FIXUP_HUAWEI_MACH_WX9_PINS), SND_PCI_QUIRK(0x19e5, 0x320f, "Huawei WRT-WX9 ", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x19e5, 0x3212, "Huawei KLV-WX9 ", ALC256_FIXUP_ACER_HEADSET_MIC), SND_PCI_QUIRK(0x1b35, 0x1235, "CZC B20", ALC269_FIXUP_CZC_B20), SND_PCI_QUIRK(0x1b35, 0x1236, "CZC TMI", ALC269_FIXUP_CZC_TMI), SND_PCI_QUIRK(0x1b35, 0x1237, "CZC L101", ALC269_FIXUP_CZC_L101), SND_PCI_QUIRK(0x1b7d, 0xa831, "Ordissimo EVE2 ", ALC269VB_FIXUP_ORDISSIMO_EVE2), /* Also known as Malata PC-B1303 */ SND_PCI_QUIRK(0x1c06, 0x2013, "Lemote A1802", ALC269_FIXUP_LEMOTE_A1802), SND_PCI_QUIRK(0x1c06, 0x2015, "Lemote A190X", ALC269_FIXUP_LEMOTE_A190X), + SND_PCI_QUIRK(0x1c6c, 0x122a, "Positivo N14AP7", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), SND_PCI_QUIRK(0x1c6c, 0x1251, "Positivo N14KP6-TG", ALC288_FIXUP_DELL1_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1d05, 0x1132, "TongFang PHxTxX1", ALC256_FIXUP_SET_COEF_DEFAULTS), SND_PCI_QUIRK(0x1d05, 0x1096, "TongFang GMxMRxx", ALC269_FIXUP_NO_SHUTUP), @@ -10365,20 +11157,31 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1d05, 0x1147, "TongFang GMxTGxx", ALC269_FIXUP_NO_SHUTUP), SND_PCI_QUIRK(0x1d05, 0x115c, "TongFang GMxTGxx", ALC269_FIXUP_NO_SHUTUP), SND_PCI_QUIRK(0x1d05, 0x121b, "TongFang GMxAGxx", ALC269_FIXUP_NO_SHUTUP), + SND_PCI_QUIRK(0x1d05, 0x1387, "TongFang GMxIXxx", ALC2XX_FIXUP_HEADSET_MIC), + SND_PCI_QUIRK(0x1d05, 0x1409, "TongFang GMxIXxx", ALC2XX_FIXUP_HEADSET_MIC), + SND_PCI_QUIRK(0x1d17, 0x3288, "Haier Boyue G42", ALC269VC_FIXUP_ACER_VCOPPERBOX_PINS), SND_PCI_QUIRK(0x1d72, 0x1602, "RedmiBook", ALC255_FIXUP_XIAOMI_HEADSET_MIC), SND_PCI_QUIRK(0x1d72, 0x1701, "XiaomiNotebook Pro", ALC298_FIXUP_DELL1_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1d72, 0x1901, "RedmiBook 14", ALC256_FIXUP_ASUS_HEADSET_MIC), SND_PCI_QUIRK(0x1d72, 0x1945, "Redmi G", ALC256_FIXUP_ASUS_HEADSET_MIC), SND_PCI_QUIRK(0x1d72, 0x1947, "RedmiBook Air", ALC255_FIXUP_XIAOMI_HEADSET_MIC), + SND_PCI_QUIRK(0x1f66, 0x0105, "Ayaneo Portable Game Player", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x2014, 0x800a, "Positivo ARN50", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), + SND_PCI_QUIRK(0x2782, 0x0214, "VAIO VJFE-CL", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), + SND_PCI_QUIRK(0x2782, 0x0228, "Infinix ZERO BOOK 13", ALC269VB_FIXUP_INFINIX_ZERO_BOOK_13), SND_PCI_QUIRK(0x2782, 0x0232, "CHUWI CoreBook XPro", ALC269VB_FIXUP_CHUWI_COREBOOK_XPRO), + SND_PCI_QUIRK(0x2782, 0x1701, "Infinix Y4 Max", ALC269VC_FIXUP_INFINIX_Y4_MAX), + SND_PCI_QUIRK(0x2782, 0x1705, "MEDION E15433", ALC269VC_FIXUP_INFINIX_Y4_MAX), SND_PCI_QUIRK(0x2782, 0x1707, "Vaio VJFE-ADL", ALC298_FIXUP_SPK_VOLUME), + SND_PCI_QUIRK(0x2782, 0x4900, "MEDION E15443", ALC233_FIXUP_MEDION_MTL_SPK), SND_PCI_QUIRK(0x8086, 0x2074, "Intel NUC 8", ALC233_FIXUP_INTEL_NUC8_DMIC), SND_PCI_QUIRK(0x8086, 0x2080, "Intel NUC 8 Rugged", ALC256_FIXUP_INTEL_NUC8_RUGGED), SND_PCI_QUIRK(0x8086, 0x2081, "Intel NUC 10", ALC256_FIXUP_INTEL_NUC10), SND_PCI_QUIRK(0x8086, 0x3038, "Intel NUC 13", ALC295_FIXUP_CHROME_BOOK), SND_PCI_QUIRK(0xf111, 0x0001, "Framework Laptop", ALC295_FIXUP_FRAMEWORK_LAPTOP_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0xf111, 0x0005, "Framework Laptop", ALC295_FIXUP_FRAMEWORK_LAPTOP_MIC_NO_PRESENCE), SND_PCI_QUIRK(0xf111, 0x0006, "Framework Laptop", ALC295_FIXUP_FRAMEWORK_LAPTOP_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0xf111, 0x0009, "Framework Laptop", ALC295_FIXUP_FRAMEWORK_LAPTOP_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0xf111, 0x000c, "Framework Laptop", ALC295_FIXUP_FRAMEWORK_LAPTOP_MIC_NO_PRESENCE), #if 0 /* Below is a quirk table taken from the old code. @@ -10431,11 +11234,11 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { {} }; -static const struct snd_pci_quirk alc269_fixup_vendor_tbl[] = { +static const struct hda_quirk alc269_fixup_vendor_tbl[] = { SND_PCI_QUIRK_VENDOR(0x1025, "Acer Aspire", ALC271_FIXUP_DMIC), SND_PCI_QUIRK_VENDOR(0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED), SND_PCI_QUIRK_VENDOR(0x104d, "Sony VAIO", ALC269_FIXUP_SONY_VAIO), - SND_PCI_QUIRK_VENDOR(0x17aa, "Thinkpad", ALC269_FIXUP_THINKPAD_ACPI), + SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo XPAD", ALC269_FIXUP_LENOVO_XPAD_ACPI), SND_PCI_QUIRK_VENDOR(0x19e5, "Huawei Matebook", ALC255_FIXUP_MIC_MUTE_LED), {} }; @@ -10457,6 +11260,7 @@ static const struct hda_model_fixup alc269_fixup_models[] = { {.id = ALC269_FIXUP_DELL2_MIC_NO_PRESENCE, .name = "dell-headset-dock"}, {.id = ALC269_FIXUP_DELL3_MIC_NO_PRESENCE, .name = "dell-headset3"}, {.id = ALC269_FIXUP_DELL4_MIC_NO_PRESENCE, .name = "dell-headset4"}, + {.id = ALC269_FIXUP_DELL4_MIC_NO_PRESENCE_QUIET, .name = "dell-headset4-quiet"}, {.id = ALC283_FIXUP_CHROME_BOOK, .name = "alc283-dac-wcaps"}, {.id = ALC283_FIXUP_SENSE_COMBO_JACK, .name = "alc283-sense-combo"}, {.id = ALC292_FIXUP_TPT440_DOCK, .name = "tpt440-dock"}, @@ -10499,6 +11303,7 @@ static const struct hda_model_fixup alc269_fixup_models[] = { {.id = ALC290_FIXUP_MONO_SPEAKERS_HSJACK, .name = "mono-speakers"}, {.id = ALC290_FIXUP_SUBWOOFER_HSJACK, .name = "alc290-subwoofer"}, {.id = ALC269_FIXUP_THINKPAD_ACPI, .name = "thinkpad"}, + {.id = ALC269_FIXUP_LENOVO_XPAD_ACPI, .name = "lenovo-xpad-led"}, {.id = ALC269_FIXUP_DMIC_THINKPAD_ACPI, .name = "dmic-thinkpad"}, {.id = ALC255_FIXUP_ACER_MIC_NO_PRESENCE, .name = "alc255-acer"}, {.id = ALC255_FIXUP_ASUS_MIC_NO_PRESENCE, .name = "alc255-asus"}, @@ -10548,10 +11353,13 @@ static const struct hda_model_fixup alc269_fixup_models[] = { {.id = ALC295_FIXUP_HP_X360, .name = "alc295-hp-x360"}, {.id = ALC225_FIXUP_HEADSET_JACK, .name = "alc-headset-jack"}, {.id = ALC295_FIXUP_CHROME_BOOK, .name = "alc-chrome-book"}, + {.id = ALC256_FIXUP_CHROME_BOOK, .name = "alc-2024y-chromebook"}, {.id = ALC299_FIXUP_PREDATOR_SPK, .name = "predator-spk"}, {.id = ALC298_FIXUP_HUAWEI_MBX_STEREO, .name = "huawei-mbx-stereo"}, {.id = ALC256_FIXUP_MEDION_HEADSET_NO_PRESENCE, .name = "alc256-medion-headset"}, {.id = ALC298_FIXUP_SAMSUNG_AMP, .name = "alc298-samsung-amp"}, + {.id = ALC298_FIXUP_SAMSUNG_AMP_V2_2_AMPS, .name = "alc298-samsung-amp-v2-2-amps"}, + {.id = ALC298_FIXUP_SAMSUNG_AMP_V2_4_AMPS, .name = "alc298-samsung-amp-v2-4-amps"}, {.id = ALC256_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET, .name = "alc256-samsung-headphone"}, {.id = ALC255_FIXUP_XIAOMI_HEADSET_MIC, .name = "alc255-xiaomi-headset"}, {.id = ALC274_FIXUP_HP_MIC, .name = "alc274-hp-mic-detect"}, @@ -10559,11 +11367,14 @@ static const struct hda_model_fixup alc269_fixup_models[] = { {.id = ALC295_FIXUP_HP_OMEN, .name = "alc295-hp-omen"}, {.id = ALC285_FIXUP_HP_SPECTRE_X360, .name = "alc285-hp-spectre-x360"}, {.id = ALC285_FIXUP_HP_SPECTRE_X360_EB1, .name = "alc285-hp-spectre-x360-eb1"}, + {.id = ALC285_FIXUP_HP_ENVY_X360, .name = "alc285-hp-envy-x360"}, {.id = ALC287_FIXUP_IDEAPAD_BASS_SPK_AMP, .name = "alc287-ideapad-bass-spk-amp"}, {.id = ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN, .name = "alc287-yoga9-bass-spk-pin"}, {.id = ALC623_FIXUP_LENOVO_THINKSTATION_P340, .name = "alc623-lenovo-thinkstation-p340"}, {.id = ALC255_FIXUP_ACER_HEADPHONE_AND_MIC, .name = "alc255-acer-headphone-and-mic"}, {.id = ALC285_FIXUP_HP_GPIO_AMP_INIT, .name = "alc285-hp-amp-init"}, + {.id = ALC236_FIXUP_LENOVO_INV_DMIC, .name = "alc236-fixup-lenovo-inv-mic"}, + {.id = ALC2XX_FIXUP_HEADSET_MIC, .name = "alc2xx-fixup-headset-mic"}, {} }; #define ALC225_STANDARD_PINS \ @@ -11006,20 +11817,22 @@ static const struct snd_hda_pin_quirk alc269_fallback_pin_fixup_tbl[] = { SND_HDA_PIN_QUIRK(0x10ec0289, 0x1028, "Dell", ALC269_FIXUP_DELL4_MIC_NO_PRESENCE, {0x19, 0x40000000}, {0x1b, 0x40000000}), - SND_HDA_PIN_QUIRK(0x10ec0295, 0x1028, "Dell", ALC269_FIXUP_DELL4_MIC_NO_PRESENCE, + SND_HDA_PIN_QUIRK(0x10ec0295, 0x1028, "Dell", ALC269_FIXUP_DELL4_MIC_NO_PRESENCE_QUIET, {0x19, 0x40000000}, {0x1b, 0x40000000}), SND_HDA_PIN_QUIRK(0x10ec0256, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, {0x19, 0x40000000}, {0x1a, 0x40000000}), - SND_HDA_PIN_QUIRK(0x10ec0236, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, + SND_HDA_PIN_QUIRK(0x10ec0236, 0x1028, "Dell", ALC255_FIXUP_DELL1_LIMIT_INT_MIC_BOOST, {0x19, 0x40000000}, {0x1a, 0x40000000}), - SND_HDA_PIN_QUIRK(0x10ec0274, 0x1028, "Dell", ALC274_FIXUP_DELL_AIO_LINEOUT_VERB, + SND_HDA_PIN_QUIRK(0x10ec0274, 0x1028, "Dell", ALC269_FIXUP_DELL1_LIMIT_INT_MIC_BOOST, {0x19, 0x40000000}, {0x1a, 0x40000000}), SND_HDA_PIN_QUIRK(0x10ec0256, 0x1043, "ASUS", ALC2XX_FIXUP_HEADSET_MIC, {0x19, 0x40000000}), + SND_HDA_PIN_QUIRK(0x10ec0255, 0x1558, "Clevo", ALC2XX_FIXUP_HEADSET_MIC, + {0x19, 0x40000000}), {} }; @@ -11079,10 +11892,8 @@ static int patch_alc269(struct hda_codec *codec) codec->power_save_node = 0; spec->en_3kpull_low = true; -#ifdef CONFIG_PM codec->patch_ops.suspend = alc269_suspend; codec->patch_ops.resume = alc269_resume; -#endif spec->shutup = alc_default_shutup; spec->init_hook = alc_default_init; @@ -11209,8 +12020,11 @@ static int patch_alc269(struct hda_codec *codec) spec->codec_variant = ALC269_TYPE_ALC300; spec->gen.mixer_nid = 0; /* no loopback on ALC300 */ break; + case 0x10ec0222: case 0x10ec0623: spec->codec_variant = ALC269_TYPE_ALC623; + spec->shutup = alc222_shutup; + spec->init_hook = alc222_init; break; case 0x10ec0700: case 0x10ec0701: @@ -11247,6 +12061,13 @@ static int patch_alc269(struct hda_codec *codec) snd_hda_pick_pin_fixup(codec, alc269_fallback_pin_fixup_tbl, alc269_fixups, false); snd_hda_pick_fixup(codec, NULL, alc269_fixup_vendor_tbl, alc269_fixups); + + /* + * Check whether ACPI describes companion amplifiers that require + * component binding + */ + find_cirrus_companion_amps(codec); + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); alc_auto_parse_customize_define(codec); @@ -11354,7 +12175,7 @@ static const struct hda_fixup alc861_fixups[] = { } }; -static const struct snd_pci_quirk alc861_fixup_tbl[] = { +static const struct hda_quirk alc861_fixup_tbl[] = { SND_PCI_QUIRK(0x1043, 0x1253, "ASUS W7J", ALC660_FIXUP_ASUS_W7J), SND_PCI_QUIRK(0x1043, 0x1263, "ASUS Z35HL", ALC660_FIXUP_ASUS_W7J), SND_PCI_QUIRK(0x1043, 0x1393, "ASUS A6Rp", ALC861_FIXUP_ASUS_A6RP), @@ -11380,9 +12201,7 @@ static int patch_alc861(struct hda_codec *codec) if (has_cdefine_beep(codec)) spec->gen.beep_nid = 0x23; -#ifdef CONFIG_PM spec->power_hook = alc_power_eapd; -#endif alc_pre_init(codec); @@ -11460,7 +12279,7 @@ static const struct hda_fixup alc861vd_fixups[] = { }, }; -static const struct snd_pci_quirk alc861vd_fixup_tbl[] = { +static const struct hda_quirk alc861vd_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x30bf, "HP TX1000", ALC861VD_FIX_DALLAS), SND_PCI_QUIRK(0x1043, 0x1339, "ASUS A7-K", ALC660VD_FIX_ASUS_GPIO1), SND_PCI_QUIRK(0x1179, 0xff31, "Toshiba L30-149", ALC861VD_FIX_DALLAS), @@ -11805,6 +12624,7 @@ enum { ALC897_FIXUP_LENOVO_HEADSET_MODE, ALC897_FIXUP_HEADSET_MIC_PIN2, ALC897_FIXUP_UNIS_H3C_X500S, + ALC897_FIXUP_HEADSET_MIC_PIN3, }; static const struct hda_fixup alc662_fixups[] = { @@ -12251,10 +13071,18 @@ static const struct hda_fixup alc662_fixups[] = { {} }, }, + [ALC897_FIXUP_HEADSET_MIC_PIN3] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x19, 0x03a11050 }, /* use as headset mic */ + { } + }, + }, }; -static const struct snd_pci_quirk alc662_fixup_tbl[] = { +static const struct hda_quirk alc662_fixup_tbl[] = { SND_PCI_QUIRK(0x1019, 0x9087, "ECS", ALC662_FIXUP_ASUS_MODE2), + SND_PCI_QUIRK(0x1019, 0x9859, "JP-IK LEAP W502", ALC897_FIXUP_HEADSET_MIC_PIN3), SND_PCI_QUIRK(0x1025, 0x022f, "Acer Aspire One", ALC662_FIXUP_INV_DMIC), SND_PCI_QUIRK(0x1025, 0x0241, "Packard Bell DOTS", ALC662_FIXUP_INV_DMIC), SND_PCI_QUIRK(0x1025, 0x0308, "Acer Aspire 8942G", ALC662_FIXUP_ASPIRE), @@ -12674,6 +13502,7 @@ MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_realtek); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Realtek HD-audio codec"); +MODULE_IMPORT_NS("SND_HDA_SCODEC_COMPONENT"); static struct hda_codec_driver realtek_driver = { .id = snd_hda_id_realtek, diff --git a/sound/pci/hda/patch_senarytech.c b/sound/pci/hda/patch_senarytech.c new file mode 100644 index 000000000000..0691996fa971 --- /dev/null +++ b/sound/pci/hda/patch_senarytech.c @@ -0,0 +1,244 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * HD audio interface patch for Senary HDA audio codec + * + * Initially based on sound/pci/hda/patch_conexant.c + */ + +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <sound/core.h> +#include <sound/jack.h> + +#include <sound/hda_codec.h> +#include "hda_local.h" +#include "hda_auto_parser.h" +#include "hda_beep.h" +#include "hda_jack.h" +#include "hda_generic.h" + +struct senary_spec { + struct hda_gen_spec gen; + + /* extra EAPD pins */ + unsigned int num_eapds; + hda_nid_t eapds[4]; + hda_nid_t mute_led_eapd; + + unsigned int parse_flags; /* flag for snd_hda_parse_pin_defcfg() */ + + int mute_led_polarity; + unsigned int gpio_led; + unsigned int gpio_mute_led_mask; + unsigned int gpio_mic_led_mask; +}; + +#ifdef CONFIG_SND_HDA_INPUT_BEEP +/* additional beep mixers; private_value will be overwritten */ +static const struct snd_kcontrol_new senary_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), +}; + +static int set_beep_amp(struct senary_spec *spec, hda_nid_t nid, + int idx, int dir) +{ + struct snd_kcontrol_new *knew; + unsigned int beep_amp = HDA_COMPOSE_AMP_VAL(nid, 1, idx, dir); + int i; + + spec->gen.beep_nid = nid; + for (i = 0; i < ARRAY_SIZE(senary_beep_mixer); i++) { + knew = snd_hda_gen_add_kctl(&spec->gen, NULL, + &senary_beep_mixer[i]); + if (!knew) + return -ENOMEM; + knew->private_value = beep_amp; + } + return 0; +} + +static int senary_auto_parse_beep(struct hda_codec *codec) +{ + struct senary_spec *spec = codec->spec; + hda_nid_t nid; + + for_each_hda_codec_node(nid, codec) + if ((get_wcaps_type(get_wcaps(codec, nid)) == AC_WID_BEEP) && + (get_wcaps(codec, nid) & (AC_WCAP_OUT_AMP | AC_WCAP_AMP_OVRD))) + return set_beep_amp(spec, nid, 0, HDA_OUTPUT); + return 0; +} +#else +#define senary_auto_parse_beep(codec) 0 +#endif + +/* parse EAPDs */ +static void senary_auto_parse_eapd(struct hda_codec *codec) +{ + struct senary_spec *spec = codec->spec; + hda_nid_t nid; + + for_each_hda_codec_node(nid, codec) { + if (get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_PIN) + continue; + if (!(snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_EAPD)) + continue; + spec->eapds[spec->num_eapds++] = nid; + if (spec->num_eapds >= ARRAY_SIZE(spec->eapds)) + break; + } +} + +static void senary_auto_turn_eapd(struct hda_codec *codec, int num_pins, + const hda_nid_t *pins, bool on) +{ + int i; + + for (i = 0; i < num_pins; i++) { + if (snd_hda_query_pin_caps(codec, pins[i]) & AC_PINCAP_EAPD) + snd_hda_codec_write(codec, pins[i], 0, + AC_VERB_SET_EAPD_BTLENABLE, + on ? 0x02 : 0); + } +} + +/* turn on/off EAPD according to Master switch */ +static void senary_auto_vmaster_hook(void *private_data, int enabled) +{ + struct hda_codec *codec = private_data; + struct senary_spec *spec = codec->spec; + + senary_auto_turn_eapd(codec, spec->num_eapds, spec->eapds, enabled); +} + +static void senary_init_gpio_led(struct hda_codec *codec) +{ + struct senary_spec *spec = codec->spec; + unsigned int mask = spec->gpio_mute_led_mask | spec->gpio_mic_led_mask; + + if (mask) { + snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_MASK, + mask); + snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DIRECTION, + mask); + snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, + spec->gpio_led); + } +} + +static int senary_auto_init(struct hda_codec *codec) +{ + snd_hda_gen_init(codec); + senary_init_gpio_led(codec); + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_INIT); + + return 0; +} + +static void senary_auto_shutdown(struct hda_codec *codec) +{ + struct senary_spec *spec = codec->spec; + + /* Turn the problematic codec into D3 to avoid spurious noises + * from the internal speaker during (and after) reboot + */ + senary_auto_turn_eapd(codec, spec->num_eapds, spec->eapds, false); +} + +static void senary_auto_free(struct hda_codec *codec) +{ + senary_auto_shutdown(codec); + snd_hda_gen_free(codec); +} + +static int senary_auto_suspend(struct hda_codec *codec) +{ + senary_auto_shutdown(codec); + return 0; +} + +static const struct hda_codec_ops senary_auto_patch_ops = { + .build_controls = snd_hda_gen_build_controls, + .build_pcms = snd_hda_gen_build_pcms, + .init = senary_auto_init, + .free = senary_auto_free, + .unsol_event = snd_hda_jack_unsol_event, + .suspend = senary_auto_suspend, + .check_power_status = snd_hda_gen_check_power_status, +}; + +static int patch_senary_auto(struct hda_codec *codec) +{ + struct senary_spec *spec; + int err; + + codec_info(codec, "%s: BIOS auto-probing.\n", codec->core.chip_name); + + spec = kzalloc(sizeof(*spec), GFP_KERNEL); + if (!spec) + return -ENOMEM; + snd_hda_gen_spec_init(&spec->gen); + codec->spec = spec; + codec->patch_ops = senary_auto_patch_ops; + + senary_auto_parse_eapd(codec); + spec->gen.own_eapd_ctl = 1; + + if (!spec->gen.vmaster_mute.hook) + spec->gen.vmaster_mute.hook = senary_auto_vmaster_hook; + + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); + + err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL, + spec->parse_flags); + if (err < 0) + goto error; + + err = senary_auto_parse_beep(codec); + if (err < 0) + goto error; + + err = snd_hda_gen_parse_auto_config(codec, &spec->gen.autocfg); + if (err < 0) + goto error; + + /* Some laptops with Senary chips show stalls in S3 resume, + * which falls into the single-cmd mode. + * Better to make reset, then. + */ + if (!codec->bus->core.sync_write) { + codec_info(codec, + "Enable sync_write for stable communication\n"); + codec->bus->core.sync_write = 1; + codec->bus->allow_bus_reset = 1; + } + + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); + + return 0; + + error: + senary_auto_free(codec); + return err; +} + +/* + */ + +static const struct hda_device_id snd_hda_id_senary[] = { + HDA_CODEC_ENTRY(0x1fa86186, "SN6186", patch_senary_auto), + {} /* terminator */ +}; +MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_senary); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Senarytech HD-audio codec"); + +static struct hda_codec_driver senary_driver = { + .id = snd_hda_id_senary, +}; + +module_hda_codec_driver(senary_driver); diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 61258b0aac8d..bde6b7373858 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -1462,7 +1462,7 @@ static const struct hda_model_fixup stac9200_models[] = { {} }; -static const struct snd_pci_quirk stac9200_fixup_tbl[] = { +static const struct hda_quirk stac9200_fixup_tbl[] = { /* SigmaTel reference board */ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668, "DFI LanParty", STAC_REF), @@ -1683,7 +1683,7 @@ static const struct hda_model_fixup stac925x_models[] = { {} }; -static const struct snd_pci_quirk stac925x_fixup_tbl[] = { +static const struct hda_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), @@ -1957,7 +1957,7 @@ static const struct hda_model_fixup stac92hd73xx_models[] = { {} }; -static const struct snd_pci_quirk stac92hd73xx_fixup_tbl[] = { +static const struct hda_quirk stac92hd73xx_fixup_tbl[] = { /* SigmaTel reference board */ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668, "DFI LanParty", STAC_92HD73XX_REF), @@ -2154,10 +2154,8 @@ static void stac92hd83xxx_fixup_hp_mic_led(struct hda_codec *codec, if (action == HDA_FIXUP_ACT_PRE_PROBE) { spec->mic_mute_led_gpio = 0x08; /* GPIO3 */ -#ifdef CONFIG_PM /* resetting controller clears GPIO, so we need to keep on */ codec->core.power_caps &= ~AC_PWRST_CLKSTOP; -#endif } } @@ -2755,7 +2753,7 @@ static const struct hda_model_fixup stac92hd83xxx_models[] = { {} }; -static const struct snd_pci_quirk stac92hd83xxx_fixup_tbl[] = { +static const struct hda_quirk stac92hd83xxx_fixup_tbl[] = { /* SigmaTel reference board */ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668, "DFI LanParty", STAC_92HD83XXX_REF), @@ -3238,7 +3236,7 @@ static const struct hda_model_fixup stac92hd71bxx_models[] = { {} }; -static const struct snd_pci_quirk stac92hd71bxx_fixup_tbl[] = { +static const struct hda_quirk stac92hd71bxx_fixup_tbl[] = { /* SigmaTel reference board */ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668, "DFI LanParty", STAC_92HD71BXX_REF), @@ -3498,7 +3496,7 @@ static const struct hda_pintbl ecs202_pin_configs[] = { }; /* codec SSIDs for Intel Mac sharing the same PCI SSID 8384:7680 */ -static const struct snd_pci_quirk stac922x_intel_mac_fixup_tbl[] = { +static const struct hda_quirk stac922x_intel_mac_fixup_tbl[] = { SND_PCI_QUIRK(0x0000, 0x0100, "Mac Mini", STAC_INTEL_MAC_V3), SND_PCI_QUIRK(0x106b, 0x0800, "Mac", STAC_INTEL_MAC_V1), SND_PCI_QUIRK(0x106b, 0x0600, "Mac", STAC_INTEL_MAC_V2), @@ -3642,7 +3640,7 @@ static const struct hda_model_fixup stac922x_models[] = { {} }; -static const struct snd_pci_quirk stac922x_fixup_tbl[] = { +static const struct hda_quirk stac922x_fixup_tbl[] = { /* SigmaTel reference board */ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668, "DFI LanParty", STAC_D945_REF), @@ -3970,7 +3968,7 @@ static const struct hda_model_fixup stac927x_models[] = { {} }; -static const struct snd_pci_quirk stac927x_fixup_tbl[] = { +static const struct hda_quirk stac927x_fixup_tbl[] = { /* SigmaTel reference board */ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668, "DFI LanParty", STAC_D965_REF), @@ -4180,7 +4178,7 @@ static const struct hda_model_fixup stac9205_models[] = { {} }; -static const struct snd_pci_quirk stac9205_fixup_tbl[] = { +static const struct hda_quirk stac9205_fixup_tbl[] = { /* SigmaTel reference board */ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668, "DFI LanParty", STAC_9205_REF), @@ -4257,7 +4255,7 @@ static const struct hda_fixup stac92hd95_fixups[] = { }, }; -static const struct snd_pci_quirk stac92hd95_fixup_tbl[] = { +static const struct hda_quirk stac92hd95_fixup_tbl[] = { SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1911, "HP Spectre 13", STAC_92HD95_HP_BASS), {} /* terminator */ }; @@ -4442,7 +4440,6 @@ static void stac927x_proc_hook(struct snd_info_buffer *buffer, #define stac927x_proc_hook NULL #endif -#ifdef CONFIG_PM static int stac_suspend(struct hda_codec *codec) { struct sigmatel_spec *spec = codec->spec; @@ -4456,9 +4453,6 @@ static int stac_suspend(struct hda_codec *codec) return 0; } -#else -#define stac_suspend NULL -#endif /* CONFIG_PM */ static const struct hda_codec_ops stac_patch_ops = { .build_controls = snd_hda_gen_build_controls, @@ -4466,9 +4460,7 @@ static const struct hda_codec_ops stac_patch_ops = { .init = stac_init, .free = stac_free, .unsol_event = snd_hda_jack_unsol_event, -#ifdef CONFIG_PM .suspend = stac_suspend, -#endif }; static int alloc_stac_spec(struct hda_codec *codec) @@ -5010,7 +5002,7 @@ static const struct hda_fixup stac9872_fixups[] = { }, }; -static const struct snd_pci_quirk stac9872_fixup_tbl[] = { +static const struct hda_quirk stac9872_fixup_tbl[] = { SND_PCI_QUIRK_MASK(0x104d, 0xfff0, 0x81e0, "Sony VAIO F/S", STAC_9872_VAIO), {} /* terminator */ diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 2994f85bc1b9..d0893059b1b9 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -379,7 +379,6 @@ static void via_free(struct hda_codec *codec) snd_hda_gen_free(codec); } -#ifdef CONFIG_PM static int via_suspend(struct hda_codec *codec) { struct via_spec *spec = codec->spec; @@ -400,9 +399,7 @@ static int via_resume(struct hda_codec *codec) snd_hda_regmap_sync(codec); 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; @@ -410,7 +407,6 @@ static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid) vt1708_update_hp_work(codec); return snd_hda_check_amp_list_power(codec, &spec->gen.loopback, nid); } -#endif /* */ @@ -423,11 +419,9 @@ static const struct hda_codec_ops via_patch_ops = { .init = via_init, .free = via_free, .unsol_event = snd_hda_jack_unsol_event, -#ifdef CONFIG_PM .suspend = via_suspend, .resume = via_resume, .check_power_status = via_check_power_status, -#endif }; @@ -1041,7 +1035,7 @@ static const struct hda_fixup via_fixups[] = { }, }; -static const struct snd_pci_quirk vt2002p_fixups[] = { +static const struct hda_quirk vt2002p_fixups[] = { SND_PCI_QUIRK(0x1043, 0x13f7, "Asus B23E", VIA_FIXUP_POWER_SAVE), SND_PCI_QUIRK(0x1043, 0x1487, "Asus G75", VIA_FIXUP_ASUS_G75), SND_PCI_QUIRK(0x1043, 0x8532, "Asus X202E", VIA_FIXUP_INTMIC_BOOST), diff --git a/sound/pci/hda/tas2781-spi.h b/sound/pci/hda/tas2781-spi.h new file mode 100644 index 000000000000..ecfc3c8bb821 --- /dev/null +++ b/sound/pci/hda/tas2781-spi.h @@ -0,0 +1,158 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +// +// ALSA SoC Texas Instruments TAS2781 Audio Smart Amplifier +// +// Copyright (C) 2024 Texas Instruments Incorporated +// https://www.ti.com +// +// The TAS2781 driver implements a flexible and configurable +// algo coefficient setting for TAS2781 chips. +// +// Author: Baojun Xu <baojun.xu@ti.com> +// + +#ifndef __TAS2781_SPI_H__ +#define __TAS2781_SPI_H__ + +#define TASDEVICE_RATES \ + (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | \ + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_88200) + +#define TASDEVICE_FORMATS \ + (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) + +#define TASDEVICE_MAX_BOOK_NUM 256 +#define TASDEVICE_MAX_PAGE 256 + +#define TASDEVICE_MAX_SIZE (TASDEVICE_MAX_BOOK_NUM * TASDEVICE_MAX_PAGE) + +/* PAGE Control Register (available in page0 of each book) */ +#define TASDEVICE_PAGE_SELECT 0x00 +#define TASDEVICE_BOOKCTL_PAGE 0x00 +#define TASDEVICE_BOOKCTL_REG GENMASK(7, 1) +#define TASDEVICE_BOOK_ID(reg) (((reg) & GENMASK(24, 16)) >> 16) +#define TASDEVICE_PAGE_ID(reg) (((reg) & GENMASK(15, 8)) >> 8) +#define TASDEVICE_REG_ID(reg) (((reg) & GENMASK(7, 1)) >> 1) +#define TASDEVICE_PAGE_REG(reg) ((reg) & GENMASK(15, 1)) +#define TASDEVICE_REG(book, page, reg) \ + (((book) << 16) | ((page) << 8) | ((reg) << 1)) + +/* Software Reset */ +#define TAS2781_REG_SWRESET TASDEVICE_REG(0x0, 0x0, 0x01) +#define TAS2781_REG_SWRESET_RESET BIT(0) + +/* System Reset Check Register */ +#define TAS2781_REG_CLK_CONFIG TASDEVICE_REG(0x0, 0x0, 0x5c) +#define TAS2781_REG_CLK_CONFIG_RESET (0x19) +#define TAS2781_PRE_POST_RESET_CFG 3 + +/* Block Checksum */ +#define TASDEVICE_CHECKSUM TASDEVICE_REG(0x0, 0x0, 0x7e) + +/* Volume control */ +#define TAS2781_DVC_LVL TASDEVICE_REG(0x0, 0x0, 0x1a) +#define TAS2781_AMP_LEVEL TASDEVICE_REG(0x0, 0x0, 0x03) +#define TAS2781_AMP_LEVEL_MASK GENMASK(5, 1) + +#define TASDEVICE_CMD_SING_W 0x1 +#define TASDEVICE_CMD_BURST 0x2 +#define TASDEVICE_CMD_DELAY 0x3 +#define TASDEVICE_CMD_FIELD_W 0x4 + +#define TAS2781_SPI_MAX_FREQ (4 * HZ_PER_MHZ) + +#define TASDEVICE_CRC8_POLYNOMIAL 0x4d +#define TASDEVICE_SPEAKER_CALIBRATION_SIZE 20 + +/* Flag of calibration registers address. */ +#define TASDEVICE_CALIBRATION_REG_ADDRESS BIT(7) + +#define TASDEVICE_CALIBRATION_DATA_NAME L"CALI_DATA" +#define TASDEVICE_CALIBRATION_DATA_SIZE 256 + +enum calib_data { + R0_VAL = 0, + INV_R0, + R0LOW, + POWER, + TLIM, + CALIB_MAX +}; + +struct tasdevice_priv { + struct tasdevice_fw *cali_data_fmw; + struct tasdevice_rca rcabin; + struct tasdevice_fw *fmw; + struct gpio_desc *reset; + struct mutex codec_lock; + struct regmap *regmap; + struct device *dev; + struct tm tm; + + unsigned char crc8_lkp_tbl[CRC8_TABLE_SIZE]; + unsigned char coef_binaryname[64]; + unsigned char rca_binaryname[64]; + unsigned char dev_name[32]; + + bool force_fwload_status; + bool playback_started; + bool is_loading; + bool is_loaderr; + unsigned int cali_reg_array[CALIB_MAX]; + unsigned int cali_data[CALIB_MAX]; + unsigned int err_code; + void *codec; + int cur_book; + int cur_prog; + int cur_conf; + int fw_state; + int index; + int irq; + + int (*fw_parse_variable_header)(struct tasdevice_priv *tas_priv, + const struct firmware *fmw, + int offset); + int (*fw_parse_program_data)(struct tasdevice_priv *tas_priv, + struct tasdevice_fw *tas_fmw, + const struct firmware *fmw, int offset); + int (*fw_parse_configuration_data)(struct tasdevice_priv *tas_priv, + struct tasdevice_fw *tas_fmw, + const struct firmware *fmw, + int offset); + int (*tasdevice_load_block)(struct tasdevice_priv *tas_priv, + struct tasdev_blk *block); + + int (*save_calibration)(struct tasdevice_priv *tas_priv); + void (*apply_calibration)(struct tasdevice_priv *tas_priv); +}; + +int tasdevice_spi_dev_read(struct tasdevice_priv *tas_priv, + unsigned int reg, unsigned int *value); +int tasdevice_spi_dev_write(struct tasdevice_priv *tas_priv, + unsigned int reg, unsigned int value); +int tasdevice_spi_dev_bulk_write(struct tasdevice_priv *tas_priv, + unsigned int reg, unsigned char *p_data, + unsigned int n_length); +int tasdevice_spi_dev_bulk_read(struct tasdevice_priv *tas_priv, + unsigned int reg, unsigned char *p_data, + unsigned int n_length); +int tasdevice_spi_dev_update_bits(struct tasdevice_priv *tasdevice, + unsigned int reg, unsigned int mask, + unsigned int value); + +void tasdevice_spi_select_cfg_blk(void *context, int conf_no, + unsigned char block_type); +void tasdevice_spi_config_info_remove(void *context); +int tasdevice_spi_dsp_parser(void *context); +int tasdevice_spi_rca_parser(void *context, const struct firmware *fmw); +void tasdevice_spi_dsp_remove(void *context); +void tasdevice_spi_calbin_remove(void *context); +int tasdevice_spi_select_tuningprm_cfg(void *context, int prm, int cfg_no, + int rca_conf_no); +int tasdevice_spi_prmg_load(void *context, int prm_no); +int tasdevice_spi_prmg_calibdata_load(void *context, int prm_no); +void tasdevice_spi_tuning_switch(void *context, int state); +int tas2781_spi_load_calibration(void *context, char *file_name, + unsigned short i); +#endif /* __TAS2781_SPI_H__ */ diff --git a/sound/pci/hda/tas2781_hda_i2c.c b/sound/pci/hda/tas2781_hda_i2c.c index 1bfb00102a77..0e42b87dadb8 100644 --- a/sound/pci/hda/tas2781_hda_i2c.c +++ b/sound/pci/hda/tas2781_hda_i2c.c @@ -2,10 +2,12 @@ // // TAS2781 HDA I2C driver // -// Copyright 2023 Texas Instruments, Inc. +// Copyright 2023 - 2024 Texas Instruments, Inc. // // Author: Shenghao Ding <shenghao-ding@ti.com> +// Current maintainer: Baojun Xu <baojun.xu@ti.com> +#include <linux/unaligned.h> #include <linux/acpi.h> #include <linux/crc8.h> #include <linux/crc32.h> @@ -14,6 +16,7 @@ #include <linux/i2c.h> #include <linux/mod_devicetable.h> #include <linux/module.h> +#include <linux/pci_ids.h> #include <linux/pm_runtime.h> #include <linux/regmap.h> #include <sound/hda_codec.h> @@ -89,7 +92,7 @@ struct tas2781_hda { struct snd_kcontrol *dsp_prog_ctl; struct snd_kcontrol *dsp_conf_ctl; struct snd_kcontrol *prof_ctl; - struct snd_kcontrol *snd_ctls[3]; + struct snd_kcontrol *snd_ctls[2]; }; static int tas2781_get_i2c_res(struct acpi_resource *ares, void *data) @@ -108,12 +111,20 @@ static int tas2781_get_i2c_res(struct acpi_resource *ares, void *data) return 1; } +static const struct acpi_gpio_params speakerid_gpios = { 0, 0, false }; + +static const struct acpi_gpio_mapping tas2781_speaker_id_gpios[] = { + { "speakerid-gpios", &speakerid_gpios, 1 }, + { } +}; + static int tas2781_read_acpi(struct tasdevice_priv *p, const char *hid) { struct acpi_device *adev; struct device *physdev; LIST_HEAD(resources); const char *sub; + uint32_t subid; int ret; adev = acpi_dev_get_first_match_dev(hid, NULL, -1); @@ -123,28 +134,50 @@ static int tas2781_read_acpi(struct tasdevice_priv *p, const char *hid) return -ENODEV; } + physdev = get_device(acpi_get_first_physical_node(adev)); ret = acpi_dev_get_resources(adev, &resources, tas2781_get_i2c_res, p); - if (ret < 0) + if (ret < 0) { + dev_err(p->dev, "Failed to get ACPI resource.\n"); + goto err; + } + sub = acpi_get_subsystem_id(ACPI_HANDLE(physdev)); + if (IS_ERR(sub)) { + /* No subsys id in older tas2563 projects. */ + if (!strncmp(hid, "INT8866", sizeof("INT8866"))) + goto end_2563; + dev_err(p->dev, "Failed to get SUBSYS ID.\n"); + ret = PTR_ERR(sub); goto err; + } + /* Speaker id was needed for ASUS projects. */ + ret = kstrtou32(sub, 16, &subid); + if (!ret && upper_16_bits(subid) == PCI_VENDOR_ID_ASUSTEK) { + ret = devm_acpi_dev_add_driver_gpios(p->dev, + tas2781_speaker_id_gpios); + if (ret < 0) + dev_err(p->dev, "Failed to add driver gpio %d.\n", + ret); + p->speaker_id = devm_gpiod_get(p->dev, "speakerid", GPIOD_IN); + if (IS_ERR(p->speaker_id)) { + dev_err(p->dev, "Failed to get Speaker id.\n"); + ret = PTR_ERR(p->speaker_id); + goto err; + } + } else { + p->speaker_id = NULL; + } +end_2563: acpi_dev_free_resource_list(&resources); strscpy(p->dev_name, hid, sizeof(p->dev_name)); - physdev = get_device(acpi_get_first_physical_node(adev)); - acpi_dev_put(adev); - - /* No side-effect to the playback even if subsystem_id is NULL*/ - sub = acpi_get_subsystem_id(ACPI_HANDLE(physdev)); - if (IS_ERR(sub)) - sub = NULL; - - p->acpi_subsystem_id = sub; - put_device(physdev); + acpi_dev_put(adev); return 0; err: dev_err(p->dev, "read acpi error, ret: %d\n", ret); + put_device(physdev); acpi_dev_put(adev); return ret; @@ -160,19 +193,19 @@ static void tas2781_hda_playback_hook(struct device *dev, int action) pm_runtime_get_sync(dev); mutex_lock(&tas_hda->priv->codec_lock); tasdevice_tuning_switch(tas_hda->priv, 0); + tas_hda->priv->playback_started = true; mutex_unlock(&tas_hda->priv->codec_lock); break; case HDA_GEN_PCM_ACT_CLOSE: mutex_lock(&tas_hda->priv->codec_lock); tasdevice_tuning_switch(tas_hda->priv, 1); + tas_hda->priv->playback_started = false; mutex_unlock(&tas_hda->priv->codec_lock); pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); break; default: - dev_dbg(tas_hda->dev, "Playback action not supported: %d\n", - action); break; } } @@ -195,8 +228,15 @@ static int tasdevice_get_profile_id(struct snd_kcontrol *kcontrol, { struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol); + mutex_lock(&tas_priv->codec_lock); + ucontrol->value.integer.value[0] = tas_priv->rcabin.profile_cfg_id; + dev_dbg(tas_priv->dev, "%s: kcontrol %s: %d\n", + __func__, kcontrol->id.name, tas_priv->rcabin.profile_cfg_id); + + mutex_unlock(&tas_priv->codec_lock); + return 0; } @@ -210,11 +250,19 @@ static int tasdevice_set_profile_id(struct snd_kcontrol *kcontrol, val = clamp(nr_profile, 0, max); + mutex_lock(&tas_priv->codec_lock); + + dev_dbg(tas_priv->dev, "%s: kcontrol %s: %d -> %d\n", + __func__, kcontrol->id.name, + tas_priv->rcabin.profile_cfg_id, val); + if (tas_priv->rcabin.profile_cfg_id != val) { tas_priv->rcabin.profile_cfg_id = val; ret = 1; } + mutex_unlock(&tas_priv->codec_lock); + return ret; } @@ -251,8 +299,15 @@ static int tasdevice_program_get(struct snd_kcontrol *kcontrol, { struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol); + mutex_lock(&tas_priv->codec_lock); + ucontrol->value.integer.value[0] = tas_priv->cur_prog; + dev_dbg(tas_priv->dev, "%s: kcontrol %s: %d\n", + __func__, kcontrol->id.name, tas_priv->cur_prog); + + mutex_unlock(&tas_priv->codec_lock); + return 0; } @@ -267,11 +322,18 @@ static int tasdevice_program_put(struct snd_kcontrol *kcontrol, val = clamp(nr_program, 0, max); + mutex_lock(&tas_priv->codec_lock); + + dev_dbg(tas_priv->dev, "%s: kcontrol %s: %d -> %d\n", + __func__, kcontrol->id.name, tas_priv->cur_prog, val); + if (tas_priv->cur_prog != val) { tas_priv->cur_prog = val; ret = 1; } + mutex_unlock(&tas_priv->codec_lock); + return ret; } @@ -280,8 +342,15 @@ static int tasdevice_config_get(struct snd_kcontrol *kcontrol, { struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol); + mutex_lock(&tas_priv->codec_lock); + ucontrol->value.integer.value[0] = tas_priv->cur_conf; + dev_dbg(tas_priv->dev, "%s: kcontrol %s: %d\n", + __func__, kcontrol->id.name, tas_priv->cur_conf); + + mutex_unlock(&tas_priv->codec_lock); + return 0; } @@ -296,54 +365,39 @@ static int tasdevice_config_put(struct snd_kcontrol *kcontrol, val = clamp(nr_config, 0, max); + mutex_lock(&tas_priv->codec_lock); + + dev_dbg(tas_priv->dev, "%s: kcontrol %s: %d -> %d\n", + __func__, kcontrol->id.name, tas_priv->cur_conf, val); + if (tas_priv->cur_conf != val) { tas_priv->cur_conf = val; ret = 1; } + mutex_unlock(&tas_priv->codec_lock); + return ret; } -/* - * tas2781_digital_getvol - get the volum control - * @kcontrol: control pointer - * @ucontrol: User data - * Customer Kcontrol for tas2781 is primarily for regmap booking, paging - * depends on internal regmap mechanism. - * tas2781 contains book and page two-level register map, especially - * book switching will set the register BXXP00R7F, after switching to the - * correct book, then leverage the mechanism for paging to access the - * register. - */ -static int tas2781_digital_getvol(struct snd_kcontrol *kcontrol, +static int tas2781_amp_getvol(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol); struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; + int ret; - return tasdevice_digital_getvol(tas_priv, ucontrol, mc); -} + mutex_lock(&tas_priv->codec_lock); -static int tas2781_amp_getvol(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol); - struct soc_mixer_control *mc = - (struct soc_mixer_control *)kcontrol->private_value; + ret = tasdevice_amp_getvol(tas_priv, ucontrol, mc); - return tasdevice_amp_getvol(tas_priv, ucontrol, mc); -} + dev_dbg(tas_priv->dev, "%s: kcontrol %s: %ld\n", + __func__, kcontrol->id.name, ucontrol->value.integer.value[0]); -static int tas2781_digital_putvol(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol); - struct soc_mixer_control *mc = - (struct soc_mixer_control *)kcontrol->private_value; + mutex_unlock(&tas_priv->codec_lock); - /* The check of the given value is in tasdevice_digital_putvol. */ - return tasdevice_digital_putvol(tas_priv, ucontrol, mc); + return ret; } static int tas2781_amp_putvol(struct snd_kcontrol *kcontrol, @@ -352,9 +406,19 @@ static int tas2781_amp_putvol(struct snd_kcontrol *kcontrol, struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol); struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; + int ret; + + mutex_lock(&tas_priv->codec_lock); + + dev_dbg(tas_priv->dev, "%s: kcontrol %s: -> %ld\n", + __func__, kcontrol->id.name, ucontrol->value.integer.value[0]); /* The check of the given value is in tasdevice_amp_putvol. */ - return tasdevice_amp_putvol(tas_priv, ucontrol, mc); + ret = tasdevice_amp_putvol(tas_priv, ucontrol, mc); + + mutex_unlock(&tas_priv->codec_lock); + + return ret; } static int tas2781_force_fwload_get(struct snd_kcontrol *kcontrol, @@ -362,9 +426,13 @@ static int tas2781_force_fwload_get(struct snd_kcontrol *kcontrol, { struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol); + mutex_lock(&tas_priv->codec_lock); + ucontrol->value.integer.value[0] = (int)tas_priv->force_fwload_status; - dev_dbg(tas_priv->dev, "%s : Force FWload %s\n", __func__, - tas_priv->force_fwload_status ? "ON" : "OFF"); + dev_dbg(tas_priv->dev, "%s: kcontrol %s: %d\n", + __func__, kcontrol->id.name, tas_priv->force_fwload_status); + + mutex_unlock(&tas_priv->codec_lock); return 0; } @@ -375,14 +443,20 @@ static int tas2781_force_fwload_put(struct snd_kcontrol *kcontrol, struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol); bool change, val = (bool)ucontrol->value.integer.value[0]; + mutex_lock(&tas_priv->codec_lock); + + dev_dbg(tas_priv->dev, "%s: kcontrol %s: %d -> %d\n", + __func__, kcontrol->id.name, + tas_priv->force_fwload_status, val); + if (tas_priv->force_fwload_status == val) change = false; else { change = true; tas_priv->force_fwload_status = val; } - dev_dbg(tas_priv->dev, "%s : Force FWload %s\n", __func__, - tas_priv->force_fwload_status ? "ON" : "OFF"); + + mutex_unlock(&tas_priv->codec_lock); return change; } @@ -391,9 +465,6 @@ static const struct snd_kcontrol_new tas2781_snd_controls[] = { ACARD_SINGLE_RANGE_EXT_TLV("Speaker Analog Gain", TAS2781_AMP_LEVEL, 1, 0, 20, 0, tas2781_amp_getvol, tas2781_amp_putvol, amp_vol_tlv), - ACARD_SINGLE_RANGE_EXT_TLV("Speaker Digital Gain", TAS2781_DVC_LVL, - 0, 0, 200, 1, tas2781_digital_getvol, - tas2781_digital_putvol, dvc_tlv), ACARD_SINGLE_BOOL_EXT("Speaker Force Firmware Load", 0, tas2781_force_fwload_get, tas2781_force_fwload_put), }; @@ -488,25 +559,27 @@ static int tas2563_save_calibration(struct tasdevice_priv *tas_priv) static void tas2781_apply_calib(struct tasdevice_priv *tas_priv) { static const unsigned char page_array[CALIB_MAX] = { - 0x17, 0x18, 0x18, 0x0d, 0x18 + 0x17, 0x18, 0x18, 0x13, 0x18, }; static const unsigned char rgno_array[CALIB_MAX] = { - 0x74, 0x0c, 0x14, 0x3c, 0x7c + 0x74, 0x0c, 0x14, 0x70, 0x7c, }; - unsigned char *data; + int offset = 0; int i, j, rc; + __be32 data; for (i = 0; i < tas_priv->ndev; i++) { - data = tas_priv->cali_data.data + - i * TASDEVICE_SPEAKER_CALIBRATION_SIZE; for (j = 0; j < CALIB_MAX; j++) { + data = cpu_to_be32( + *(uint32_t *)&tas_priv->cali_data.data[offset]); rc = tasdevice_dev_bulk_write(tas_priv, i, TASDEVICE_REG(0, page_array[j], rgno_array[j]), - &(data[4 * j]), 4); + (unsigned char *)&data, 4); if (rc < 0) dev_err(tas_priv->dev, "chn %d calib %d bulk_wr err = %d\n", i, j, rc); + offset += 4; } } } @@ -571,18 +644,13 @@ static void tas2781_hda_remove_controls(struct tas2781_hda *tas_hda) { struct hda_codec *codec = tas_hda->priv->codec; - if (tas_hda->dsp_prog_ctl) - snd_ctl_remove(codec->card, tas_hda->dsp_prog_ctl); - - if (tas_hda->dsp_conf_ctl) - snd_ctl_remove(codec->card, tas_hda->dsp_conf_ctl); + snd_ctl_remove(codec->card, tas_hda->dsp_prog_ctl); + snd_ctl_remove(codec->card, tas_hda->dsp_conf_ctl); for (int i = ARRAY_SIZE(tas_hda->snd_ctls) - 1; i >= 0; i--) - if (tas_hda->snd_ctls[i]) - snd_ctl_remove(codec->card, tas_hda->snd_ctls[i]); + snd_ctl_remove(codec->card, tas_hda->snd_ctls[i]); - if (tas_hda->prof_ctl) - snd_ctl_remove(codec->card, tas_hda->prof_ctl); + snd_ctl_remove(codec->card, tas_hda->prof_ctl); } static void tasdev_fw_ready(const struct firmware *fmw, void *context) @@ -590,7 +658,7 @@ static void tasdev_fw_ready(const struct firmware *fmw, void *context) struct tasdevice_priv *tas_priv = context; struct tas2781_hda *tas_hda = dev_get_drvdata(tas_priv->dev); struct hda_codec *codec = tas_priv->codec; - int i, ret; + int i, ret, spk_id; pm_runtime_get_sync(tas_priv->dev); mutex_lock(&tas_priv->codec_lock); @@ -623,8 +691,25 @@ static void tasdev_fw_ready(const struct firmware *fmw, void *context) tasdevice_dsp_remove(tas_priv); tas_priv->fw_state = TASDEVICE_DSP_FW_PENDING; - scnprintf(tas_priv->coef_binaryname, 64, "TAS2XXX%04X.bin", - codec->core.subsystem_id & 0xffff); + if (tas_priv->speaker_id != NULL) { + // Speaker id need to be checked for ASUS only. + spk_id = gpiod_get_value(tas_priv->speaker_id); + if (spk_id < 0) { + // Speaker id is not valid, use default. + dev_dbg(tas_priv->dev, "Wrong spk_id = %d\n", spk_id); + spk_id = 0; + } + snprintf(tas_priv->coef_binaryname, + sizeof(tas_priv->coef_binaryname), + "TAS2XXX%04X%d.bin", + lower_16_bits(codec->core.subsystem_id), + spk_id); + } else { + snprintf(tas_priv->coef_binaryname, + sizeof(tas_priv->coef_binaryname), + "TAS2XXX%04X.bin", + lower_16_bits(codec->core.subsystem_id)); + } ret = tasdevice_dsp_parser(tas_priv); if (ret) { dev_err(tas_priv->dev, "dspfw load %s error\n", @@ -666,6 +751,7 @@ static void tasdev_fw_ready(const struct firmware *fmw, void *context) tasdevice_save_calibration(tas_priv); tasdevice_tuning_switch(tas_hda->priv, 0); + tas_hda->priv->playback_started = true; out: mutex_unlock(&tas_hda->priv->codec_lock); @@ -679,20 +765,20 @@ static int tas2781_hda_bind(struct device *dev, struct device *master, void *master_data) { struct tas2781_hda *tas_hda = dev_get_drvdata(dev); - struct hda_component *comps = master_data; + struct hda_component_parent *parent = master_data; + struct hda_component *comp; struct hda_codec *codec; unsigned int subid; int ret; - if (!comps || tas_hda->priv->index < 0 || - tas_hda->priv->index >= HDA_MAX_COMPONENTS) + comp = hda_component_from_index(parent, tas_hda->priv->index); + if (!comp) return -EINVAL; - comps = &comps[tas_hda->priv->index]; - if (comps->dev) + if (comp->dev) return -EBUSY; - codec = comps->codec; + codec = parent->codec; subid = codec->core.subsystem_id >> 16; switch (subid) { @@ -706,13 +792,13 @@ static int tas2781_hda_bind(struct device *dev, struct device *master, pm_runtime_get_sync(dev); - comps->dev = dev; + comp->dev = dev; - strscpy(comps->name, dev_name(dev), sizeof(comps->name)); + strscpy(comp->name, dev_name(dev), sizeof(comp->name)); ret = tascodec_init(tas_hda->priv, codec, THIS_MODULE, tasdev_fw_ready); if (!ret) - comps->playback_hook = tas2781_hda_playback_hook; + comp->playback_hook = tas2781_hda_playback_hook; pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); @@ -724,13 +810,14 @@ static void tas2781_hda_unbind(struct device *dev, struct device *master, void *master_data) { struct tas2781_hda *tas_hda = dev_get_drvdata(dev); - struct hda_component *comps = master_data; - comps = &comps[tas_hda->priv->index]; - - if (comps->dev == dev) { - comps->dev = NULL; - memset(comps->name, 0, sizeof(comps->name)); - comps->playback_hook = NULL; + struct hda_component_parent *parent = master_data; + struct hda_component *comp; + + comp = hda_component_from_index(parent, tas_hda->priv->index); + if (comp && (comp->dev == dev)) { + comp->dev = NULL; + memset(comp->name, 0, sizeof(comp->name)); + comp->playback_hook = NULL; } tas2781_hda_remove_controls(tas_hda); @@ -750,11 +837,11 @@ static void tas2781_hda_remove(struct device *dev) { struct tas2781_hda *tas_hda = dev_get_drvdata(dev); + component_del(tas_hda->dev, &tas2781_hda_comp_ops); + pm_runtime_get_sync(tas_hda->dev); pm_runtime_disable(tas_hda->dev); - component_del(tas_hda->dev, &tas2781_hda_comp_ops); - pm_runtime_put_noidle(tas_hda->dev); tasdevice_remove(tas_hda->priv); @@ -791,7 +878,7 @@ static int tas2781_hda_i2c_probe(struct i2c_client *clt) } else return -ENODEV; - tas_hda->priv->irq_info.irq = clt->irq; + tas_hda->priv->irq = clt->irq; ret = tas2781_read_acpi(tas_hda->priv, device_name); if (ret) return dev_err_probe(tas_hda->dev, ret, @@ -805,12 +892,9 @@ static int tas2781_hda_i2c_probe(struct i2c_client *clt) pm_runtime_use_autosuspend(tas_hda->dev); pm_runtime_mark_last_busy(tas_hda->dev); pm_runtime_set_active(tas_hda->dev); - pm_runtime_get_noresume(tas_hda->dev); pm_runtime_enable(tas_hda->dev); - pm_runtime_put_autosuspend(tas_hda->dev); - - tas2781_reset(tas_hda->priv); + tasdevice_reset(tas_hda->priv); ret = component_add(tas_hda->dev, &tas2781_hda_comp_ops); if (ret) { @@ -832,23 +916,19 @@ static void tas2781_hda_i2c_remove(struct i2c_client *clt) static int tas2781_runtime_suspend(struct device *dev) { struct tas2781_hda *tas_hda = dev_get_drvdata(dev); - int i; dev_dbg(tas_hda->dev, "Runtime Suspend\n"); mutex_lock(&tas_hda->priv->codec_lock); + /* The driver powers up the amplifiers at module load time. + * Stop the playback if it's unused. + */ if (tas_hda->priv->playback_started) { tasdevice_tuning_switch(tas_hda->priv, 1); tas_hda->priv->playback_started = false; } - for (i = 0; i < tas_hda->priv->ndev; i++) { - tas_hda->priv->tasdevice[i].cur_book = -1; - tas_hda->priv->tasdevice[i].cur_prog = -1; - tas_hda->priv->tasdevice[i].cur_conf = -1; - } - mutex_unlock(&tas_hda->priv->codec_lock); return 0; @@ -877,16 +957,16 @@ static int tas2781_runtime_resume(struct device *dev) static int tas2781_system_suspend(struct device *dev) { struct tas2781_hda *tas_hda = dev_get_drvdata(dev); - int ret; dev_dbg(tas_hda->priv->dev, "System Suspend\n"); - ret = pm_runtime_force_suspend(dev); - if (ret) - return ret; + mutex_lock(&tas_hda->priv->codec_lock); /* Shutdown chip before system suspend */ - tasdevice_tuning_switch(tas_hda->priv, 1); + if (tas_hda->priv->playback_started) + tasdevice_tuning_switch(tas_hda->priv, 1); + + mutex_unlock(&tas_hda->priv->codec_lock); /* * Reset GPIO may be shared, so cannot reset here. @@ -898,13 +978,9 @@ static int tas2781_system_suspend(struct device *dev) static int tas2781_system_resume(struct device *dev) { struct tas2781_hda *tas_hda = dev_get_drvdata(dev); - int i, ret; - - dev_info(tas_hda->priv->dev, "System Resume\n"); + int i; - ret = pm_runtime_force_resume(dev); - if (ret) - return ret; + dev_dbg(tas_hda->priv->dev, "System Resume\n"); mutex_lock(&tas_hda->priv->codec_lock); @@ -913,13 +989,17 @@ static int tas2781_system_resume(struct device *dev) tas_hda->priv->tasdevice[i].cur_prog = -1; tas_hda->priv->tasdevice[i].cur_conf = -1; } - tas2781_reset(tas_hda->priv); + tasdevice_reset(tas_hda->priv); tasdevice_prmg_load(tas_hda->priv, tas_hda->priv->cur_prog); /* If calibrated data occurs error, dsp will still work with default * calibrated data inside algo. */ tasdevice_apply_calibration(tas_hda->priv); + + if (tas_hda->priv->playback_started) + tasdevice_tuning_switch(tas_hda->priv, 0); + mutex_unlock(&tas_hda->priv->codec_lock); return 0; @@ -931,7 +1011,7 @@ static const struct dev_pm_ops tas2781_hda_pm_ops = { }; static const struct i2c_device_id tas2781_hda_i2c_id[] = { - { "tas2781-hda", 0 }, + { "tas2781-hda" }, {} }; @@ -957,4 +1037,4 @@ module_i2c_driver(tas2781_hda_i2c_driver); MODULE_DESCRIPTION("TAS2781 HDA Driver"); MODULE_AUTHOR("Shenghao Ding, TI, <shenghao-ding@ti.com>"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(SND_SOC_TAS2781_FMWLIB); +MODULE_IMPORT_NS("SND_SOC_TAS2781_FMWLIB"); diff --git a/sound/pci/hda/tas2781_hda_spi.c b/sound/pci/hda/tas2781_hda_spi.c new file mode 100644 index 000000000000..04db80af53c0 --- /dev/null +++ b/sound/pci/hda/tas2781_hda_spi.c @@ -0,0 +1,1266 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// TAS2781 HDA SPI driver +// +// Copyright 2024 Texas Instruments, Inc. +// +// Author: Baojun Xu <baojun.xu@ti.com> + +#include <linux/acpi.h> +#include <linux/array_size.h> +#include <linux/bits.h> +#include <linux/cleanup.h> +#include <linux/crc8.h> +#include <linux/crc32.h> +#include <linux/efi.h> +#include <linux/firmware.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/pm_runtime.h> +#include <linux/property.h> +#include <linux/regmap.h> +#include <linux/spi/spi.h> +#include <linux/time.h> +#include <linux/types.h> +#include <linux/units.h> + +#include <sound/hda_codec.h> +#include <sound/soc.h> +#include <sound/tas2781-dsp.h> +#include <sound/tlv.h> +#include <sound/tas2781-tlv.h> + +#include "tas2781-spi.h" + +#include "hda_local.h" +#include "hda_auto_parser.h" +#include "hda_component.h" +#include "hda_jack.h" +#include "hda_generic.h" + +/* + * No standard control callbacks for SNDRV_CTL_ELEM_IFACE_CARD + * Define two controls, one is Volume control callbacks, the other is + * flag setting control callbacks. + */ + +/* Volume control callbacks for tas2781 */ +#define ACARD_SINGLE_RANGE_EXT_TLV(xname, xreg, xshift, xmin, xmax, xinvert, \ + xhandler_get, xhandler_put, tlv_array) { \ + .iface = SNDRV_CTL_ELEM_IFACE_CARD, .name = (xname), \ + .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \ + SNDRV_CTL_ELEM_ACCESS_READWRITE, \ + .tlv.p = (tlv_array), \ + .info = snd_soc_info_volsw_range, \ + .get = xhandler_get, .put = xhandler_put, \ + .private_value = (unsigned long)&(struct soc_mixer_control) { \ + .reg = xreg, .rreg = xreg, \ + .shift = xshift, .rshift = xshift,\ + .min = xmin, .max = xmax, .invert = xinvert, \ + } \ +} + +/* Flag control callbacks for tas2781 */ +#define ACARD_SINGLE_BOOL_EXT(xname, xdata, xhandler_get, xhandler_put) { \ + .iface = SNDRV_CTL_ELEM_IFACE_CARD, \ + .name = xname, \ + .info = snd_ctl_boolean_mono_info, \ + .get = xhandler_get, \ + .put = xhandler_put, \ + .private_value = xdata, \ +} + +struct tas2781_hda { + struct tasdevice_priv *priv; + struct acpi_device *dacpi; + struct snd_kcontrol *dsp_prog_ctl; + struct snd_kcontrol *dsp_conf_ctl; + struct snd_kcontrol *snd_ctls[3]; + struct snd_kcontrol *prof_ctl; +}; + +static const struct regmap_range_cfg tasdevice_ranges[] = { + { + .range_min = 0, + .range_max = TASDEVICE_MAX_SIZE, + .selector_reg = TASDEVICE_PAGE_SELECT, + .selector_mask = GENMASK(7, 0), + .selector_shift = 0, + .window_start = 0, + .window_len = TASDEVICE_MAX_PAGE, + }, +}; + +static const struct regmap_config tasdevice_regmap = { + .reg_bits = 8, + .val_bits = 8, + .zero_flag_mask = true, + .cache_type = REGCACHE_NONE, + .ranges = tasdevice_ranges, + .num_ranges = ARRAY_SIZE(tasdevice_ranges), + .max_register = TASDEVICE_MAX_SIZE, +}; + +static int tasdevice_spi_switch_book(struct tasdevice_priv *tas_priv, int reg) +{ + struct regmap *map = tas_priv->regmap; + + if (tas_priv->cur_book != TASDEVICE_BOOK_ID(reg)) { + int ret = regmap_write(map, TASDEVICE_BOOKCTL_REG, + TASDEVICE_BOOK_ID(reg)); + if (ret < 0) { + dev_err(tas_priv->dev, "Switch Book E=%d\n", ret); + return ret; + } + tas_priv->cur_book = TASDEVICE_BOOK_ID(reg); + } + return 0; +} + +int tasdevice_spi_dev_read(struct tasdevice_priv *tas_priv, + unsigned int reg, + unsigned int *val) +{ + struct regmap *map = tas_priv->regmap; + int ret; + + ret = tasdevice_spi_switch_book(tas_priv, reg); + if (ret < 0) + return ret; + + /* + * In our TAS2781 SPI mode, if read from other book (not book 0), + * or read from page number larger than 1 in book 0, one more byte + * read is needed, and first byte is a dummy byte, need to be ignored. + */ + if ((TASDEVICE_BOOK_ID(reg) > 0) || (TASDEVICE_PAGE_ID(reg) > 1)) { + unsigned char data[2]; + + ret = regmap_bulk_read(map, TASDEVICE_PAGE_REG(reg) | 1, + data, sizeof(data)); + *val = data[1]; + } else { + ret = regmap_read(map, TASDEVICE_PAGE_REG(reg) | 1, val); + } + if (ret < 0) + dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret); + + return ret; +} + +int tasdevice_spi_dev_write(struct tasdevice_priv *tas_priv, + unsigned int reg, + unsigned int value) +{ + struct regmap *map = tas_priv->regmap; + int ret; + + ret = tasdevice_spi_switch_book(tas_priv, reg); + if (ret < 0) + return ret; + + ret = regmap_write(map, TASDEVICE_PAGE_REG(reg), value); + if (ret < 0) + dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret); + + return ret; +} + +int tasdevice_spi_dev_bulk_write(struct tasdevice_priv *tas_priv, + unsigned int reg, + unsigned char *data, + unsigned int len) +{ + struct regmap *map = tas_priv->regmap; + int ret; + + ret = tasdevice_spi_switch_book(tas_priv, reg); + if (ret < 0) + return ret; + + ret = regmap_bulk_write(map, TASDEVICE_PAGE_REG(reg), data, len); + if (ret < 0) + dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret); + + return ret; +} + +int tasdevice_spi_dev_bulk_read(struct tasdevice_priv *tas_priv, + unsigned int reg, + unsigned char *data, + unsigned int len) +{ + struct regmap *map = tas_priv->regmap; + int ret; + + ret = tasdevice_spi_switch_book(tas_priv, reg); + if (ret < 0) + return ret; + + if (len > TASDEVICE_MAX_PAGE) + len = TASDEVICE_MAX_PAGE; + /* + * In our TAS2781 SPI mode, if read from other book (not book 0), + * or read from page number larger than 1 in book 0, one more byte + * read is needed, and first byte is a dummy byte, need to be ignored. + */ + if ((TASDEVICE_BOOK_ID(reg) > 0) || (TASDEVICE_PAGE_ID(reg) > 1)) { + unsigned char buf[TASDEVICE_MAX_PAGE+1]; + + ret = regmap_bulk_read(map, TASDEVICE_PAGE_REG(reg) | 1, buf, + len + 1); + memcpy(data, buf + 1, len); + } else { + ret = regmap_bulk_read(map, TASDEVICE_PAGE_REG(reg) | 1, data, + len); + } + if (ret < 0) + dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret); + + return ret; +} + +int tasdevice_spi_dev_update_bits(struct tasdevice_priv *tas_priv, + unsigned int reg, + unsigned int mask, + unsigned int value) +{ + struct regmap *map = tas_priv->regmap; + int ret, val; + + /* + * In our TAS2781 SPI mode, read/write was masked in last bit of + * address, it cause regmap_update_bits() not work as expected. + */ + ret = tasdevice_spi_dev_read(tas_priv, reg, &val); + if (ret < 0) { + dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret); + return ret; + } + ret = regmap_write(map, TASDEVICE_PAGE_REG(reg), + (val & ~mask) | (mask & value)); + if (ret < 0) + dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret); + + return ret; +} + +static void tas2781_spi_reset(struct tasdevice_priv *tas_dev) +{ + int ret; + + if (tas_dev->reset) { + gpiod_set_value_cansleep(tas_dev->reset, 0); + fsleep(800); + gpiod_set_value_cansleep(tas_dev->reset, 1); + } + ret = tasdevice_spi_dev_write(tas_dev, TAS2781_REG_SWRESET, + TAS2781_REG_SWRESET_RESET); + if (ret < 0) + dev_err(tas_dev->dev, "dev sw-reset fail, %d\n", ret); + fsleep(1000); +} + +static int tascodec_spi_init(struct tasdevice_priv *tas_priv, + void *codec, struct module *module, + void (*cont)(const struct firmware *fw, void *context)) +{ + int ret; + + /* + * Codec Lock Hold to ensure that codec_probe and firmware parsing and + * loading do not simultaneously execute. + */ + guard(mutex)(&tas_priv->codec_lock); + + scnprintf(tas_priv->rca_binaryname, + sizeof(tas_priv->rca_binaryname), "%sRCA%d.bin", + tas_priv->dev_name, tas_priv->index); + crc8_populate_msb(tas_priv->crc8_lkp_tbl, TASDEVICE_CRC8_POLYNOMIAL); + tas_priv->codec = codec; + ret = request_firmware_nowait(module, FW_ACTION_UEVENT, + tas_priv->rca_binaryname, tas_priv->dev, GFP_KERNEL, tas_priv, + cont); + if (ret) + dev_err(tas_priv->dev, "request_firmware_nowait err:0x%08x\n", + ret); + + return ret; +} + +static void tasdevice_spi_init(struct tasdevice_priv *tas_priv) +{ + tas_priv->cur_prog = -1; + tas_priv->cur_conf = -1; + + tas_priv->cur_book = -1; + tas_priv->cur_prog = -1; + tas_priv->cur_conf = -1; + + /* Store default registers address for calibration data. */ + tas_priv->cali_reg_array[0] = TASDEVICE_REG(0, 0x17, 0x74); + tas_priv->cali_reg_array[1] = TASDEVICE_REG(0, 0x18, 0x0c); + tas_priv->cali_reg_array[2] = TASDEVICE_REG(0, 0x18, 0x14); + tas_priv->cali_reg_array[3] = TASDEVICE_REG(0, 0x13, 0x70); + tas_priv->cali_reg_array[4] = TASDEVICE_REG(0, 0x18, 0x7c); + + mutex_init(&tas_priv->codec_lock); +} + +static int tasdevice_spi_amp_putvol(struct tasdevice_priv *tas_priv, + struct snd_ctl_elem_value *ucontrol, + struct soc_mixer_control *mc) +{ + unsigned int invert = mc->invert; + unsigned char mask; + int max = mc->max; + int val, ret; + + mask = rounddown_pow_of_two(max); + mask <<= mc->shift; + val = clamp(invert ? max - ucontrol->value.integer.value[0] : + ucontrol->value.integer.value[0], 0, max); + ret = tasdevice_spi_dev_update_bits(tas_priv, + mc->reg, mask, (unsigned int)(val << mc->shift)); + if (ret) + dev_err(tas_priv->dev, "set AMP vol error in dev %d\n", + tas_priv->index); + + return ret; +} + +static int tasdevice_spi_amp_getvol(struct tasdevice_priv *tas_priv, + struct snd_ctl_elem_value *ucontrol, + struct soc_mixer_control *mc) +{ + unsigned int invert = mc->invert; + unsigned char mask = 0; + int max = mc->max; + int ret, val; + + /* Read the primary device */ + ret = tasdevice_spi_dev_read(tas_priv, mc->reg, &val); + if (ret) { + dev_err(tas_priv->dev, "%s, get AMP vol error\n", __func__); + return ret; + } + + mask = rounddown_pow_of_two(max); + mask <<= mc->shift; + val = (val & mask) >> mc->shift; + val = clamp(invert ? max - val : val, 0, max); + ucontrol->value.integer.value[0] = val; + + return ret; +} + +static int tasdevice_spi_digital_putvol(struct tasdevice_priv *tas_priv, + struct snd_ctl_elem_value *ucontrol, + struct soc_mixer_control *mc) +{ + unsigned int invert = mc->invert; + int max = mc->max; + int val, ret; + + val = clamp(invert ? max - ucontrol->value.integer.value[0] : + ucontrol->value.integer.value[0], 0, max); + ret = tasdevice_spi_dev_write(tas_priv, mc->reg, (unsigned int)val); + if (ret) + dev_err(tas_priv->dev, "set digital vol err in dev %d\n", + tas_priv->index); + + return ret; +} + +static int tasdevice_spi_digital_getvol(struct tasdevice_priv *tas_priv, + struct snd_ctl_elem_value *ucontrol, + struct soc_mixer_control *mc) +{ + unsigned int invert = mc->invert; + int max = mc->max; + int ret, val; + + /* Read the primary device as the whole */ + ret = tasdevice_spi_dev_read(tas_priv, mc->reg, &val); + if (ret) { + dev_err(tas_priv->dev, "%s, get digital vol err\n", __func__); + return ret; + } + + val = clamp(invert ? max - val : val, 0, max); + ucontrol->value.integer.value[0] = val; + + return ret; +} + +static int tas2781_read_acpi(struct tas2781_hda *tas_hda, + const char *hid, + int id) +{ + struct tasdevice_priv *p = tas_hda->priv; + struct acpi_device *adev; + struct device *physdev; + u32 values[HDA_MAX_COMPONENTS]; + const char *property; + size_t nval; + int ret, i; + + adev = acpi_dev_get_first_match_dev(hid, NULL, -1); + if (!adev) { + dev_err(p->dev, "Failed to find ACPI device: %s\n", hid); + return -ENODEV; + } + + strscpy(p->dev_name, hid, sizeof(p->dev_name)); + tas_hda->dacpi = adev; + physdev = get_device(acpi_get_first_physical_node(adev)); + acpi_dev_put(adev); + + property = "ti,dev-index"; + ret = device_property_count_u32(physdev, property); + if (ret <= 0 || ret > ARRAY_SIZE(values)) { + ret = -EINVAL; + goto err; + } + nval = ret; + + ret = device_property_read_u32_array(physdev, property, values, nval); + if (ret) + goto err; + + p->index = U8_MAX; + for (i = 0; i < nval; i++) { + if (values[i] == id) { + p->index = i; + break; + } + } + if (p->index == U8_MAX) { + dev_dbg(p->dev, "No index found in %s\n", property); + ret = -ENODEV; + goto err; + } + + if (p->index == 0) { + /* All of amps share same RESET pin. */ + p->reset = devm_gpiod_get_index_optional(physdev, "reset", + p->index, GPIOD_OUT_LOW); + if (IS_ERR(p->reset)) { + ret = PTR_ERR(p->reset); + dev_err_probe(p->dev, ret, "Failed on reset GPIO\n"); + goto err; + } + } + put_device(physdev); + + return 0; +err: + dev_err(p->dev, "read acpi error, ret: %d\n", ret); + put_device(physdev); + acpi_dev_put(adev); + + return ret; +} + +static void tas2781_hda_playback_hook(struct device *dev, int action) +{ + struct tas2781_hda *tas_hda = dev_get_drvdata(dev); + + if (action == HDA_GEN_PCM_ACT_OPEN) { + pm_runtime_get_sync(dev); + guard(mutex)(&tas_hda->priv->codec_lock); + tasdevice_spi_tuning_switch(tas_hda->priv, 0); + } else if (action == HDA_GEN_PCM_ACT_CLOSE) { + guard(mutex)(&tas_hda->priv->codec_lock); + tasdevice_spi_tuning_switch(tas_hda->priv, 1); + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + } +} + +static int tasdevice_info_profile(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol); + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = tas_priv->rcabin.ncfgs - 1; + + return 0; +} + +static int tasdevice_get_profile_id(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol); + + ucontrol->value.integer.value[0] = tas_priv->rcabin.profile_cfg_id; + + return 0; +} + +static int tasdevice_set_profile_id(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol); + int max = tas_priv->rcabin.ncfgs - 1; + int val; + + val = clamp(ucontrol->value.integer.value[0], 0, max); + if (tas_priv->rcabin.profile_cfg_id != val) { + tas_priv->rcabin.profile_cfg_id = val; + return 1; + } + + return 0; +} + +static int tasdevice_info_programs(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol); + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = tas_priv->fmw->nr_programs - 1; + + return 0; +} + +static int tasdevice_info_config(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol); + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = tas_priv->fmw->nr_configurations - 1; + + return 0; +} + +static int tasdevice_program_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol); + + ucontrol->value.integer.value[0] = tas_priv->cur_prog; + + return 0; +} + +static int tasdevice_program_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol); + int nr_program = ucontrol->value.integer.value[0]; + int max = tas_priv->fmw->nr_programs - 1; + int val; + + val = clamp(nr_program, 0, max); + + if (tas_priv->cur_prog != val) { + tas_priv->cur_prog = val; + return 1; + } + + return 0; +} + +static int tasdevice_config_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol); + + ucontrol->value.integer.value[0] = tas_priv->cur_conf; + + return 0; +} + +static int tasdevice_config_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol); + int max = tas_priv->fmw->nr_configurations - 1; + int val; + + val = clamp(ucontrol->value.integer.value[0], 0, max); + + if (tas_priv->cur_conf != val) { + tas_priv->cur_conf = val; + return 1; + } + + return 0; +} + +/* + * tas2781_digital_getvol - get the volum control + * @kcontrol: control pointer + * @ucontrol: User data + * + * Customer Kcontrol for tas2781 is primarily for regmap booking, paging + * depends on internal regmap mechanism. + * tas2781 contains book and page two-level register map, especially + * book switching will set the register BXXP00R7F, after switching to the + * correct book, then leverage the mechanism for paging to access the + * register. + * + * Return 0 if succeeded. + */ +static int tas2781_digital_getvol(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + + return tasdevice_spi_digital_getvol(tas_priv, ucontrol, mc); +} + +static int tas2781_amp_getvol(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + + return tasdevice_spi_amp_getvol(tas_priv, ucontrol, mc); +} + +static int tas2781_digital_putvol(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + + /* The check of the given value is in tasdevice_digital_putvol. */ + return tasdevice_spi_digital_putvol(tas_priv, ucontrol, mc); +} + +static int tas2781_amp_putvol(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + + /* The check of the given value is in tasdevice_amp_putvol. */ + return tasdevice_spi_amp_putvol(tas_priv, ucontrol, mc); +} + +static int tas2781_force_fwload_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol); + + ucontrol->value.integer.value[0] = (int)tas_priv->force_fwload_status; + dev_dbg(tas_priv->dev, "%s : Force FWload %s\n", __func__, + str_on_off(tas_priv->force_fwload_status)); + + return 0; +} + +static int tas2781_force_fwload_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol); + bool change, val = (bool)ucontrol->value.integer.value[0]; + + if (tas_priv->force_fwload_status == val) { + change = false; + } else { + change = true; + tas_priv->force_fwload_status = val; + } + dev_dbg(tas_priv->dev, "%s : Force FWload %s\n", __func__, + str_on_off(tas_priv->force_fwload_status)); + + return change; +} + +static const struct snd_kcontrol_new tas2781_snd_controls[] = { + ACARD_SINGLE_RANGE_EXT_TLV("Speaker Analog Gain 0", TAS2781_AMP_LEVEL, + 1, 0, 20, 0, tas2781_amp_getvol, + tas2781_amp_putvol, amp_vol_tlv), + ACARD_SINGLE_RANGE_EXT_TLV("Speaker Digital Gain 0", TAS2781_DVC_LVL, + 0, 0, 200, 1, tas2781_digital_getvol, + tas2781_digital_putvol, dvc_tlv), + ACARD_SINGLE_BOOL_EXT("Speaker Force Firmware Load 0", 0, + tas2781_force_fwload_get, tas2781_force_fwload_put), + ACARD_SINGLE_RANGE_EXT_TLV("Speaker Analog Gain 1", TAS2781_AMP_LEVEL, + 1, 0, 20, 0, tas2781_amp_getvol, + tas2781_amp_putvol, amp_vol_tlv), + ACARD_SINGLE_RANGE_EXT_TLV("Speaker Digital Gain 1", TAS2781_DVC_LVL, + 0, 0, 200, 1, tas2781_digital_getvol, + tas2781_digital_putvol, dvc_tlv), + ACARD_SINGLE_BOOL_EXT("Speaker Force Firmware Load 1", 0, + tas2781_force_fwload_get, tas2781_force_fwload_put), +}; + +static const struct snd_kcontrol_new tas2781_prof_ctrl[] = { +{ + .name = "Speaker Profile Id - 0", + .iface = SNDRV_CTL_ELEM_IFACE_CARD, + .info = tasdevice_info_profile, + .get = tasdevice_get_profile_id, + .put = tasdevice_set_profile_id, +}, +{ + .name = "Speaker Profile Id - 1", + .iface = SNDRV_CTL_ELEM_IFACE_CARD, + .info = tasdevice_info_profile, + .get = tasdevice_get_profile_id, + .put = tasdevice_set_profile_id, +}, +}; +static const struct snd_kcontrol_new tas2781_dsp_prog_ctrl[] = { +{ + .name = "Speaker Program Id 0", + .iface = SNDRV_CTL_ELEM_IFACE_CARD, + .info = tasdevice_info_programs, + .get = tasdevice_program_get, + .put = tasdevice_program_put, +}, +{ + .name = "Speaker Program Id 1", + .iface = SNDRV_CTL_ELEM_IFACE_CARD, + .info = tasdevice_info_programs, + .get = tasdevice_program_get, + .put = tasdevice_program_put, +}, +}; + +static const struct snd_kcontrol_new tas2781_dsp_conf_ctrl[] = { +{ + .name = "Speaker Config Id 0", + .iface = SNDRV_CTL_ELEM_IFACE_CARD, + .info = tasdevice_info_config, + .get = tasdevice_config_get, + .put = tasdevice_config_put, +}, +{ + .name = "Speaker Config Id 1", + .iface = SNDRV_CTL_ELEM_IFACE_CARD, + .info = tasdevice_info_config, + .get = tasdevice_config_get, + .put = tasdevice_config_put, +}, +}; + +static void tas2781_apply_calib(struct tasdevice_priv *tas_priv) +{ + int i, rc; + + /* + * If no calibration data exist in tasdevice_priv *tas_priv, + * calibration apply will be ignored, and use default values + * in firmware binary, which was loaded during firmware download. + */ + if (tas_priv->cali_data[0] == 0) + return; + /* + * Calibration data was saved in tasdevice_priv *tas_priv as: + * unsigned int cali_data[CALIB_MAX]; + * and every data (in 4 bytes) will be saved in register which in + * book 0, and page number in page_array[], offset was saved in + * rgno_array[]. + */ + for (i = 0; i < CALIB_MAX; i++) { + rc = tasdevice_spi_dev_bulk_write(tas_priv, + tas_priv->cali_reg_array[i], + (unsigned char *)&tas_priv->cali_data[i], 4); + if (rc < 0) + dev_err(tas_priv->dev, + "chn %d calib %d bulk_wr err = %d\n", + tas_priv->index, i, rc); + } +} + +/* + * Update the calibration data, including speaker impedance, f0, etc, + * into algo. Calibrate data is done by manufacturer in the factory. + * These data are used by Algo for calculating the speaker temperature, + * speaker membrane excursion and f0 in real time during playback. + * Calibration data format in EFI is V2, since 2024. + */ +static int tas2781_save_calibration(struct tasdevice_priv *tas_priv) +{ + /* + * GUID was used for data access in BIOS, it was provided by board + * manufactory, like HP: "{02f9af02-7734-4233-b43d-93fe5aa35db3}" + */ + efi_guid_t efi_guid = + EFI_GUID(0x02f9af02, 0x7734, 0x4233, + 0xb4, 0x3d, 0x93, 0xfe, 0x5a, 0xa3, 0x5d, 0xb3); + static efi_char16_t efi_name[] = TASDEVICE_CALIBRATION_DATA_NAME; + unsigned char data[TASDEVICE_CALIBRATION_DATA_SIZE], *buf; + unsigned int attr, crc, offset, *tmp_val; + struct tm *tm = &tas_priv->tm; + unsigned long total_sz = 0; + efi_status_t status; + + tas_priv->cali_data[0] = 0; + status = efi.get_variable(efi_name, &efi_guid, &attr, &total_sz, data); + if (status == EFI_BUFFER_TOO_SMALL) { + if (total_sz > TASDEVICE_CALIBRATION_DATA_SIZE) + return -ENOMEM; + /* Get variable contents into buffer */ + status = efi.get_variable(efi_name, &efi_guid, &attr, + &total_sz, data); + } + if (status != EFI_SUCCESS) + return status; + + tmp_val = (unsigned int *)data; + if (tmp_val[0] == 2781) { + /* + * New features were added in calibrated Data V3: + * 1. Added calibration registers address define in + * a node, marked as Device id == 0x80. + * New features were added in calibrated Data V2: + * 1. Added some the fields to store the link_id and + * uniqie_id for multi-link solutions + * 2. Support flexible number of devices instead of + * fixed one in V1. + * Layout of calibrated data V2 in UEFI(total 256 bytes): + * ChipID (2781, 4 bytes) + * Device-Sum (4 bytes) + * TimeStamp of Calibration (4 bytes) + * for (i = 0; i < Device-Sum; i++) { + * Device #i index_info () { + * SDW link id (2bytes) + * SDW unique_id (2bytes) + * } // if Device number is 0x80, mean it's + * calibration registers address. + * Calibrated Data of Device #i (20 bytes) + * } + * CRC (4 bytes) + * Reserved (the rest) + */ + crc = crc32(~0, data, (3 + tmp_val[1] * 6) * 4) ^ ~0; + + if (crc != tmp_val[3 + tmp_val[1] * 6]) + return 0; + + time64_to_tm(tmp_val[2], 0, tm); + for (int j = 0; j < tmp_val[1]; j++) { + offset = j * 6 + 3; + if (tmp_val[offset] == tas_priv->index) { + for (int i = 0; i < CALIB_MAX; i++) + tas_priv->cali_data[i] = + tmp_val[offset + i + 1]; + } else if (tmp_val[offset] == + TASDEVICE_CALIBRATION_REG_ADDRESS) { + for (int i = 0; i < CALIB_MAX; i++) { + buf = &data[(offset + i + 1) * 4]; + tas_priv->cali_reg_array[i] = + TASDEVICE_REG(buf[1], buf[2], + buf[3]); + } + } + tas_priv->apply_calibration(tas_priv); + } + } else { + /* + * Calibration data is in V1 format. + * struct cali_data { + * char cali_data[20]; + * } + * + * struct { + * struct cali_data cali_data[4]; + * int TimeStamp of Calibration (4 bytes) + * int CRC (4 bytes) + * } ueft; + */ + crc = crc32(~0, data, 84) ^ ~0; + if (crc == tmp_val[21]) { + time64_to_tm(tmp_val[20], 0, tm); + for (int i = 0; i < CALIB_MAX; i++) + tas_priv->cali_data[i] = + tmp_val[tas_priv->index * 5 + i]; + tas_priv->apply_calibration(tas_priv); + } + } + + return 0; +} + +static void tas2781_hda_remove_controls(struct tas2781_hda *tas_hda) +{ + struct hda_codec *codec = tas_hda->priv->codec; + + snd_ctl_remove(codec->card, tas_hda->dsp_prog_ctl); + + snd_ctl_remove(codec->card, tas_hda->dsp_conf_ctl); + + for (int i = ARRAY_SIZE(tas_hda->snd_ctls) - 1; i >= 0; i--) + snd_ctl_remove(codec->card, tas_hda->snd_ctls[i]); + + snd_ctl_remove(codec->card, tas_hda->prof_ctl); +} + +static void tasdev_fw_ready(const struct firmware *fmw, void *context) +{ + struct tasdevice_priv *tas_priv = context; + struct tas2781_hda *tas_hda = dev_get_drvdata(tas_priv->dev); + struct hda_codec *codec = tas_priv->codec; + int i, j, ret, val; + + pm_runtime_get_sync(tas_priv->dev); + guard(mutex)(&tas_priv->codec_lock); + + ret = tasdevice_spi_rca_parser(tas_priv, fmw); + if (ret) + goto out; + + /* Add control one time only. */ + tas_hda->prof_ctl = snd_ctl_new1(&tas2781_prof_ctrl[tas_priv->index], + tas_priv); + ret = snd_ctl_add(codec->card, tas_hda->prof_ctl); + if (ret) { + dev_err(tas_priv->dev, "Failed to add KControl %s = %d\n", + tas2781_prof_ctrl[tas_priv->index].name, ret); + goto out; + } + j = tas_priv->index * ARRAY_SIZE(tas2781_snd_controls) / 2; + for (i = 0; i < 3; i++) { + tas_hda->snd_ctls[i] = snd_ctl_new1(&tas2781_snd_controls[i+j], + tas_priv); + ret = snd_ctl_add(codec->card, tas_hda->snd_ctls[i]); + if (ret) { + dev_err(tas_priv->dev, + "Failed to add KControl %s = %d\n", + tas2781_snd_controls[i+tas_priv->index*3].name, + ret); + goto out; + } + } + + tasdevice_spi_dsp_remove(tas_priv); + + tas_priv->fw_state = TASDEVICE_DSP_FW_PENDING; + scnprintf(tas_priv->coef_binaryname, 64, "TAS2XXX%08X-%01d.bin", + codec->core.subsystem_id, tas_priv->index); + ret = tasdevice_spi_dsp_parser(tas_priv); + if (ret) { + dev_err(tas_priv->dev, "dspfw load %s error\n", + tas_priv->coef_binaryname); + tas_priv->fw_state = TASDEVICE_DSP_FW_FAIL; + goto out; + } + + /* Add control one time only. */ + tas_hda->dsp_prog_ctl = + snd_ctl_new1(&tas2781_dsp_prog_ctrl[tas_priv->index], + tas_priv); + ret = snd_ctl_add(codec->card, tas_hda->dsp_prog_ctl); + if (ret) { + dev_err(tas_priv->dev, + "Failed to add KControl %s = %d\n", + tas2781_dsp_prog_ctrl[tas_priv->index].name, ret); + goto out; + } + + tas_hda->dsp_conf_ctl = + snd_ctl_new1(&tas2781_dsp_conf_ctrl[tas_priv->index], + tas_priv); + ret = snd_ctl_add(codec->card, tas_hda->dsp_conf_ctl); + if (ret) { + dev_err(tas_priv->dev, "Failed to add KControl %s = %d\n", + tas2781_dsp_conf_ctrl[tas_priv->index].name, ret); + goto out; + } + + /* Perform AMP reset before firmware download. */ + tas_priv->rcabin.profile_cfg_id = TAS2781_PRE_POST_RESET_CFG; + tas2781_spi_reset(tas_priv); + tas_priv->rcabin.profile_cfg_id = 0; + + tas_priv->fw_state = TASDEVICE_DSP_FW_ALL_OK; + ret = tasdevice_spi_dev_read(tas_priv, TAS2781_REG_CLK_CONFIG, &val); + if (ret < 0) + goto out; + + if (val == TAS2781_REG_CLK_CONFIG_RESET) + ret = tasdevice_spi_prmg_load(tas_priv, 0); + if (ret < 0) { + dev_err(tas_priv->dev, "FW download failed = %d\n", ret); + goto out; + } + if (tas_priv->fmw->nr_programs > 0) + tas_priv->cur_prog = 0; + if (tas_priv->fmw->nr_configurations > 0) + tas_priv->cur_conf = 0; + + /* + * If calibrated data occurs error, dsp will still works with default + * calibrated data inside algo. + */ + +out: + if (fmw) + release_firmware(fmw); + pm_runtime_mark_last_busy(tas_hda->priv->dev); + pm_runtime_put_autosuspend(tas_hda->priv->dev); +} + +static int tas2781_hda_bind(struct device *dev, struct device *master, + void *master_data) +{ + struct tas2781_hda *tas_hda = dev_get_drvdata(dev); + struct hda_component_parent *parent = master_data; + struct hda_component *comp; + struct hda_codec *codec; + int ret; + + comp = hda_component_from_index(parent, tas_hda->priv->index); + if (!comp) + return -EINVAL; + + if (comp->dev) + return -EBUSY; + + codec = parent->codec; + + pm_runtime_get_sync(dev); + + comp->dev = dev; + + strscpy(comp->name, dev_name(dev), sizeof(comp->name)); + + ret = tascodec_spi_init(tas_hda->priv, codec, THIS_MODULE, + tasdev_fw_ready); + if (!ret) + comp->playback_hook = tas2781_hda_playback_hook; + + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + + return ret; +} + +static void tas2781_hda_unbind(struct device *dev, struct device *master, + void *master_data) +{ + struct tas2781_hda *tas_hda = dev_get_drvdata(dev); + struct hda_component_parent *parent = master_data; + struct hda_component *comp; + + comp = hda_component_from_index(parent, tas_hda->priv->index); + if (comp && (comp->dev == dev)) { + comp->dev = NULL; + memset(comp->name, 0, sizeof(comp->name)); + comp->playback_hook = NULL; + } + + tas2781_hda_remove_controls(tas_hda); + + tasdevice_spi_config_info_remove(tas_hda->priv); + tasdevice_spi_dsp_remove(tas_hda->priv); + + tas_hda->priv->fw_state = TASDEVICE_DSP_FW_PENDING; +} + +static const struct component_ops tas2781_hda_comp_ops = { + .bind = tas2781_hda_bind, + .unbind = tas2781_hda_unbind, +}; + +static void tas2781_hda_remove(struct device *dev) +{ + struct tas2781_hda *tas_hda = dev_get_drvdata(dev); + + component_del(tas_hda->priv->dev, &tas2781_hda_comp_ops); + + pm_runtime_get_sync(tas_hda->priv->dev); + pm_runtime_disable(tas_hda->priv->dev); + + pm_runtime_put_noidle(tas_hda->priv->dev); + + mutex_destroy(&tas_hda->priv->codec_lock); +} + +static int tas2781_hda_spi_probe(struct spi_device *spi) +{ + struct tasdevice_priv *tas_priv; + struct tas2781_hda *tas_hda; + const char *device_name; + int ret = 0; + + tas_hda = devm_kzalloc(&spi->dev, sizeof(*tas_hda), GFP_KERNEL); + if (!tas_hda) + return -ENOMEM; + + spi->max_speed_hz = TAS2781_SPI_MAX_FREQ; + + tas_priv = devm_kzalloc(&spi->dev, sizeof(*tas_priv), GFP_KERNEL); + if (!tas_priv) + return -ENOMEM; + tas_priv->dev = &spi->dev; + tas_hda->priv = tas_priv; + tas_priv->regmap = devm_regmap_init_spi(spi, &tasdevice_regmap); + if (IS_ERR(tas_priv->regmap)) { + ret = PTR_ERR(tas_priv->regmap); + dev_err(tas_priv->dev, "Failed to allocate regmap: %d\n", + ret); + return ret; + } + if (strstr(dev_name(&spi->dev), "TXNW2781")) { + device_name = "TXNW2781"; + tas_priv->save_calibration = tas2781_save_calibration; + tas_priv->apply_calibration = tas2781_apply_calib; + } else { + dev_err(tas_priv->dev, "Unmatched spi dev %s\n", + dev_name(&spi->dev)); + return -ENODEV; + } + + tas_priv->irq = spi->irq; + dev_set_drvdata(&spi->dev, tas_hda); + ret = tas2781_read_acpi(tas_hda, device_name, + spi_get_chipselect(spi, 0)); + if (ret) + return dev_err_probe(tas_priv->dev, ret, + "Platform not supported\n"); + + tasdevice_spi_init(tas_priv); + + ret = component_add(tas_priv->dev, &tas2781_hda_comp_ops); + if (ret) { + dev_err(tas_priv->dev, "Register component fail: %d\n", ret); + return ret; + } + + pm_runtime_set_autosuspend_delay(tas_priv->dev, 3000); + pm_runtime_use_autosuspend(tas_priv->dev); + pm_runtime_mark_last_busy(tas_priv->dev); + pm_runtime_set_active(tas_priv->dev); + pm_runtime_get_noresume(tas_priv->dev); + pm_runtime_enable(tas_priv->dev); + + pm_runtime_put_autosuspend(tas_priv->dev); + + return 0; +} + +static void tas2781_hda_spi_remove(struct spi_device *spi) +{ + tas2781_hda_remove(&spi->dev); +} + +static int tas2781_runtime_suspend(struct device *dev) +{ + struct tas2781_hda *tas_hda = dev_get_drvdata(dev); + + guard(mutex)(&tas_hda->priv->codec_lock); + + if (tas_hda->priv->playback_started) + tasdevice_spi_tuning_switch(tas_hda->priv, 1); + + tas_hda->priv->cur_book = -1; + tas_hda->priv->cur_conf = -1; + + return 0; +} + +static int tas2781_runtime_resume(struct device *dev) +{ + struct tas2781_hda *tas_hda = dev_get_drvdata(dev); + + guard(mutex)(&tas_hda->priv->codec_lock); + + if (tas_hda->priv->playback_started) + tasdevice_spi_tuning_switch(tas_hda->priv, 0); + + return 0; +} + +static int tas2781_system_suspend(struct device *dev) +{ + struct tas2781_hda *tas_hda = dev_get_drvdata(dev); + int ret; + + ret = pm_runtime_force_suspend(dev); + if (ret) + return ret; + + /* Shutdown chip before system suspend */ + if (tas_hda->priv->playback_started) + tasdevice_spi_tuning_switch(tas_hda->priv, 1); + + return 0; +} + +static int tas2781_system_resume(struct device *dev) +{ + struct tas2781_hda *tas_hda = dev_get_drvdata(dev); + int ret, val; + + ret = pm_runtime_force_resume(dev); + if (ret) + return ret; + + guard(mutex)(&tas_hda->priv->codec_lock); + ret = tasdevice_spi_dev_read(tas_hda->priv, TAS2781_REG_CLK_CONFIG, + &val); + if (ret < 0) + return ret; + + if (val == TAS2781_REG_CLK_CONFIG_RESET) { + tas_hda->priv->cur_book = -1; + tas_hda->priv->cur_conf = -1; + tas_hda->priv->cur_prog = -1; + + ret = tasdevice_spi_prmg_load(tas_hda->priv, 0); + if (ret < 0) { + dev_err(tas_hda->priv->dev, + "FW download failed = %d\n", ret); + return ret; + } + + if (tas_hda->priv->playback_started) + tasdevice_spi_tuning_switch(tas_hda->priv, 0); + } + + return ret; +} + +static const struct dev_pm_ops tas2781_hda_pm_ops = { + RUNTIME_PM_OPS(tas2781_runtime_suspend, tas2781_runtime_resume, NULL) + SYSTEM_SLEEP_PM_OPS(tas2781_system_suspend, tas2781_system_resume) +}; + +static const struct spi_device_id tas2781_hda_spi_id[] = { + { "tas2781-hda", }, + {} +}; + +static const struct acpi_device_id tas2781_acpi_hda_match[] = { + {"TXNW2781", }, + {} +}; +MODULE_DEVICE_TABLE(acpi, tas2781_acpi_hda_match); + +static struct spi_driver tas2781_hda_spi_driver = { + .driver = { + .name = "tas2781-hda", + .acpi_match_table = tas2781_acpi_hda_match, + .pm = &tas2781_hda_pm_ops, + }, + .id_table = tas2781_hda_spi_id, + .probe = tas2781_hda_spi_probe, + .remove = tas2781_hda_spi_remove, +}; +module_spi_driver(tas2781_hda_spi_driver); + +MODULE_DESCRIPTION("TAS2781 HDA SPI Driver"); +MODULE_AUTHOR("Baojun, Xu, <baojun.xug@ti.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/pci/hda/tas2781_spi_fwlib.c b/sound/pci/hda/tas2781_spi_fwlib.c new file mode 100644 index 000000000000..131d9a77d140 --- /dev/null +++ b/sound/pci/hda/tas2781_spi_fwlib.c @@ -0,0 +1,2006 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// TAS2781 HDA SPI driver +// +// Copyright 2024-2025 Texas Instruments, Inc. +// +// Author: Baojun Xu <baojun.xu@ti.com> + +#include <linux/crc8.h> +#include <linux/firmware.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/types.h> +#include <linux/unaligned.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/tas2781-dsp.h> +#include <sound/tlv.h> + +#include "tas2781-spi.h" + +#define OFFSET_ERROR_BIT BIT(31) + +#define ERROR_PRAM_CRCCHK 0x0000000 +#define ERROR_YRAM_CRCCHK 0x0000001 +#define PPC_DRIVER_CRCCHK 0x00000200 + +#define TAS2781_SA_COEFF_SWAP_REG TASDEVICE_REG(0, 0x35, 0x2c) +#define TAS2781_YRAM_BOOK1 140 +#define TAS2781_YRAM1_PAGE 42 +#define TAS2781_YRAM1_START_REG 88 + +#define TAS2781_YRAM2_START_PAGE 43 +#define TAS2781_YRAM2_END_PAGE 49 +#define TAS2781_YRAM2_START_REG 8 +#define TAS2781_YRAM2_END_REG 127 + +#define TAS2781_YRAM3_PAGE 50 +#define TAS2781_YRAM3_START_REG 8 +#define TAS2781_YRAM3_END_REG 27 + +/* should not include B0_P53_R44-R47 */ +#define TAS2781_YRAM_BOOK2 0 +#define TAS2781_YRAM4_START_PAGE 50 +#define TAS2781_YRAM4_END_PAGE 60 + +#define TAS2781_YRAM5_PAGE 61 +#define TAS2781_YRAM5_START_REG TAS2781_YRAM3_START_REG +#define TAS2781_YRAM5_END_REG TAS2781_YRAM3_END_REG + +#define TASDEVICE_MAXPROGRAM_NUM_KERNEL 5 +#define TASDEVICE_MAXCONFIG_NUM_KERNEL_MULTIPLE_AMPS 64 +#define TASDEVICE_MAXCONFIG_NUM_KERNEL 10 +#define MAIN_ALL_DEVICES_1X 0x01 +#define MAIN_DEVICE_A_1X 0x02 +#define MAIN_DEVICE_B_1X 0x03 +#define MAIN_DEVICE_C_1X 0x04 +#define MAIN_DEVICE_D_1X 0x05 +#define COEFF_DEVICE_A_1X 0x12 +#define COEFF_DEVICE_B_1X 0x13 +#define COEFF_DEVICE_C_1X 0x14 +#define COEFF_DEVICE_D_1X 0x15 +#define PRE_DEVICE_A_1X 0x22 +#define PRE_DEVICE_B_1X 0x23 +#define PRE_DEVICE_C_1X 0x24 +#define PRE_DEVICE_D_1X 0x25 +#define PRE_SOFTWARE_RESET_DEVICE_A 0x41 +#define PRE_SOFTWARE_RESET_DEVICE_B 0x42 +#define PRE_SOFTWARE_RESET_DEVICE_C 0x43 +#define PRE_SOFTWARE_RESET_DEVICE_D 0x44 +#define POST_SOFTWARE_RESET_DEVICE_A 0x45 +#define POST_SOFTWARE_RESET_DEVICE_B 0x46 +#define POST_SOFTWARE_RESET_DEVICE_C 0x47 +#define POST_SOFTWARE_RESET_DEVICE_D 0x48 + +struct tas_crc { + unsigned char offset; + unsigned char len; +}; + +struct blktyp_devidx_map { + unsigned char blktyp; + unsigned char dev_idx; +}; + +/* fixed m68k compiling issue: mapping table can save code field */ +static const struct blktyp_devidx_map ppc3_tas2781_mapping_table[] = { + { MAIN_ALL_DEVICES_1X, 0x80 }, + { MAIN_DEVICE_A_1X, 0x81 }, + { COEFF_DEVICE_A_1X, 0x81 }, + { PRE_DEVICE_A_1X, 0x81 }, + { PRE_SOFTWARE_RESET_DEVICE_A, 0xC1 }, + { POST_SOFTWARE_RESET_DEVICE_A, 0xC1 }, + { MAIN_DEVICE_B_1X, 0x82 }, + { COEFF_DEVICE_B_1X, 0x82 }, + { PRE_DEVICE_B_1X, 0x82 }, + { PRE_SOFTWARE_RESET_DEVICE_B, 0xC2 }, + { POST_SOFTWARE_RESET_DEVICE_B, 0xC2 }, + { MAIN_DEVICE_C_1X, 0x83 }, + { COEFF_DEVICE_C_1X, 0x83 }, + { PRE_DEVICE_C_1X, 0x83 }, + { PRE_SOFTWARE_RESET_DEVICE_C, 0xC3 }, + { POST_SOFTWARE_RESET_DEVICE_C, 0xC3 }, + { MAIN_DEVICE_D_1X, 0x84 }, + { COEFF_DEVICE_D_1X, 0x84 }, + { PRE_DEVICE_D_1X, 0x84 }, + { PRE_SOFTWARE_RESET_DEVICE_D, 0xC4 }, + { POST_SOFTWARE_RESET_DEVICE_D, 0xC4 }, +}; + +static const struct blktyp_devidx_map ppc3_mapping_table[] = { + { MAIN_ALL_DEVICES_1X, 0x80 }, + { MAIN_DEVICE_A_1X, 0x81 }, + { COEFF_DEVICE_A_1X, 0xC1 }, + { PRE_DEVICE_A_1X, 0xC1 }, + { MAIN_DEVICE_B_1X, 0x82 }, + { COEFF_DEVICE_B_1X, 0xC2 }, + { PRE_DEVICE_B_1X, 0xC2 }, + { MAIN_DEVICE_C_1X, 0x83 }, + { COEFF_DEVICE_C_1X, 0xC3 }, + { PRE_DEVICE_C_1X, 0xC3 }, + { MAIN_DEVICE_D_1X, 0x84 }, + { COEFF_DEVICE_D_1X, 0xC4 }, + { PRE_DEVICE_D_1X, 0xC4 }, +}; + +static const struct blktyp_devidx_map non_ppc3_mapping_table[] = { + { MAIN_ALL_DEVICES, 0x80 }, + { MAIN_DEVICE_A, 0x81 }, + { COEFF_DEVICE_A, 0xC1 }, + { PRE_DEVICE_A, 0xC1 }, + { MAIN_DEVICE_B, 0x82 }, + { COEFF_DEVICE_B, 0xC2 }, + { PRE_DEVICE_B, 0xC2 }, + { MAIN_DEVICE_C, 0x83 }, + { COEFF_DEVICE_C, 0xC3 }, + { PRE_DEVICE_C, 0xC3 }, + { MAIN_DEVICE_D, 0x84 }, + { COEFF_DEVICE_D, 0xC4 }, + { PRE_DEVICE_D, 0xC4 }, +}; + +/* + * Device support different configurations for different scene, + * like voice, music, calibration, was write in regbin file. + * Will be stored into tas_priv after regbin was loaded. + */ +static struct tasdevice_config_info *tasdevice_add_config( + struct tasdevice_priv *tas_priv, unsigned char *config_data, + unsigned int config_size, int *status) +{ + struct tasdevice_config_info *cfg_info; + struct tasdev_blk_data **bk_da; + unsigned int config_offset = 0; + unsigned int i; + + /* + * In most projects are many audio cases, such as music, handfree, + * receiver, games, audio-to-haptics, PMIC record, bypass mode, + * portrait, landscape, etc. Even in multiple audios, one or + * two of the chips will work for the special case, such as + * ultrasonic application. In order to support these variable-numbers + * of audio cases, flexible configs have been introduced in the + * DSP firmware. + */ + cfg_info = kzalloc(sizeof(*cfg_info), GFP_KERNEL); + if (!cfg_info) { + *status = -ENOMEM; + return NULL; + } + + if (tas_priv->rcabin.fw_hdr.binary_version_num >= 0x105) { + if ((config_offset + 64) > config_size) { + *status = -EINVAL; + dev_err(tas_priv->dev, "add conf: Out of boundary\n"); + goto config_err; + } + config_offset += 64; + } + + if ((config_offset + 4) > config_size) { + *status = -EINVAL; + dev_err(tas_priv->dev, "add config: Out of boundary\n"); + goto config_err; + } + + /* + * convert data[offset], data[offset + 1], data[offset + 2] and + * data[offset + 3] into host + */ + cfg_info->nblocks = get_unaligned_be32(&config_data[config_offset]); + config_offset += 4; + + /* + * Several kinds of dsp/algorithm firmwares can run on tas2781, + * the number and size of blk are not fixed and different among + * these firmwares. + */ + bk_da = cfg_info->blk_data = kcalloc(cfg_info->nblocks, + sizeof(*bk_da), GFP_KERNEL); + if (!bk_da) { + *status = -ENOMEM; + goto config_err; + } + cfg_info->real_nblocks = 0; + for (i = 0; i < cfg_info->nblocks; i++) { + if (config_offset + 12 > config_size) { + *status = -EINVAL; + dev_err(tas_priv->dev, + "%s: Out of boundary: i = %d nblocks = %u!\n", + __func__, i, cfg_info->nblocks); + goto block_err; + } + bk_da[i] = kzalloc(sizeof(*bk_da[i]), GFP_KERNEL); + if (!bk_da[i]) { + *status = -ENOMEM; + goto block_err; + } + + bk_da[i]->dev_idx = config_data[config_offset]; + config_offset++; + + bk_da[i]->block_type = config_data[config_offset]; + config_offset++; + + bk_da[i]->yram_checksum = + get_unaligned_be16(&config_data[config_offset]); + config_offset += 2; + bk_da[i]->block_size = + get_unaligned_be32(&config_data[config_offset]); + config_offset += 4; + + bk_da[i]->n_subblks = + get_unaligned_be32(&config_data[config_offset]); + + config_offset += 4; + + if (config_offset + bk_da[i]->block_size > config_size) { + *status = -EINVAL; + dev_err(tas_priv->dev, + "%s: Out of boundary: i = %d blks = %u!\n", + __func__, i, cfg_info->nblocks); + goto block_err; + } + /* instead of kzalloc+memcpy */ + bk_da[i]->regdata = kmemdup(&config_data[config_offset], + bk_da[i]->block_size, GFP_KERNEL); + if (!bk_da[i]->regdata) { + *status = -ENOMEM; + i++; + goto block_err; + } + + config_offset += bk_da[i]->block_size; + cfg_info->real_nblocks += 1; + } + + return cfg_info; +block_err: + for (int j = 0; j < i; j++) + kfree(bk_da[j]); + kfree(bk_da); +config_err: + kfree(cfg_info); + return NULL; +} + +/* Regbin file parser function. */ +int tasdevice_spi_rca_parser(void *context, const struct firmware *fmw) +{ + struct tasdevice_priv *tas_priv = context; + struct tasdevice_config_info **cfg_info; + struct tasdevice_rca_hdr *fw_hdr; + struct tasdevice_rca *rca; + unsigned int total_config_sz = 0; + int offset = 0, ret = 0, i; + unsigned char *buf; + + rca = &tas_priv->rcabin; + fw_hdr = &rca->fw_hdr; + if (!fmw || !fmw->data) { + dev_err(tas_priv->dev, "Failed to read %s\n", + tas_priv->rca_binaryname); + tas_priv->fw_state = TASDEVICE_DSP_FW_FAIL; + return -EINVAL; + } + buf = (unsigned char *)fmw->data; + fw_hdr->img_sz = get_unaligned_be32(&buf[offset]); + offset += 4; + if (fw_hdr->img_sz != fmw->size) { + dev_err(tas_priv->dev, + "File size not match, %d %u", (int)fmw->size, + fw_hdr->img_sz); + tas_priv->fw_state = TASDEVICE_DSP_FW_FAIL; + return -EINVAL; + } + + fw_hdr->checksum = get_unaligned_be32(&buf[offset]); + offset += 4; + fw_hdr->binary_version_num = get_unaligned_be32(&buf[offset]); + if (fw_hdr->binary_version_num < 0x103) { + dev_err(tas_priv->dev, "File version 0x%04x is too low", + fw_hdr->binary_version_num); + tas_priv->fw_state = TASDEVICE_DSP_FW_FAIL; + return -EINVAL; + } + offset += 4; + fw_hdr->drv_fw_version = get_unaligned_be32(&buf[offset]); + offset += 8; + fw_hdr->plat_type = buf[offset++]; + fw_hdr->dev_family = buf[offset++]; + fw_hdr->reserve = buf[offset++]; + fw_hdr->ndev = buf[offset++]; + if (offset + TASDEVICE_DEVICE_SUM > fw_hdr->img_sz) { + dev_err(tas_priv->dev, "rca_ready: Out of boundary!\n"); + tas_priv->fw_state = TASDEVICE_DSP_FW_FAIL; + return -EINVAL; + } + + for (i = 0; i < TASDEVICE_DEVICE_SUM; i++, offset++) + fw_hdr->devs[i] = buf[offset]; + + fw_hdr->nconfig = get_unaligned_be32(&buf[offset]); + offset += 4; + + for (i = 0; i < TASDEVICE_CONFIG_SUM; i++) { + fw_hdr->config_size[i] = get_unaligned_be32(&buf[offset]); + offset += 4; + total_config_sz += fw_hdr->config_size[i]; + } + + if (fw_hdr->img_sz - total_config_sz != (unsigned int)offset) { + dev_err(tas_priv->dev, "Bin file err %d - %d != %d!\n", + fw_hdr->img_sz, total_config_sz, (int)offset); + tas_priv->fw_state = TASDEVICE_DSP_FW_FAIL; + return -EINVAL; + } + + cfg_info = kcalloc(fw_hdr->nconfig, sizeof(*cfg_info), GFP_KERNEL); + if (!cfg_info) { + tas_priv->fw_state = TASDEVICE_DSP_FW_FAIL; + return -ENOMEM; + } + rca->cfg_info = cfg_info; + rca->ncfgs = 0; + for (i = 0; i < (int)fw_hdr->nconfig; i++) { + rca->ncfgs += 1; + cfg_info[i] = tasdevice_add_config(tas_priv, &buf[offset], + fw_hdr->config_size[i], &ret); + if (ret) { + tas_priv->fw_state = TASDEVICE_DSP_FW_FAIL; + return ret; + } + offset += (int)fw_hdr->config_size[i]; + } + + return ret; +} + +/* fixed m68k compiling issue: mapping table can save code field */ +static unsigned char map_dev_idx(struct tasdevice_fw *tas_fmw, + struct tasdev_blk *block) +{ + struct blktyp_devidx_map *p = + (struct blktyp_devidx_map *)non_ppc3_mapping_table; + struct tasdevice_dspfw_hdr *fw_hdr = &tas_fmw->fw_hdr; + struct tasdevice_fw_fixed_hdr *fw_fixed_hdr = &fw_hdr->fixed_hdr; + int i, n = ARRAY_SIZE(non_ppc3_mapping_table); + unsigned char dev_idx = 0; + + if (fw_fixed_hdr->ppcver >= PPC3_VERSION_TAS2781) { + p = (struct blktyp_devidx_map *)ppc3_tas2781_mapping_table; + n = ARRAY_SIZE(ppc3_tas2781_mapping_table); + } else if (fw_fixed_hdr->ppcver >= PPC3_VERSION) { + p = (struct blktyp_devidx_map *)ppc3_mapping_table; + n = ARRAY_SIZE(ppc3_mapping_table); + } + + for (i = 0; i < n; i++) { + if (block->type == p[i].blktyp) { + dev_idx = p[i].dev_idx; + break; + } + } + + return dev_idx; +} + +/* Block parser function. */ +static int fw_parse_block_data_kernel(struct tasdevice_fw *tas_fmw, + struct tasdev_blk *block, const struct firmware *fmw, int offset) +{ + const unsigned char *data = fmw->data; + + if (offset + 16 > fmw->size) { + dev_err(tas_fmw->dev, "%s: File Size error\n", __func__); + return -EINVAL; + } + + /* + * Convert data[offset], data[offset + 1], data[offset + 2] and + * data[offset + 3] into host. + */ + block->type = get_unaligned_be32(&data[offset]); + offset += 4; + + block->is_pchksum_present = data[offset++]; + block->pchksum = data[offset++]; + block->is_ychksum_present = data[offset++]; + block->ychksum = data[offset++]; + block->blk_size = get_unaligned_be32(&data[offset]); + offset += 4; + block->nr_subblocks = get_unaligned_be32(&data[offset]); + offset += 4; + + /* + * Fixed m68k compiling issue: + * 1. mapping table can save code field. + * 2. storing the dev_idx as a member of block can reduce unnecessary + * time and system resource comsumption of dev_idx mapping every + * time the block data writing to the dsp. + */ + block->dev_idx = map_dev_idx(tas_fmw, block); + + if (offset + block->blk_size > fmw->size) { + dev_err(tas_fmw->dev, "%s: nSublocks error\n", __func__); + return -EINVAL; + } + /* instead of kzalloc+memcpy */ + block->data = kmemdup(&data[offset], block->blk_size, GFP_KERNEL); + if (!block->data) + return -ENOMEM; + + offset += block->blk_size; + + return offset; +} + +/* Data of block parser function. */ +static int fw_parse_data_kernel(struct tasdevice_fw *tas_fmw, + struct tasdevice_data *img_data, const struct firmware *fmw, + int offset) +{ + const unsigned char *data = fmw->data; + struct tasdev_blk *blk; + unsigned int i; + + if (offset + 4 > fmw->size) { + dev_err(tas_fmw->dev, "%s: File Size error\n", __func__); + return -EINVAL; + } + img_data->nr_blk = get_unaligned_be32(&data[offset]); + offset += 4; + + img_data->dev_blks = kcalloc(img_data->nr_blk, + sizeof(struct tasdev_blk), GFP_KERNEL); + if (!img_data->dev_blks) + return -ENOMEM; + + for (i = 0; i < img_data->nr_blk; i++) { + blk = &img_data->dev_blks[i]; + offset = fw_parse_block_data_kernel( + tas_fmw, blk, fmw, offset); + if (offset < 0) { + kfree(img_data->dev_blks); + return -EINVAL; + } + } + + return offset; +} + +/* Data of DSP program parser function. */ +static int fw_parse_program_data_kernel( + struct tasdevice_priv *tas_priv, struct tasdevice_fw *tas_fmw, + const struct firmware *fmw, int offset) +{ + struct tasdevice_prog *program; + unsigned int i; + + for (i = 0; i < tas_fmw->nr_programs; i++) { + program = &tas_fmw->programs[i]; + if (offset + 72 > fmw->size) { + dev_err(tas_priv->dev, "%s: mpName error\n", __func__); + return -EINVAL; + } + /* skip 72 unused byts */ + offset += 72; + + offset = fw_parse_data_kernel(tas_fmw, &program->dev_data, + fmw, offset); + if (offset < 0) + break; + } + + return offset; +} + +/* Data of DSP configurations parser function. */ +static int fw_parse_configuration_data_kernel(struct tasdevice_priv *tas_priv, + struct tasdevice_fw *tas_fmw, const struct firmware *fmw, int offset) +{ + const unsigned char *data = fmw->data; + struct tasdevice_config *config; + unsigned int i; + + for (i = 0; i < tas_fmw->nr_configurations; i++) { + config = &tas_fmw->configs[i]; + if (offset + 80 > fmw->size) { + dev_err(tas_priv->dev, "%s: mpName error\n", __func__); + return -EINVAL; + } + memcpy(config->name, &data[offset], 64); + /* skip extra 16 bytes */ + offset += 80; + + offset = fw_parse_data_kernel(tas_fmw, &config->dev_data, + fmw, offset); + if (offset < 0) + break; + } + + return offset; +} + +/* DSP firmware file header parser function for early PPC3 firmware binary. */ +static int fw_parse_variable_header_kernel(struct tasdevice_priv *tas_priv, + const struct firmware *fmw, int offset) +{ + struct tasdevice_fw *tas_fmw = tas_priv->fmw; + struct tasdevice_dspfw_hdr *fw_hdr = &tas_fmw->fw_hdr; + struct tasdevice_config *config; + struct tasdevice_prog *program; + const unsigned char *buf = fmw->data; + unsigned short max_confs; + unsigned int i; + + if (offset + 12 + 4 * TASDEVICE_MAXPROGRAM_NUM_KERNEL > fmw->size) { + dev_err(tas_priv->dev, "%s: File Size error\n", __func__); + return -EINVAL; + } + fw_hdr->device_family = get_unaligned_be16(&buf[offset]); + if (fw_hdr->device_family != 0) { + dev_err(tas_priv->dev, "%s:not TAS device\n", __func__); + return -EINVAL; + } + offset += 2; + fw_hdr->device = get_unaligned_be16(&buf[offset]); + if (fw_hdr->device >= TASDEVICE_DSP_TAS_MAX_DEVICE || + fw_hdr->device == 6) { + dev_err(tas_priv->dev, "Unsupported dev %d\n", fw_hdr->device); + return -EINVAL; + } + offset += 2; + + tas_fmw->nr_programs = get_unaligned_be32(&buf[offset]); + offset += 4; + + if (tas_fmw->nr_programs == 0 || + tas_fmw->nr_programs > TASDEVICE_MAXPROGRAM_NUM_KERNEL) { + dev_err(tas_priv->dev, "mnPrograms is invalid\n"); + return -EINVAL; + } + + tas_fmw->programs = kcalloc(tas_fmw->nr_programs, + sizeof(*tas_fmw->programs), GFP_KERNEL); + if (!tas_fmw->programs) + return -ENOMEM; + + for (i = 0; i < tas_fmw->nr_programs; i++) { + program = &tas_fmw->programs[i]; + program->prog_size = get_unaligned_be32(&buf[offset]); + offset += 4; + } + + /* Skip the unused prog_size */ + offset += 4 * (TASDEVICE_MAXPROGRAM_NUM_KERNEL - tas_fmw->nr_programs); + + tas_fmw->nr_configurations = get_unaligned_be32(&buf[offset]); + offset += 4; + + /* + * The max number of config in firmware greater than 4 pieces of + * tas2781s is different from the one lower than 4 pieces of + * tas2781s. + */ + max_confs = TASDEVICE_MAXCONFIG_NUM_KERNEL; + if (tas_fmw->nr_configurations == 0 || + tas_fmw->nr_configurations > max_confs) { + dev_err(tas_priv->dev, "%s: Conf is invalid\n", __func__); + kfree(tas_fmw->programs); + return -EINVAL; + } + + if (offset + 4 * max_confs > fmw->size) { + dev_err(tas_priv->dev, "%s: mpConfigurations err\n", __func__); + kfree(tas_fmw->programs); + return -EINVAL; + } + + tas_fmw->configs = kcalloc(tas_fmw->nr_configurations, + sizeof(*tas_fmw->configs), GFP_KERNEL); + if (!tas_fmw->configs) { + kfree(tas_fmw->programs); + return -ENOMEM; + } + + for (i = 0; i < tas_fmw->nr_programs; i++) { + config = &tas_fmw->configs[i]; + config->cfg_size = get_unaligned_be32(&buf[offset]); + offset += 4; + } + + /* Skip the unused configs */ + offset += 4 * (max_confs - tas_fmw->nr_programs); + + return offset; +} + +/* + * In sub-block data, have three type sub-block: + * 1. Single byte write. + * 2. Multi-byte write. + * 3. Delay. + * 4. Bits update. + * This function perform single byte write to device. + */ +static int tasdevice_single_byte_wr(void *context, int dev_idx, + unsigned char *data, int sublocksize) +{ + struct tasdevice_priv *tas_priv = context; + unsigned short len = get_unaligned_be16(&data[2]); + int i, subblk_offset, rc; + + subblk_offset = 4; + if (subblk_offset + 4 * len > sublocksize) { + dev_err(tas_priv->dev, "process_block: Out of boundary\n"); + return 0; + } + + for (i = 0; i < len; i++) { + if (dev_idx == (tas_priv->index + 1) || dev_idx == 0) { + rc = tasdevice_spi_dev_write(tas_priv, + TASDEVICE_REG(data[subblk_offset], + data[subblk_offset + 1], + data[subblk_offset + 2]), + data[subblk_offset + 3]); + if (rc < 0) { + dev_err(tas_priv->dev, + "process_block: single write error\n"); + subblk_offset |= OFFSET_ERROR_BIT; + } + } + subblk_offset += 4; + } + + return subblk_offset; +} + +/* + * In sub-block data, have three type sub-block: + * 1. Single byte write. + * 2. Multi-byte write. + * 3. Delay. + * 4. Bits update. + * This function perform multi-write to device. + */ +static int tasdevice_burst_wr(void *context, int dev_idx, unsigned char *data, + int sublocksize) +{ + struct tasdevice_priv *tas_priv = context; + unsigned short len = get_unaligned_be16(&data[2]); + int subblk_offset, rc; + + subblk_offset = 4; + if (subblk_offset + 4 + len > sublocksize) { + dev_err(tas_priv->dev, "%s: BST Out of boundary\n", __func__); + subblk_offset |= OFFSET_ERROR_BIT; + } + if (len % 4) { + dev_err(tas_priv->dev, "%s:Bst-len(%u)not div by 4\n", + __func__, len); + subblk_offset |= OFFSET_ERROR_BIT; + } + + if (dev_idx == (tas_priv->index + 1) || dev_idx == 0) { + rc = tasdevice_spi_dev_bulk_write(tas_priv, + TASDEVICE_REG(data[subblk_offset], + data[subblk_offset + 1], + data[subblk_offset + 2]), + &data[subblk_offset + 4], len); + if (rc < 0) { + dev_err(tas_priv->dev, "%s: bulk_write error = %d\n", + __func__, rc); + subblk_offset |= OFFSET_ERROR_BIT; + } + } + subblk_offset += (len + 4); + + return subblk_offset; +} + +/* Just delay for ms.*/ +static int tasdevice_delay(void *context, int dev_idx, unsigned char *data, + int sublocksize) +{ + struct tasdevice_priv *tas_priv = context; + unsigned int sleep_time, subblk_offset = 2; + + if (subblk_offset + 2 > sublocksize) { + dev_err(tas_priv->dev, "%s: delay Out of boundary\n", + __func__); + subblk_offset |= OFFSET_ERROR_BIT; + } + if (dev_idx == (tas_priv->index + 1) || dev_idx == 0) { + sleep_time = get_unaligned_be16(&data[2]) * 1000; + fsleep(sleep_time); + } + subblk_offset += 2; + + return subblk_offset; +} + +/* + * In sub-block data, have three type sub-block: + * 1. Single byte write. + * 2. Multi-byte write. + * 3. Delay. + * 4. Bits update. + * This function perform bits update. + */ +static int tasdevice_field_wr(void *context, int dev_idx, unsigned char *data, + int sublocksize) +{ + struct tasdevice_priv *tas_priv = context; + int rc, subblk_offset = 2; + + if (subblk_offset + 6 > sublocksize) { + dev_err(tas_priv->dev, "%s: bit write Out of boundary\n", + __func__); + subblk_offset |= OFFSET_ERROR_BIT; + } + if (dev_idx == (tas_priv->index + 1) || dev_idx == 0) { + rc = tasdevice_spi_dev_update_bits(tas_priv, + TASDEVICE_REG(data[subblk_offset + 2], + data[subblk_offset + 3], + data[subblk_offset + 4]), + data[subblk_offset + 1], + data[subblk_offset + 5]); + if (rc < 0) { + dev_err(tas_priv->dev, "%s: update_bits error = %d\n", + __func__, rc); + subblk_offset |= OFFSET_ERROR_BIT; + } + } + subblk_offset += 6; + + return subblk_offset; +} + +/* Data block process function. */ +static int tasdevice_process_block(void *context, unsigned char *data, + unsigned char dev_idx, int sublocksize) +{ + struct tasdevice_priv *tas_priv = context; + int blktyp = dev_idx & 0xC0, subblk_offset; + unsigned char subblk_typ = data[1]; + + switch (subblk_typ) { + case TASDEVICE_CMD_SING_W: + subblk_offset = tasdevice_single_byte_wr(tas_priv, + dev_idx & 0x3f, data, sublocksize); + break; + case TASDEVICE_CMD_BURST: + subblk_offset = tasdevice_burst_wr(tas_priv, + dev_idx & 0x3f, data, sublocksize); + break; + case TASDEVICE_CMD_DELAY: + subblk_offset = tasdevice_delay(tas_priv, + dev_idx & 0x3f, data, sublocksize); + break; + case TASDEVICE_CMD_FIELD_W: + subblk_offset = tasdevice_field_wr(tas_priv, + dev_idx & 0x3f, data, sublocksize); + break; + default: + subblk_offset = 2; + break; + } + if (((subblk_offset & OFFSET_ERROR_BIT) != 0) && blktyp != 0) { + if (blktyp == 0x80) { + tas_priv->cur_prog = -1; + tas_priv->cur_conf = -1; + } else + tas_priv->cur_conf = -1; + } + subblk_offset &= ~OFFSET_ERROR_BIT; + + return subblk_offset; +} + +/* + * Device support different configurations for different scene, + * this function was used for choose different config. + */ +void tasdevice_spi_select_cfg_blk(void *pContext, int conf_no, + unsigned char block_type) +{ + struct tasdevice_priv *tas_priv = pContext; + struct tasdevice_rca *rca = &tas_priv->rcabin; + struct tasdevice_config_info **cfg_info = rca->cfg_info; + struct tasdev_blk_data **blk_data; + unsigned int j, k; + + if (conf_no >= rca->ncfgs || conf_no < 0 || !cfg_info) { + dev_err(tas_priv->dev, "conf_no should be not more than %u\n", + rca->ncfgs); + return; + } + blk_data = cfg_info[conf_no]->blk_data; + + for (j = 0; j < cfg_info[conf_no]->real_nblocks; j++) { + unsigned int length = 0, rc = 0; + + if (block_type > 5 || block_type < 2) { + dev_err(tas_priv->dev, + "block_type should be in range from 2 to 5\n"); + break; + } + if (block_type != blk_data[j]->block_type) + continue; + + for (k = 0; k < blk_data[j]->n_subblks; k++) { + tas_priv->is_loading = true; + + rc = tasdevice_process_block(tas_priv, + blk_data[j]->regdata + length, + blk_data[j]->dev_idx, + blk_data[j]->block_size - length); + length += rc; + if (blk_data[j]->block_size < length) { + dev_err(tas_priv->dev, + "%s: %u %u out of boundary\n", + __func__, length, + blk_data[j]->block_size); + break; + } + } + if (length != blk_data[j]->block_size) + dev_err(tas_priv->dev, "%s: %u %u size is not same\n", + __func__, length, blk_data[j]->block_size); + } +} + +/* Block process function. */ +static int tasdevice_load_block_kernel( + struct tasdevice_priv *tasdevice, struct tasdev_blk *block) +{ + const unsigned int blk_size = block->blk_size; + unsigned char *data = block->data; + unsigned int i, length; + + for (i = 0, length = 0; i < block->nr_subblocks; i++) { + int rc = tasdevice_process_block(tasdevice, data + length, + block->dev_idx, blk_size - length); + if (rc < 0) { + dev_err(tasdevice->dev, + "%s: %u %u sublock write error\n", + __func__, length, blk_size); + return rc; + } + length += rc; + if (blk_size < length) { + dev_err(tasdevice->dev, "%s: %u %u out of boundary\n", + __func__, length, blk_size); + rc = -ENOMEM; + return rc; + } + } + + return 0; +} + +/* DSP firmware file header parser function. */ +static int fw_parse_variable_hdr(struct tasdevice_priv *tas_priv, + struct tasdevice_dspfw_hdr *fw_hdr, + const struct firmware *fmw, int offset) +{ + const unsigned char *buf = fmw->data; + int len = strlen((char *)&buf[offset]); + + len++; + + if (offset + len + 8 > fmw->size) { + dev_err(tas_priv->dev, "%s: File Size error\n", __func__); + return -EINVAL; + } + + offset += len; + + fw_hdr->device_family = get_unaligned_be32(&buf[offset]); + if (fw_hdr->device_family != 0) { + dev_err(tas_priv->dev, "%s: not TAS device\n", __func__); + return -EINVAL; + } + offset += 4; + + fw_hdr->device = get_unaligned_be32(&buf[offset]); + if (fw_hdr->device >= TASDEVICE_DSP_TAS_MAX_DEVICE || + fw_hdr->device == 6) { + dev_err(tas_priv->dev, "Unsupported dev %d\n", fw_hdr->device); + return -EINVAL; + } + offset += 4; + fw_hdr->ndev = 1; + + return offset; +} + +/* DSP firmware file header parser function for size variabled header. */ +static int fw_parse_variable_header_git(struct tasdevice_priv + *tas_priv, const struct firmware *fmw, int offset) +{ + struct tasdevice_fw *tas_fmw = tas_priv->fmw; + struct tasdevice_dspfw_hdr *fw_hdr = &tas_fmw->fw_hdr; + + offset = fw_parse_variable_hdr(tas_priv, fw_hdr, fmw, offset); + + return offset; +} + +/* DSP firmware file block parser function. */ +static int fw_parse_block_data(struct tasdevice_fw *tas_fmw, + struct tasdev_blk *block, const struct firmware *fmw, int offset) +{ + unsigned char *data = (unsigned char *)fmw->data; + int n; + + if (offset + 8 > fmw->size) { + dev_err(tas_fmw->dev, "%s: Type error\n", __func__); + return -EINVAL; + } + block->type = get_unaligned_be32(&data[offset]); + offset += 4; + + if (tas_fmw->fw_hdr.fixed_hdr.drv_ver >= PPC_DRIVER_CRCCHK) { + if (offset + 8 > fmw->size) { + dev_err(tas_fmw->dev, "PChkSumPresent error\n"); + return -EINVAL; + } + block->is_pchksum_present = data[offset]; + offset++; + + block->pchksum = data[offset]; + offset++; + + block->is_ychksum_present = data[offset]; + offset++; + + block->ychksum = data[offset]; + offset++; + } else { + block->is_pchksum_present = 0; + block->is_ychksum_present = 0; + } + + block->nr_cmds = get_unaligned_be32(&data[offset]); + offset += 4; + + n = block->nr_cmds * 4; + if (offset + n > fmw->size) { + dev_err(tas_fmw->dev, + "%s: File Size(%lu) error offset = %d n = %d\n", + __func__, (unsigned long)fmw->size, offset, n); + return -EINVAL; + } + /* instead of kzalloc+memcpy */ + block->data = kmemdup(&data[offset], n, GFP_KERNEL); + if (!block->data) + return -ENOMEM; + + offset += n; + + return offset; +} + +/* + * When parsing error occurs, all the memory resource will be released + * in the end of tasdevice_rca_ready. + */ +static int fw_parse_data(struct tasdevice_fw *tas_fmw, + struct tasdevice_data *img_data, const struct firmware *fmw, + int offset) +{ + const unsigned char *data = (unsigned char *)fmw->data; + struct tasdev_blk *blk; + unsigned int i, n; + + if (offset + 64 > fmw->size) { + dev_err(tas_fmw->dev, "%s: Name error\n", __func__); + return -EINVAL; + } + memcpy(img_data->name, &data[offset], 64); + offset += 64; + + n = strlen((char *)&data[offset]); + n++; + if (offset + n + 2 > fmw->size) { + dev_err(tas_fmw->dev, "%s: Description error\n", __func__); + return -EINVAL; + } + offset += n; + img_data->nr_blk = get_unaligned_be16(&data[offset]); + offset += 2; + + img_data->dev_blks = kcalloc(img_data->nr_blk, + sizeof(*img_data->dev_blks), GFP_KERNEL); + if (!img_data->dev_blks) + return -ENOMEM; + + for (i = 0; i < img_data->nr_blk; i++) { + blk = &img_data->dev_blks[i]; + offset = fw_parse_block_data(tas_fmw, blk, fmw, offset); + if (offset < 0) + return -EINVAL; + } + + return offset; +} + +/* + * When parsing error occurs, all the memory resource will be released + * in the end of tasdevice_rca_ready. + */ +static int fw_parse_program_data(struct tasdevice_priv *tas_priv, + struct tasdevice_fw *tas_fmw, const struct firmware *fmw, int offset) +{ + unsigned char *buf = (unsigned char *)fmw->data; + struct tasdevice_prog *program; + int i; + + if (offset + 2 > fmw->size) { + dev_err(tas_priv->dev, "%s: File Size error\n", __func__); + return -EINVAL; + } + tas_fmw->nr_programs = get_unaligned_be16(&buf[offset]); + offset += 2; + + if (tas_fmw->nr_programs == 0) { + /* Not error in calibration Data file, return directly */ + dev_dbg(tas_priv->dev, "%s: No Programs data, maybe calbin\n", + __func__); + return offset; + } + + tas_fmw->programs = + kcalloc(tas_fmw->nr_programs, sizeof(*tas_fmw->programs), + GFP_KERNEL); + if (!tas_fmw->programs) + return -ENOMEM; + + for (i = 0; i < tas_fmw->nr_programs; i++) { + int n = 0; + + program = &tas_fmw->programs[i]; + if (offset + 64 > fmw->size) { + dev_err(tas_priv->dev, "%s: mpName error\n", __func__); + return -EINVAL; + } + offset += 64; + + n = strlen((char *)&buf[offset]); + /* skip '\0' and 5 unused bytes */ + n += 6; + if (offset + n > fmw->size) { + dev_err(tas_priv->dev, "Description err\n"); + return -EINVAL; + } + + offset += n; + + offset = fw_parse_data(tas_fmw, &program->dev_data, fmw, + offset); + if (offset < 0) + return offset; + } + + return offset; +} + +/* + * When parsing error occurs, all the memory resource will be released + * in the end of tasdevice_rca_ready. + */ +static int fw_parse_configuration_data(struct tasdevice_priv *tas_priv, + struct tasdevice_fw *tas_fmw, const struct firmware *fmw, int offset) +{ + unsigned char *data = (unsigned char *)fmw->data; + struct tasdevice_config *config; + unsigned int i, n; + + if (offset + 2 > fmw->size) { + dev_err(tas_priv->dev, "%s: File Size error\n", __func__); + return -EINVAL; + } + tas_fmw->nr_configurations = get_unaligned_be16(&data[offset]); + offset += 2; + + if (tas_fmw->nr_configurations == 0) { + dev_err(tas_priv->dev, "%s: Conf is zero\n", __func__); + /* Not error for calibration Data file, return directly */ + return offset; + } + tas_fmw->configs = kcalloc(tas_fmw->nr_configurations, + sizeof(*tas_fmw->configs), GFP_KERNEL); + if (!tas_fmw->configs) + return -ENOMEM; + for (i = 0; i < tas_fmw->nr_configurations; i++) { + config = &tas_fmw->configs[i]; + if (offset + 64 > fmw->size) { + dev_err(tas_priv->dev, "File Size err\n"); + return -EINVAL; + } + memcpy(config->name, &data[offset], 64); + offset += 64; + + n = strlen((char *)&data[offset]); + n += 15; + if (offset + n > fmw->size) { + dev_err(tas_priv->dev, "Description err\n"); + return -EINVAL; + } + offset += n; + offset = fw_parse_data(tas_fmw, &config->dev_data, + fmw, offset); + if (offset < 0) + break; + } + + return offset; +} + +/* yram5 page check. */ +static bool check_inpage_yram_rg(struct tas_crc *cd, + unsigned char reg, unsigned char len) +{ + bool in = false; + + if (reg <= TAS2781_YRAM5_END_REG && + reg >= TAS2781_YRAM5_START_REG) { + if (reg + len > TAS2781_YRAM5_END_REG) + cd->len = TAS2781_YRAM5_END_REG - reg + 1; + else + cd->len = len; + cd->offset = reg; + in = true; + } else if (reg < TAS2781_YRAM5_START_REG) { + if (reg + len > TAS2781_YRAM5_START_REG) { + cd->offset = TAS2781_YRAM5_START_REG; + cd->len = len - TAS2781_YRAM5_START_REG + reg; + in = true; + } + } + + return in; +} + +/* DSP firmware yram block check. */ +static bool check_inpage_yram_bk1(struct tas_crc *cd, + unsigned char page, unsigned char reg, unsigned char len) +{ + bool in = false; + + if (page == TAS2781_YRAM1_PAGE) { + if (reg >= TAS2781_YRAM1_START_REG) { + cd->offset = reg; + cd->len = len; + in = true; + } else if (reg + len > TAS2781_YRAM1_START_REG) { + cd->offset = TAS2781_YRAM1_START_REG; + cd->len = len - TAS2781_YRAM1_START_REG + reg; + in = true; + } + } else if (page == TAS2781_YRAM3_PAGE) { + in = check_inpage_yram_rg(cd, reg, len); + } + + return in; +} + +/* + * Return Code: + * true -- the registers are in the inpage yram + * false -- the registers are NOT in the inpage yram + */ +static bool check_inpage_yram(struct tas_crc *cd, unsigned char book, + unsigned char page, unsigned char reg, unsigned char len) +{ + bool in = false; + + if (book == TAS2781_YRAM_BOOK1) + in = check_inpage_yram_bk1(cd, page, reg, len); + else if (book == TAS2781_YRAM_BOOK2 && page == TAS2781_YRAM5_PAGE) + in = check_inpage_yram_rg(cd, reg, len); + + return in; +} + +/* yram4 page check. */ +static bool check_inblock_yram_bk(struct tas_crc *cd, + unsigned char page, unsigned char reg, unsigned char len) +{ + bool in = false; + + if ((page >= TAS2781_YRAM4_START_PAGE && + page <= TAS2781_YRAM4_END_PAGE) || + (page >= TAS2781_YRAM2_START_PAGE && + page <= TAS2781_YRAM2_END_PAGE)) { + if (reg <= TAS2781_YRAM2_END_REG && + reg >= TAS2781_YRAM2_START_REG) { + cd->offset = reg; + cd->len = len; + in = true; + } else if (reg < TAS2781_YRAM2_START_REG) { + if (reg + len - 1 >= TAS2781_YRAM2_START_REG) { + cd->offset = TAS2781_YRAM2_START_REG; + cd->len = reg + len - TAS2781_YRAM2_START_REG; + in = true; + } + } + } + + return in; +} + +/* + * Return Code: + * true -- the registers are in the inblock yram + * false -- the registers are NOT in the inblock yram + */ +static bool check_inblock_yram(struct tas_crc *cd, unsigned char book, + unsigned char page, unsigned char reg, unsigned char len) +{ + bool in = false; + + if (book == TAS2781_YRAM_BOOK1 || book == TAS2781_YRAM_BOOK2) + in = check_inblock_yram_bk(cd, page, reg, len); + + return in; +} + +/* yram page check. */ +static bool check_yram(struct tas_crc *cd, unsigned char book, + unsigned char page, unsigned char reg, unsigned char len) +{ + bool in; + + in = check_inpage_yram(cd, book, page, reg, len); + if (!in) + in = check_inblock_yram(cd, book, page, reg, len); + + return in; +} + +/* Checksum for data block. */ +static int tasdev_multibytes_chksum(struct tasdevice_priv *tasdevice, + unsigned char book, unsigned char page, + unsigned char reg, unsigned int len) +{ + struct tas_crc crc_data; + unsigned char crc_chksum = 0; + unsigned char nBuf1[128]; + int ret = 0, i; + bool in; + + if ((reg + len - 1) > 127) { + ret = -EINVAL; + dev_err(tasdevice->dev, "firmware error\n"); + goto end; + } + + if ((book == TASDEVICE_BOOK_ID(TAS2781_SA_COEFF_SWAP_REG)) && + (page == TASDEVICE_PAGE_ID(TAS2781_SA_COEFF_SWAP_REG)) && + (reg == TASDEVICE_REG_ID(TAS2781_SA_COEFF_SWAP_REG)) && + (len == 4)) { + /* DSP swap command, pass */ + ret = 0; + goto end; + } + + in = check_yram(&crc_data, book, page, reg, len); + if (!in) + goto end; + + if (len == 1) { + dev_err(tasdevice->dev, "firmware error\n"); + ret = -EINVAL; + goto end; + } + + ret = tasdevice_spi_dev_bulk_read(tasdevice, + TASDEVICE_REG(book, page, crc_data.offset), + nBuf1, crc_data.len); + if (ret < 0) + goto end; + + for (i = 0; i < crc_data.len; i++) { + if ((book == TASDEVICE_BOOK_ID(TAS2781_SA_COEFF_SWAP_REG)) && + (page == TASDEVICE_PAGE_ID(TAS2781_SA_COEFF_SWAP_REG)) && + ((i + crc_data.offset) >= + TASDEVICE_REG_ID(TAS2781_SA_COEFF_SWAP_REG)) && + ((i + crc_data.offset) <= + (TASDEVICE_REG_ID(TAS2781_SA_COEFF_SWAP_REG) + 4))) + /* DSP swap command, bypass */ + continue; + else + crc_chksum += crc8(tasdevice->crc8_lkp_tbl, &nBuf1[i], + 1, 0); + } + + ret = crc_chksum; + +end: + return ret; +} + +/* Checksum for single register. */ +static int do_singlereg_checksum(struct tasdevice_priv *tasdevice, + unsigned char book, unsigned char page, + unsigned char reg, unsigned char val) +{ + struct tas_crc crc_data; + unsigned int nData1; + int ret = 0; + bool in; + + /* DSP swap command, pass */ + if ((book == TASDEVICE_BOOK_ID(TAS2781_SA_COEFF_SWAP_REG)) && + (page == TASDEVICE_PAGE_ID(TAS2781_SA_COEFF_SWAP_REG)) && + (reg >= TASDEVICE_REG_ID(TAS2781_SA_COEFF_SWAP_REG)) && + (reg <= (TASDEVICE_REG_ID(TAS2781_SA_COEFF_SWAP_REG) + 4))) + return 0; + + in = check_yram(&crc_data, book, page, reg, 1); + if (!in) + return 0; + ret = tasdevice_spi_dev_read(tasdevice, + TASDEVICE_REG(book, page, reg), &nData1); + if (ret < 0) + return ret; + + if (nData1 != val) { + dev_err(tasdevice->dev, + "B[0x%x]P[0x%x]R[0x%x] W[0x%x], R[0x%x]\n", + book, page, reg, val, nData1); + tasdevice->err_code |= ERROR_YRAM_CRCCHK; + return -EAGAIN; + } + + ret = crc8(tasdevice->crc8_lkp_tbl, &val, 1, 0); + + return ret; +} + +/* Block type check. */ +static void set_err_prg_cfg(unsigned int type, struct tasdevice_priv *p) +{ + if ((type == MAIN_ALL_DEVICES) || (type == MAIN_DEVICE_A) || + (type == MAIN_DEVICE_B) || (type == MAIN_DEVICE_C) || + (type == MAIN_DEVICE_D)) + p->cur_prog = -1; + else + p->cur_conf = -1; +} + +/* Checksum for data bytes. */ +static int tasdev_bytes_chksum(struct tasdevice_priv *tas_priv, + struct tasdev_blk *block, unsigned char book, + unsigned char page, unsigned char reg, unsigned int len, + unsigned char val, unsigned char *crc_chksum) +{ + int ret; + + if (len > 1) + ret = tasdev_multibytes_chksum(tas_priv, book, page, reg, + len); + else + ret = do_singlereg_checksum(tas_priv, book, page, reg, val); + + if (ret > 0) { + *crc_chksum += ret; + goto end; + } + + if (ret != -EAGAIN) + goto end; + + block->nr_retry--; + if (block->nr_retry > 0) + goto end; + + set_err_prg_cfg(block->type, tas_priv); + +end: + return ret; +} + +/* Multi-data byte write. */ +static int tasdev_multibytes_wr(struct tasdevice_priv *tas_priv, + struct tasdev_blk *block, unsigned char book, + unsigned char page, unsigned char reg, unsigned char *data, + unsigned int len, unsigned int *nr_cmds, + unsigned char *crc_chksum) +{ + int ret; + + if (len > 1) { + ret = tasdevice_spi_dev_bulk_write(tas_priv, + TASDEVICE_REG(book, page, reg), data + 3, len); + if (ret < 0) + return ret; + if (block->is_ychksum_present) + ret = tasdev_bytes_chksum(tas_priv, block, + book, page, reg, len, 0, crc_chksum); + } else { + ret = tasdevice_spi_dev_write(tas_priv, + TASDEVICE_REG(book, page, reg), data[3]); + if (ret < 0) + return ret; + if (block->is_ychksum_present) + ret = tasdev_bytes_chksum(tas_priv, block, book, + page, reg, 1, data[3], crc_chksum); + } + + if (!block->is_ychksum_present || ret >= 0) { + *nr_cmds += 1; + if (len >= 2) + *nr_cmds += ((len - 2) / 4) + 1; + } + + return ret; +} + +/* Checksum for block. */ +static int tasdev_block_chksum(struct tasdevice_priv *tas_priv, + struct tasdev_blk *block) +{ + unsigned int nr_value; + int ret; + + ret = tasdevice_spi_dev_read(tas_priv, TASDEVICE_CHECKSUM, &nr_value); + if (ret < 0) { + dev_err(tas_priv->dev, "%s: read error %d.\n", __func__, ret); + set_err_prg_cfg(block->type, tas_priv); + return ret; + } + + if ((nr_value & 0xff) != block->pchksum) { + dev_err(tas_priv->dev, "%s: PChkSum err %d ", __func__, ret); + dev_err(tas_priv->dev, "PChkSum = 0x%x, Reg = 0x%x\n", + block->pchksum, (nr_value & 0xff)); + tas_priv->err_code |= ERROR_PRAM_CRCCHK; + ret = -EAGAIN; + block->nr_retry--; + + if (block->nr_retry <= 0) + set_err_prg_cfg(block->type, tas_priv); + } else { + tas_priv->err_code &= ~ERROR_PRAM_CRCCHK; + } + + return ret; +} + +/* Firmware block load function. */ +static int tasdev_load_blk(struct tasdevice_priv *tas_priv, + struct tasdev_blk *block) +{ + unsigned int sleep_time, len, nr_cmds; + unsigned char offset, book, page, val; + unsigned char *data = block->data; + unsigned char crc_chksum = 0; + int ret = 0; + + while (block->nr_retry > 0) { + if (block->is_pchksum_present) { + ret = tasdevice_spi_dev_write(tas_priv, + TASDEVICE_CHECKSUM, 0); + if (ret < 0) + break; + } + + if (block->is_ychksum_present) + crc_chksum = 0; + + nr_cmds = 0; + + while (nr_cmds < block->nr_cmds) { + data = block->data + nr_cmds * 4; + + book = data[0]; + page = data[1]; + offset = data[2]; + val = data[3]; + + nr_cmds++; + /* Single byte write */ + if (offset <= 0x7F) { + ret = tasdevice_spi_dev_write(tas_priv, + TASDEVICE_REG(book, page, offset), + val); + if (ret < 0) + break; + if (block->is_ychksum_present) { + ret = tasdev_bytes_chksum(tas_priv, + block, book, page, offset, + 1, val, &crc_chksum); + if (ret < 0) + break; + } + continue; + } + /* sleep command */ + if (offset == 0x81) { + /* book -- data[0] page -- data[1] */ + sleep_time = ((book << 8) + page)*1000; + fsleep(sleep_time); + continue; + } + /* Multiple bytes write */ + if (offset == 0x85) { + data += 4; + len = (book << 8) + page; + book = data[0]; + page = data[1]; + offset = data[2]; + ret = tasdev_multibytes_wr(tas_priv, + block, book, page, offset, data, + len, &nr_cmds, &crc_chksum); + if (ret < 0) + break; + } + } + if (ret == -EAGAIN) { + if (block->nr_retry > 0) + continue; + } else if (ret < 0) { + /* err in current device, skip it */ + break; + } + + if (block->is_pchksum_present) { + ret = tasdev_block_chksum(tas_priv, block); + if (ret == -EAGAIN) { + if (block->nr_retry > 0) + continue; + } else if (ret < 0) { + /* err in current device, skip it */ + break; + } + } + + if (block->is_ychksum_present) { + /* TBD, open it when FW ready */ + dev_err(tas_priv->dev, + "Blk YChkSum: FW = 0x%x, YCRC = 0x%x\n", + block->ychksum, crc_chksum); + + tas_priv->err_code &= + ~ERROR_YRAM_CRCCHK; + ret = 0; + } + /* skip current blk */ + break; + } + + return ret; +} + +/* Firmware block load function. */ +static int tasdevice_load_block(struct tasdevice_priv *tas_priv, + struct tasdev_blk *block) +{ + int ret = 0; + + block->nr_retry = 6; + if (tas_priv->is_loading == false) + return 0; + ret = tasdev_load_blk(tas_priv, block); + if (ret < 0) + dev_err(tas_priv->dev, "Blk (%d) load error\n", block->type); + + return ret; +} + +/* + * Select firmware binary parser & load callback functions by ppc3 version + * and firmware binary version. + */ +static int dspfw_default_callback(struct tasdevice_priv *tas_priv, + unsigned int drv_ver, unsigned int ppcver) +{ + int rc = 0; + + if (drv_ver == 0x100) { + if (ppcver >= PPC3_VERSION) { + tas_priv->fw_parse_variable_header = + fw_parse_variable_header_kernel; + tas_priv->fw_parse_program_data = + fw_parse_program_data_kernel; + tas_priv->fw_parse_configuration_data = + fw_parse_configuration_data_kernel; + tas_priv->tasdevice_load_block = + tasdevice_load_block_kernel; + } else if (ppcver == 0x00) { + tas_priv->fw_parse_variable_header = + fw_parse_variable_header_git; + tas_priv->fw_parse_program_data = + fw_parse_program_data; + tas_priv->fw_parse_configuration_data = + fw_parse_configuration_data; + tas_priv->tasdevice_load_block = + tasdevice_load_block; + } else { + dev_err(tas_priv->dev, + "Wrong PPCVer :0x%08x\n", ppcver); + rc = -EINVAL; + } + } else { + dev_err(tas_priv->dev, "Wrong DrvVer : 0x%02x\n", drv_ver); + rc = -EINVAL; + } + + return rc; +} + +/* DSP firmware binary file header parser function. */ +static int fw_parse_header(struct tasdevice_priv *tas_priv, + struct tasdevice_fw *tas_fmw, const struct firmware *fmw, int offset) +{ + struct tasdevice_dspfw_hdr *fw_hdr = &tas_fmw->fw_hdr; + struct tasdevice_fw_fixed_hdr *fw_fixed_hdr = &fw_hdr->fixed_hdr; + static const unsigned char magic_number[] = {0x35, 0x35, 0x35, 0x32, }; + const unsigned char *buf = (unsigned char *)fmw->data; + + if (offset + 92 > fmw->size) { + dev_err(tas_priv->dev, "%s: File Size error\n", __func__); + offset = -EINVAL; + goto out; + } + if (memcmp(&buf[offset], magic_number, 4)) { + dev_err(tas_priv->dev, "%s: Magic num NOT match\n", __func__); + offset = -EINVAL; + goto out; + } + offset += 4; + + /* + * Convert data[offset], data[offset + 1], data[offset + 2] and + * data[offset + 3] into host + */ + fw_fixed_hdr->fwsize = get_unaligned_be32(&buf[offset]); + offset += 4; + if (fw_fixed_hdr->fwsize != fmw->size) { + dev_err(tas_priv->dev, "File size not match, %lu %u", + (unsigned long)fmw->size, fw_fixed_hdr->fwsize); + offset = -EINVAL; + goto out; + } + offset += 4; + fw_fixed_hdr->ppcver = get_unaligned_be32(&buf[offset]); + offset += 8; + fw_fixed_hdr->drv_ver = get_unaligned_be32(&buf[offset]); + offset += 72; + +out: + return offset; +} + +/* DSP firmware binary file parser function. */ +static int tasdevice_dspfw_ready(const struct firmware *fmw, void *context) +{ + struct tasdevice_priv *tas_priv = context; + struct tasdevice_fw_fixed_hdr *fw_fixed_hdr; + struct tasdevice_fw *tas_fmw; + int offset = 0, ret = 0; + + if (!fmw || !fmw->data) { + dev_err(tas_priv->dev, "%s: Failed to read firmware %s\n", + __func__, tas_priv->coef_binaryname); + return -EINVAL; + } + + tas_priv->fmw = kzalloc(sizeof(*tas_priv->fmw), GFP_KERNEL); + if (!tas_priv->fmw) + return -ENOMEM; + tas_fmw = tas_priv->fmw; + tas_fmw->dev = tas_priv->dev; + offset = fw_parse_header(tas_priv, tas_fmw, fmw, offset); + + if (offset == -EINVAL) + return -EINVAL; + + fw_fixed_hdr = &tas_fmw->fw_hdr.fixed_hdr; + /* Support different versions of firmware */ + switch (fw_fixed_hdr->drv_ver) { + case 0x301: + case 0x302: + case 0x502: + case 0x503: + tas_priv->fw_parse_variable_header = + fw_parse_variable_header_kernel; + tas_priv->fw_parse_program_data = + fw_parse_program_data_kernel; + tas_priv->fw_parse_configuration_data = + fw_parse_configuration_data_kernel; + tas_priv->tasdevice_load_block = + tasdevice_load_block_kernel; + break; + case 0x202: + case 0x400: + tas_priv->fw_parse_variable_header = + fw_parse_variable_header_git; + tas_priv->fw_parse_program_data = + fw_parse_program_data; + tas_priv->fw_parse_configuration_data = + fw_parse_configuration_data; + tas_priv->tasdevice_load_block = + tasdevice_load_block; + break; + default: + ret = dspfw_default_callback(tas_priv, + fw_fixed_hdr->drv_ver, fw_fixed_hdr->ppcver); + if (ret) + return ret; + break; + } + + offset = tas_priv->fw_parse_variable_header(tas_priv, fmw, offset); + if (offset < 0) + return offset; + + offset = tas_priv->fw_parse_program_data(tas_priv, tas_fmw, fmw, + offset); + if (offset < 0) + return offset; + + offset = tas_priv->fw_parse_configuration_data(tas_priv, + tas_fmw, fmw, offset); + if (offset < 0) + ret = offset; + + return ret; +} + +/* DSP firmware binary file parser function. */ +int tasdevice_spi_dsp_parser(void *context) +{ + struct tasdevice_priv *tas_priv = context; + const struct firmware *fw_entry; + int ret; + + ret = request_firmware(&fw_entry, tas_priv->coef_binaryname, + tas_priv->dev); + if (ret) { + dev_err(tas_priv->dev, "%s: load %s error\n", __func__, + tas_priv->coef_binaryname); + return ret; + } + + ret = tasdevice_dspfw_ready(fw_entry, tas_priv); + release_firmware(fw_entry); + fw_entry = NULL; + + return ret; +} + +/* DSP firmware program block data remove function. */ +static void tasdev_dsp_prog_blk_remove(struct tasdevice_prog *prog) +{ + struct tasdevice_data *tas_dt; + struct tasdev_blk *blk; + unsigned int i; + + if (!prog) + return; + + tas_dt = &prog->dev_data; + + if (!tas_dt->dev_blks) + return; + + for (i = 0; i < tas_dt->nr_blk; i++) { + blk = &tas_dt->dev_blks[i]; + kfree(blk->data); + } + kfree(tas_dt->dev_blks); +} + +/* DSP firmware program block data remove function. */ +static void tasdev_dsp_prog_remove(struct tasdevice_prog *prog, + unsigned short nr) +{ + int i; + + for (i = 0; i < nr; i++) + tasdev_dsp_prog_blk_remove(&prog[i]); + kfree(prog); +} + +/* DSP firmware config block data remove function. */ +static void tasdev_dsp_cfg_blk_remove(struct tasdevice_config *cfg) +{ + struct tasdevice_data *tas_dt; + struct tasdev_blk *blk; + unsigned int i; + + if (cfg) { + tas_dt = &cfg->dev_data; + + if (!tas_dt->dev_blks) + return; + + for (i = 0; i < tas_dt->nr_blk; i++) { + blk = &tas_dt->dev_blks[i]; + kfree(blk->data); + } + kfree(tas_dt->dev_blks); + } +} + +/* DSP firmware config remove function. */ +static void tasdev_dsp_cfg_remove(struct tasdevice_config *config, + unsigned short nr) +{ + int i; + + for (i = 0; i < nr; i++) + tasdev_dsp_cfg_blk_remove(&config[i]); + kfree(config); +} + +/* DSP firmware remove function. */ +void tasdevice_spi_dsp_remove(void *context) +{ + struct tasdevice_priv *tas_dev = context; + + if (!tas_dev->fmw) + return; + + if (tas_dev->fmw->programs) + tasdev_dsp_prog_remove(tas_dev->fmw->programs, + tas_dev->fmw->nr_programs); + if (tas_dev->fmw->configs) + tasdev_dsp_cfg_remove(tas_dev->fmw->configs, + tas_dev->fmw->nr_configurations); + kfree(tas_dev->fmw); + tas_dev->fmw = NULL; +} + +/* DSP firmware calibration data remove function. */ +static void tas2781_clear_calfirmware(struct tasdevice_fw *tas_fmw) +{ + struct tasdevice_calibration *calibration; + struct tasdev_blk *block; + unsigned int blks; + int i; + + if (!tas_fmw->calibrations) + goto out; + + for (i = 0; i < tas_fmw->nr_calibrations; i++) { + calibration = &tas_fmw->calibrations[i]; + if (!calibration) + continue; + + if (!calibration->dev_data.dev_blks) + continue; + + for (blks = 0; blks < calibration->dev_data.nr_blk; blks++) { + block = &calibration->dev_data.dev_blks[blks]; + if (!block) + continue; + kfree(block->data); + } + kfree(calibration->dev_data.dev_blks); + } + kfree(tas_fmw->calibrations); +out: + kfree(tas_fmw); +} + +/* Calibration data from firmware remove function. */ +void tasdevice_spi_calbin_remove(void *context) +{ + struct tasdevice_priv *tas_priv = context; + + if (tas_priv->cali_data_fmw) { + tas2781_clear_calfirmware(tas_priv->cali_data_fmw); + tas_priv->cali_data_fmw = NULL; + } +} + +/* Configuration remove function. */ +void tasdevice_spi_config_info_remove(void *context) +{ + struct tasdevice_priv *tas_priv = context; + struct tasdevice_rca *rca = &tas_priv->rcabin; + struct tasdevice_config_info **ci = rca->cfg_info; + unsigned int i, j; + + if (!ci) + return; + for (i = 0; i < rca->ncfgs; i++) { + if (!ci[i]) + continue; + if (ci[i]->blk_data) { + for (j = 0; j < ci[i]->real_nblocks; j++) { + if (!ci[i]->blk_data[j]) + continue; + kfree(ci[i]->blk_data[j]->regdata); + kfree(ci[i]->blk_data[j]); + } + kfree(ci[i]->blk_data); + } + kfree(ci[i]); + } + kfree(ci); +} + +/* DSP firmware program block data load function. */ +static int tasdevice_load_data(struct tasdevice_priv *tas_priv, + struct tasdevice_data *dev_data) +{ + struct tasdev_blk *block; + unsigned int i; + int ret = 0; + + for (i = 0; i < dev_data->nr_blk; i++) { + block = &dev_data->dev_blks[i]; + ret = tas_priv->tasdevice_load_block(tas_priv, block); + if (ret < 0) + break; + } + + return ret; +} + +/* DSP firmware program load interface function. */ +int tasdevice_spi_prmg_load(void *context, int prm_no) +{ + struct tasdevice_priv *tas_priv = context; + struct tasdevice_fw *tas_fmw = tas_priv->fmw; + struct tasdevice_prog *program; + struct tasdevice_config *conf; + int ret = 0; + + if (!tas_fmw) { + dev_err(tas_priv->dev, "%s: Firmware is NULL\n", __func__); + return -EINVAL; + } + if (prm_no >= 0 && prm_no <= tas_fmw->nr_programs) { + tas_priv->cur_conf = 0; + tas_priv->is_loading = true; + program = &tas_fmw->programs[prm_no]; + ret = tasdevice_load_data(tas_priv, &program->dev_data); + if (ret < 0) { + dev_err(tas_priv->dev, "Program failed %d.\n", ret); + return ret; + } + tas_priv->cur_prog = prm_no; + + conf = &tas_fmw->configs[tas_priv->cur_conf]; + ret = tasdevice_load_data(tas_priv, &conf->dev_data); + if (ret < 0) + dev_err(tas_priv->dev, "Config failed %d.\n", ret); + } else { + dev_err(tas_priv->dev, + "%s: prm(%d) is not in range of Programs %u\n", + __func__, prm_no, tas_fmw->nr_programs); + return -EINVAL; + } + + return ret; +} + +/* RCABIN configuration switch interface function. */ +void tasdevice_spi_tuning_switch(void *context, int state) +{ + struct tasdevice_priv *tas_priv = context; + int profile_cfg_id = tas_priv->rcabin.profile_cfg_id; + + if (tas_priv->fw_state == TASDEVICE_DSP_FW_FAIL) { + dev_err(tas_priv->dev, "DSP bin file not loaded\n"); + return; + } + + if (state == 0) + tasdevice_spi_select_cfg_blk(tas_priv, profile_cfg_id, + TASDEVICE_BIN_BLK_PRE_POWER_UP); + else + tasdevice_spi_select_cfg_blk(tas_priv, profile_cfg_id, + TASDEVICE_BIN_BLK_PRE_SHUTDOWN); +} diff --git a/sound/pci/ice1712/Makefile b/sound/pci/ice1712/Makefile index 1196f22a9b45..f406a048374c 100644 --- a/sound/pci/ice1712/Makefile +++ b/sound/pci/ice1712/Makefile @@ -4,9 +4,9 @@ # Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz> # -snd-ice17xx-ak4xxx-objs := ak4xxx.o -snd-ice1712-objs := ice1712.o delta.o hoontech.o ews.o -snd-ice1724-objs := ice1724.o amp.o revo.o aureon.o vt1720_mobo.o pontis.o prodigy192.o prodigy_hifi.o juli.o phase.o wtm.o se.o maya44.o quartet.o psc724.o wm8766.o wm8776.o +snd-ice17xx-ak4xxx-y := ak4xxx.o +snd-ice1712-y := ice1712.o delta.o hoontech.o ews.o +snd-ice1724-y := ice1724.o amp.o revo.o aureon.o vt1720_mobo.o pontis.o prodigy192.o prodigy_hifi.o juli.o phase.o wtm.o se.o maya44.o quartet.o psc724.o wm8766.o wm8776.o # Toplevel Module Dependency obj-$(CONFIG_SND_ICE1712) += snd-ice1712.o snd-ice17xx-ak4xxx.o diff --git a/sound/pci/ice1712/prodigy192.c b/sound/pci/ice1712/prodigy192.c index 096ec76f5304..a12dafbf53ab 100644 --- a/sound/pci/ice1712/prodigy192.c +++ b/sound/pci/ice1712/prodigy192.c @@ -170,14 +170,9 @@ static int stac9460_dac_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_el tmp = stac9460_get(ice, idx); ovol = 0x7f - (tmp & 0x7f); change = (ovol != nvol); - if (change) { - ovol = (0x7f - nvol) | (tmp & 0x80); - /* - dev_dbg(ice->card->dev, "DAC Volume: reg 0x%02x: 0x%02x\n", - idx, ovol); - */ + if (change) stac9460_put(ice, idx, (0x7f - nvol) | (tmp & 0x80)); - } + return change; } diff --git a/sound/pci/intel8x0.c b/sound/pci/intel8x0.c index ae285c0a629c..e4bb99f71c2c 100644 --- a/sound/pci/intel8x0.c +++ b/sound/pci/intel8x0.c @@ -703,7 +703,6 @@ static inline void snd_intel8x0_update(struct intel8x0 *chip, struct ichdev *ich if (!(status & ICH_BCIS)) { step = 0; } else if (civ == ichdev->civ) { - // snd_printd("civ same %d\n", civ); step = 1; ichdev->civ++; ichdev->civ &= ICH_REG_LVI_MASK; @@ -711,8 +710,6 @@ static inline void snd_intel8x0_update(struct intel8x0 *chip, struct ichdev *ich step = civ - ichdev->civ; if (step < 0) step += ICH_REG_LVI_MASK + 1; - // if (step != 1) - // snd_printd("step = %d, %d -> %d\n", step, ichdev->civ, civ); ichdev->civ = civ; } @@ -2555,7 +2552,6 @@ static void snd_intel8x0_free(struct snd_card *card) free_irq(chip->irq, chip); } -#ifdef CONFIG_PM_SLEEP /* * power management */ @@ -2628,11 +2624,7 @@ static int intel8x0_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(intel8x0_pm, intel8x0_suspend, intel8x0_resume); -#define INTEL8X0_PM_OPS &intel8x0_pm -#else -#define INTEL8X0_PM_OPS NULL -#endif /* CONFIG_PM_SLEEP */ +static DEFINE_SIMPLE_DEV_PM_OPS(intel8x0_pm, intel8x0_suspend, intel8x0_resume); #define INTEL8X0_TESTBUF_SIZE 32768 /* enough large for one shot */ @@ -3200,7 +3192,7 @@ static struct pci_driver intel8x0_driver = { .id_table = snd_intel8x0_ids, .probe = snd_intel8x0_probe, .driver = { - .pm = INTEL8X0_PM_OPS, + .pm = &intel8x0_pm, }, }; diff --git a/sound/pci/intel8x0m.c b/sound/pci/intel8x0m.c index 653ecca78238..38f8de51d641 100644 --- a/sound/pci/intel8x0m.c +++ b/sound/pci/intel8x0m.c @@ -423,7 +423,6 @@ static inline void snd_intel8x0m_update(struct intel8x0m *chip, struct ichdev *i civ = igetbyte(chip, port + ICH_REG_OFF_CIV); if (civ == ichdev->civ) { - // snd_printd("civ same %d\n", civ); step = 1; ichdev->civ++; ichdev->civ &= ICH_REG_LVI_MASK; @@ -431,8 +430,6 @@ static inline void snd_intel8x0m_update(struct intel8x0m *chip, struct ichdev *i step = civ - ichdev->civ; if (step < 0) step += ICH_REG_LVI_MASK + 1; - // if (step != 1) - // snd_printd("step = %d, %d -> %d\n", step, ichdev->civ, civ); ichdev->civ = civ; } @@ -965,7 +962,6 @@ static void snd_intel8x0m_free(struct snd_card *card) free_irq(chip->irq, chip); } -#ifdef CONFIG_PM_SLEEP /* * power management */ @@ -1006,11 +1002,7 @@ static int intel8x0m_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(intel8x0m_pm, intel8x0m_suspend, intel8x0m_resume); -#define INTEL8X0M_PM_OPS &intel8x0m_pm -#else -#define INTEL8X0M_PM_OPS NULL -#endif /* CONFIG_PM_SLEEP */ +static DEFINE_SIMPLE_DEV_PM_OPS(intel8x0m_pm, intel8x0m_suspend, intel8x0m_resume); static void snd_intel8x0m_proc_read(struct snd_info_entry * entry, struct snd_info_buffer *buffer) @@ -1236,7 +1228,7 @@ static struct pci_driver intel8x0m_driver = { .id_table = snd_intel8x0m_ids, .probe = snd_intel8x0m_probe, .driver = { - .pm = INTEL8X0M_PM_OPS, + .pm = &intel8x0m_pm, }, }; diff --git a/sound/pci/korg1212/Makefile b/sound/pci/korg1212/Makefile index 42eb287c77af..ab0186ffbd58 100644 --- a/sound/pci/korg1212/Makefile +++ b/sound/pci/korg1212/Makefile @@ -4,7 +4,7 @@ # Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz> # -snd-korg1212-objs := korg1212.o +snd-korg1212-y := korg1212.o # Toplevel Module Dependency obj-$(CONFIG_SND_KORG1212) += snd-korg1212.o diff --git a/sound/pci/korg1212/korg1212.c b/sound/pci/korg1212/korg1212.c index 5c2cac201a28..49b71082c485 100644 --- a/sound/pci/korg1212/korg1212.c +++ b/sound/pci/korg1212/korg1212.c @@ -28,14 +28,14 @@ // ---------------------------------------------------------------------------- #define K1212_DEBUG_LEVEL 0 #if K1212_DEBUG_LEVEL > 0 -#define K1212_DEBUG_PRINTK(fmt,args...) printk(KERN_DEBUG fmt,##args) +#define K1212_DEBUG_PRINTK(fmt, args...) pr_debug(fmt, ##args) #else -#define K1212_DEBUG_PRINTK(fmt,...) do { } while (0) +#define K1212_DEBUG_PRINTK(fmt, ...) do { } while (0) #endif #if K1212_DEBUG_LEVEL > 1 -#define K1212_DEBUG_PRINTK_VERBOSE(fmt,args...) printk(KERN_DEBUG fmt,##args) +#define K1212_DEBUG_PRINTK_VERBOSE(fmt, args...) pr_debug(fmt, ##args) #else -#define K1212_DEBUG_PRINTK_VERBOSE(fmt,...) +#define K1212_DEBUG_PRINTK_VERBOSE(fmt, ...) #endif // ---------------------------------------------------------------------------- @@ -602,7 +602,7 @@ static void snd_korg1212_timer_func(struct timer_list *t) /* reprogram timer */ mod_timer(&korg1212->timer, jiffies + 1); } else { - snd_printd("korg1212_timer_func timeout\n"); + dev_dbg(korg1212->card->dev, "korg1212_timer_func timeout\n"); korg1212->sharedBufferPtr->cardCommand = 0; korg1212->dsp_stop_is_processed = 1; wake_up(&korg1212->wait); @@ -1131,7 +1131,7 @@ static irqreturn_t snd_korg1212_interrupt(int irq, void *dev_id) K1212_DEBUG_PRINTK_VERBOSE("K1212_DEBUG: IRQ DMAE count - %ld, %x, [%s].\n", korg1212->irqcount, doorbellValue, stateName[korg1212->cardState]); - snd_printk(KERN_ERR "korg1212: DMA Error\n"); + dev_err(korg1212->card->dev, "korg1212: DMA Error\n"); korg1212->errorcnt++; korg1212->totalerrorcnt++; korg1212->sharedBufferPtr->cardCommand = 0; @@ -1272,8 +1272,8 @@ static int snd_korg1212_silence(struct snd_korg1212 *korg1212, int pos, int coun #if K1212_DEBUG_LEVEL > 0 if ( (void *) dst < (void *) korg1212->playDataBufsPtr || (void *) dst > (void *) korg1212->playDataBufsPtr[8].bufferData ) { - printk(KERN_DEBUG "K1212_DEBUG: snd_korg1212_silence KERNEL EFAULT dst=%p iter=%d\n", - dst, i); + pr_debug("K1212_DEBUG: %s KERNEL EFAULT dst=%p iter=%d\n", + __func__, dst, i); return -EFAULT; } #endif @@ -1305,7 +1305,8 @@ static int snd_korg1212_copy_to(struct snd_pcm_substream *substream, #if K1212_DEBUG_LEVEL > 0 if ( (void *) src < (void *) korg1212->recordDataBufsPtr || (void *) src > (void *) korg1212->recordDataBufsPtr[8].bufferData ) { - printk(KERN_DEBUG "K1212_DEBUG: snd_korg1212_copy_to KERNEL EFAULT, src=%p dst=%p iter=%d\n", src, dst->kvec.iov_base, i); + pr_debug("K1212_DEBUG: %s KERNEL EFAULT, src=%p dst=%p iter=%d\n", + __func__, src, dst->kvec.iov_base, i); return -EFAULT; } #endif @@ -1330,8 +1331,8 @@ static int snd_korg1212_copy_from(struct snd_pcm_substream *substream, size = korg1212->channels * 2; dst = korg1212->playDataBufsPtr[0].bufferData + pos; - K1212_DEBUG_PRINTK_VERBOSE("K1212_DEBUG: snd_korg1212_copy_from pos=%d size=%d count=%d\n", - pos, size, count); + K1212_DEBUG_PRINTK_VERBOSE("K1212_DEBUG: %s pos=%d size=%d count=%d\n", + __func__, pos, size, count); if (snd_BUG_ON(pos + count > K1212_MAX_SAMPLES)) return -EINVAL; @@ -1340,7 +1341,8 @@ static int snd_korg1212_copy_from(struct snd_pcm_substream *substream, #if K1212_DEBUG_LEVEL > 0 if ( (void *) dst < (void *) korg1212->playDataBufsPtr || (void *) dst > (void *) korg1212->playDataBufsPtr[8].bufferData ) { - printk(KERN_DEBUG "K1212_DEBUG: snd_korg1212_copy_from KERNEL EFAULT, src=%p dst=%p iter=%d\n", src->kvec.iov_base, dst, i); + pr_debug("K1212_DEBUG: %s KERNEL EFAULT, src=%p dst=%p iter=%d\n", + __func__, src->kvec.iov_base, dst, i); return -EFAULT; } #endif @@ -2106,7 +2108,7 @@ static int snd_korg1212_create(struct snd_card *card, struct pci_dev *pci) for (i=0; i<kAudioChannels; i++) korg1212->volumePhase[i] = 0; - err = pcim_iomap_regions_request_all(pci, 1 << 0, "korg1212"); + err = pcim_request_all_regions(pci, "korg1212"); if (err < 0) return err; @@ -2128,14 +2130,16 @@ static int snd_korg1212_create(struct snd_card *card, struct pci_dev *pci) korg1212->iomem2, iomem2_size, stateName[korg1212->cardState]); - korg1212->iobase = pcim_iomap_table(pci)[0]; + korg1212->iobase = pcim_iomap(pci, 0, 0); + if (!korg1212->iobase) + return -ENOMEM; err = devm_request_irq(&pci->dev, pci->irq, snd_korg1212_interrupt, IRQF_SHARED, KBUILD_MODNAME, korg1212); if (err) { - snd_printk(KERN_ERR "korg1212: unable to grab IRQ %d\n", pci->irq); + dev_err(&pci->dev, "korg1212: unable to grab IRQ %d\n", pci->irq); return -EBUSY; } @@ -2232,7 +2236,7 @@ static int snd_korg1212_create(struct snd_card *card, struct pci_dev *pci) err = request_firmware(&dsp_code, "korg/k1212.dsp", &pci->dev); if (err < 0) { - snd_printk(KERN_ERR "firmware not available\n"); + dev_err(&pci->dev, "firmware not available\n"); return err; } diff --git a/sound/pci/lola/lola_clock.c b/sound/pci/lola/lola_clock.c index cafd30e30913..2e73fbf335ed 100644 --- a/sound/pci/lola/lola_clock.c +++ b/sound/pci/lola/lola_clock.c @@ -35,7 +35,7 @@ unsigned int lola_sample_rate_convert(unsigned int coded) default: return 0; /* error */ } - /* ajustement */ + /* adjustement */ switch (coded & 0x60) { case (0 << 5): break; case (1 << 5): freq = (freq * 999) / 1000; break; diff --git a/sound/pci/lx6464es/Makefile b/sound/pci/lx6464es/Makefile index c295f68bac68..2b3047c7a388 100644 --- a/sound/pci/lx6464es/Makefile +++ b/sound/pci/lx6464es/Makefile @@ -1,3 +1,3 @@ # SPDX-License-Identifier: GPL-2.0-only -snd-lx6464es-objs := lx6464es.o lx_core.o +snd-lx6464es-y := lx6464es.o lx_core.o obj-$(CONFIG_SND_LX6464ES) += snd-lx6464es.o diff --git a/sound/pci/lx6464es/lx_core.c b/sound/pci/lx6464es/lx_core.c index b5b0d43bb8dc..9d95ecb299ae 100644 --- a/sound/pci/lx6464es/lx_core.c +++ b/sound/pci/lx6464es/lx_core.c @@ -231,14 +231,14 @@ static void lx_message_dump(struct lx_rmh *rmh) u8 idx = rmh->cmd_idx; int i; - snd_printk(LXRMH "command %s\n", dsp_commands[idx].dcOpName); + pr_debug(LXRMH "command %s\n", dsp_commands[idx].dcOpName); for (i = 0; i != rmh->cmd_len; ++i) - snd_printk(LXRMH "\tcmd[%d] %08x\n", i, rmh->cmd[i]); + pr_debug(LXRMH "\tcmd[%d] %08x\n", i, rmh->cmd[i]); for (i = 0; i != rmh->stat_len; ++i) - snd_printk(LXRMH "\tstat[%d]: %08x\n", i, rmh->stat[i]); - snd_printk("\n"); + pr_debug(LXRMH "\tstat[%d]: %08x\n", i, rmh->stat[i]); + pr_debug("\n"); } #else static inline void lx_message_dump(struct lx_rmh *rmh) diff --git a/sound/pci/lx6464es/lx_core.h b/sound/pci/lx6464es/lx_core.h index 296013c910b7..c1113439f7c9 100644 --- a/sound/pci/lx6464es/lx_core.h +++ b/sound/pci/lx6464es/lx_core.h @@ -129,21 +129,18 @@ int lx_stream_set_state(struct lx6464es *chip, u32 pipe, static inline int lx_stream_start(struct lx6464es *chip, u32 pipe, int is_capture) { - snd_printdd("->lx_stream_start\n"); return lx_stream_set_state(chip, pipe, is_capture, SSTATE_RUN); } static inline int lx_stream_pause(struct lx6464es *chip, u32 pipe, int is_capture) { - snd_printdd("->lx_stream_pause\n"); return lx_stream_set_state(chip, pipe, is_capture, SSTATE_PAUSE); } static inline int lx_stream_stop(struct lx6464es *chip, u32 pipe, int is_capture) { - snd_printdd("->lx_stream_stop\n"); return lx_stream_set_state(chip, pipe, is_capture, SSTATE_STOP); } diff --git a/sound/pci/maestro3.c b/sound/pci/maestro3.c index 305cbd24a391..f4d211970d7e 100644 --- a/sound/pci/maestro3.c +++ b/sound/pci/maestro3.c @@ -769,9 +769,7 @@ struct snd_m3 { unsigned int in_suspend; -#ifdef CONFIG_PM_SLEEP u16 *suspend_mem; -#endif const struct firmware *assp_kernel_image; const struct firmware *assp_minisrc_image; @@ -2354,9 +2352,7 @@ static void snd_m3_free(struct snd_card *card) outw(0, chip->iobase + HOST_INT_CTRL); /* disable ints */ } -#ifdef CONFIG_PM_SLEEP vfree(chip->suspend_mem); -#endif release_firmware(chip->assp_kernel_image); release_firmware(chip->assp_minisrc_image); } @@ -2365,7 +2361,6 @@ static void snd_m3_free(struct snd_card *card) /* * APM support */ -#ifdef CONFIG_PM_SLEEP static int m3_suspend(struct device *dev) { struct snd_card *card = dev_get_drvdata(dev); @@ -2439,11 +2434,7 @@ static int m3_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(m3_pm, m3_suspend, m3_resume); -#define M3_PM_OPS &m3_pm -#else -#define M3_PM_OPS NULL -#endif /* CONFIG_PM_SLEEP */ +static DEFINE_SIMPLE_DEV_PM_OPS(m3_pm, m3_suspend, m3_resume); #ifdef CONFIG_SND_MAESTRO3_INPUT static int snd_m3_input_register(struct snd_m3 *chip) @@ -2587,14 +2578,14 @@ snd_m3_create(struct snd_card *card, struct pci_dev *pci, chip->irq = pci->irq; card->sync_irq = chip->irq; -#ifdef CONFIG_PM_SLEEP - chip->suspend_mem = - vmalloc(array_size(sizeof(u16), - REV_B_CODE_MEMORY_LENGTH + - REV_B_DATA_MEMORY_LENGTH)); - if (chip->suspend_mem == NULL) - dev_warn(card->dev, "can't allocate apm buffer\n"); -#endif + if (IS_ENABLED(CONFIG_PM_SLEEP)) { + chip->suspend_mem = + vmalloc(array_size(sizeof(u16), + REV_B_CODE_MEMORY_LENGTH + + REV_B_DATA_MEMORY_LENGTH)); + if (!chip->suspend_mem) + dev_warn(card->dev, "can't allocate apm buffer\n"); + } err = snd_m3_mixer(chip); if (err < 0) @@ -2706,7 +2697,7 @@ static struct pci_driver m3_driver = { .id_table = snd_m3_ids, .probe = snd_m3_probe, .driver = { - .pm = M3_PM_OPS, + .pm = &m3_pm, }, }; diff --git a/sound/pci/mixart/Makefile b/sound/pci/mixart/Makefile index 16cfeb78a0b6..b803e5e72791 100644 --- a/sound/pci/mixart/Makefile +++ b/sound/pci/mixart/Makefile @@ -4,6 +4,6 @@ # Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz> # -snd-mixart-objs := mixart.o mixart_core.o mixart_hwdep.o mixart_mixer.o +snd-mixart-y := mixart.o mixart_core.o mixart_hwdep.o mixart_mixer.o obj-$(CONFIG_SND_MIXART) += snd-mixart.o diff --git a/sound/pci/nm256/Makefile b/sound/pci/nm256/Makefile index 3063766ac56b..7d55fe774d20 100644 --- a/sound/pci/nm256/Makefile +++ b/sound/pci/nm256/Makefile @@ -4,7 +4,7 @@ # Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz> # -snd-nm256-objs := nm256.o +snd-nm256-y := nm256.o # Toplevel Module Dependency obj-$(CONFIG_SND_NM256) += snd-nm256.o diff --git a/sound/pci/nm256/nm256.c b/sound/pci/nm256/nm256.c index 34f90829e656..44085237fb44 100644 --- a/sound/pci/nm256/nm256.c +++ b/sound/pci/nm256/nm256.c @@ -696,7 +696,9 @@ snd_nm256_playback_copy(struct snd_pcm_substream *substream, struct snd_pcm_runtime *runtime = substream->runtime; struct nm256_stream *s = runtime->private_data; - return copy_from_iter_toio(s->bufptr + pos, src, count); + if (copy_from_iter_toio(s->bufptr + pos, count, src) != count) + return -EFAULT; + return 0; } /* @@ -710,7 +712,9 @@ snd_nm256_capture_copy(struct snd_pcm_substream *substream, struct snd_pcm_runtime *runtime = substream->runtime; struct nm256_stream *s = runtime->private_data; - return copy_to_iter_fromio(dst, s->bufptr + pos, count); + if (copy_to_iter_fromio(s->bufptr + pos, count, dst) != count) + return -EFAULT; + return 0; } #endif /* !__i386__ */ @@ -1356,7 +1360,6 @@ snd_nm256_peek_for_sig(struct nm256 *chip) return 0; } -#ifdef CONFIG_PM_SLEEP /* * APM event handler, so the card is properly reinitialized after a power * event. @@ -1400,11 +1403,7 @@ static int nm256_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(nm256_pm, nm256_suspend, nm256_resume); -#define NM256_PM_OPS &nm256_pm -#else -#define NM256_PM_OPS NULL -#endif /* CONFIG_PM_SLEEP */ +static DEFINE_SIMPLE_DEV_PM_OPS(nm256_pm, nm256_suspend, nm256_resume); static void snd_nm256_free(struct snd_card *card) { @@ -1660,7 +1659,7 @@ static struct pci_driver nm256_driver = { .id_table = snd_nm256_ids, .probe = snd_nm256_probe, .driver = { - .pm = NM256_PM_OPS, + .pm = &nm256_pm, }, }; diff --git a/sound/pci/oxygen/Makefile b/sound/pci/oxygen/Makefile index 0dfc4f840992..cc0c24694750 100644 --- a/sound/pci/oxygen/Makefile +++ b/sound/pci/oxygen/Makefile @@ -1,8 +1,8 @@ # SPDX-License-Identifier: GPL-2.0 -snd-oxygen-lib-objs := oxygen_io.o oxygen_lib.o oxygen_mixer.o oxygen_pcm.o -snd-oxygen-objs := oxygen.o xonar_dg_mixer.o xonar_dg.o -snd-se6x-objs := se6x.o -snd-virtuoso-objs := virtuoso.o xonar_lib.o \ +snd-oxygen-lib-y := oxygen_io.o oxygen_lib.o oxygen_mixer.o oxygen_pcm.o +snd-oxygen-y := oxygen.o xonar_dg_mixer.o xonar_dg.o +snd-se6x-y := se6x.o +snd-virtuoso-y := virtuoso.o xonar_lib.o \ xonar_pcm179x.o xonar_cs43xx.o xonar_wm87x6.o xonar_hdmi.o obj-$(CONFIG_SND_OXYGEN_LIB) += snd-oxygen-lib.o diff --git a/sound/pci/pcxhr/Makefile b/sound/pci/pcxhr/Makefile index 5993d86cfb5d..0ea1e5ccb56f 100644 --- a/sound/pci/pcxhr/Makefile +++ b/sound/pci/pcxhr/Makefile @@ -1,3 +1,3 @@ # SPDX-License-Identifier: GPL-2.0-only -snd-pcxhr-objs := pcxhr.o pcxhr_hwdep.o pcxhr_mixer.o pcxhr_core.o pcxhr_mix22.o +snd-pcxhr-y := pcxhr.o pcxhr_hwdep.o pcxhr_mixer.o pcxhr_core.o pcxhr_mix22.o obj-$(CONFIG_SND_PCXHR) += snd-pcxhr.o diff --git a/sound/pci/pcxhr/pcxhr_mix22.c b/sound/pci/pcxhr/pcxhr_mix22.c index f340458fd2e1..e1435afc4907 100644 --- a/sound/pci/pcxhr/pcxhr_mix22.c +++ b/sound/pci/pcxhr/pcxhr_mix22.c @@ -535,7 +535,7 @@ int hr222_update_analog_audio_level(struct snd_pcxhr *chip, { dev_dbg(chip->card->dev, "hr222_update_analog_audio_level(%s chan=%d)\n", - is_capture ? "capture" : "playback", channel); + snd_pcm_direction_name(is_capture), channel); if (is_capture) { int level_l, level_r, level_mic; /* we have to update all levels */ diff --git a/sound/pci/riptide/Makefile b/sound/pci/riptide/Makefile index 9a505bae243e..9b4e9595859a 100644 --- a/sound/pci/riptide/Makefile +++ b/sound/pci/riptide/Makefile @@ -1,4 +1,4 @@ # SPDX-License-Identifier: GPL-2.0-only -snd-riptide-objs := riptide.o +snd-riptide-y := riptide.o obj-$(CONFIG_SND_RIPTIDE) += snd-riptide.o diff --git a/sound/pci/riptide/riptide.c b/sound/pci/riptide/riptide.c index 9dee0345f22c..329816f37b76 100644 --- a/sound/pci/riptide/riptide.c +++ b/sound/pci/riptide/riptide.c @@ -380,6 +380,7 @@ struct riptideport { }; struct cmdif { + struct device *dev; struct riptideport *hwport; spinlock_t lock; unsigned int cmdcnt; /* cmd statistics */ @@ -448,9 +449,7 @@ struct snd_riptide { unsigned long received_irqs; unsigned long handled_irqs; -#ifdef CONFIG_PM_SLEEP int in_suspend; -#endif }; struct sgd { /* scatter gather desriptor */ @@ -729,7 +728,7 @@ static int loadfirmware(struct cmdif *cif, const unsigned char *img, } } } - snd_printdd("load firmware return %d\n", err); + dev_dbg(cif->dev, "load firmware return %d\n", err); return err; } @@ -742,7 +741,7 @@ alloclbuspath(struct cmdif *cif, unsigned char source, sink = *path & (~SPLIT_PATH); if (sink != E2SINK_MAX) { - snd_printdd("alloc path 0x%x->0x%x\n", source, sink); + dev_dbg(cif->dev, "alloc path 0x%x->0x%x\n", source, sink); SEND_PSEL(cif, source, sink); source = lbusin2out[sink][0]; type = lbusin2out[sink][1]; @@ -780,7 +779,7 @@ freelbuspath(struct cmdif *cif, unsigned char source, const unsigned char *path) sink = *path & (~SPLIT_PATH); if (sink != E2SINK_MAX) { - snd_printdd("free path 0x%x->0x%x\n", source, sink); + dev_dbg(cif->dev, "free path 0x%x->0x%x\n", source, sink); SEND_PCLR(cif, source, sink); source = lbusin2out[sink][0]; } @@ -813,8 +812,8 @@ static int writearm(struct cmdif *cif, u32 addr, u32 data, u32 mask) } else rptr.retlongs[0] &= ~mask; } - snd_printdd("send arm 0x%x 0x%x 0x%x return %d\n", addr, data, mask, - flag); + dev_dbg(cif->dev, "send arm 0x%x 0x%x 0x%x return %d\n", addr, data, mask, + flag); return flag; } @@ -834,14 +833,14 @@ static int sendcmd(struct cmdif *cif, u32 flags, u32 cmd, u32 parm, hwport = cif->hwport; if (cif->errcnt > MAX_ERROR_COUNT) { if (cif->is_reset) { - snd_printk(KERN_ERR - "Riptide: Too many failed cmds, reinitializing\n"); + dev_err(cif->dev, + "Riptide: Too many failed cmds, reinitializing\n"); if (riptide_reset(cif, NULL) == 0) { cif->errcnt = 0; return -EIO; } } - snd_printk(KERN_ERR "Riptide: Initialization failed.\n"); + dev_err(cif->dev, "Riptide: Initialization failed.\n"); return -EINVAL; } if (ret) { @@ -901,21 +900,21 @@ static int sendcmd(struct cmdif *cif, u32 flags, u32 cmd, u32 parm, if (time < cif->cmdtimemin) cif->cmdtimemin = time; if ((cif->cmdcnt) % 1000 == 0) - snd_printdd - ("send cmd %d time: %d mintime: %d maxtime %d err: %d\n", - cif->cmdcnt, cif->cmdtime, cif->cmdtimemin, - cif->cmdtimemax, cif->errcnt); + dev_dbg(cif->dev, + "send cmd %d time: %d mintime: %d maxtime %d err: %d\n", + cif->cmdcnt, cif->cmdtime, cif->cmdtimemin, + cif->cmdtimemax, cif->errcnt); return 0; errout: cif->errcnt++; spin_unlock_irqrestore(&cif->lock, irqflags); - snd_printdd - ("send cmd %d hw: 0x%x flag: 0x%x cmd: 0x%x parm: 0x%x ret: 0x%x 0x%x CMDE: %d DATF: %d failed %d\n", - cif->cmdcnt, (int)((void *)&(cmdport->stat) - (void *)hwport), - flags, cmd, parm, ret ? ret->retlongs[0] : 0, - ret ? ret->retlongs[1] : 0, IS_CMDE(cmdport), IS_DATF(cmdport), - err); + dev_dbg(cif->dev, + "send cmd %d hw: 0x%x flag: 0x%x cmd: 0x%x parm: 0x%x ret: 0x%x 0x%x CMDE: %d DATF: %d failed %d\n", + cif->cmdcnt, (int)((void *)&(cmdport->stat) - (void *)hwport), + flags, cmd, parm, ret ? ret->retlongs[0] : 0, + ret ? ret->retlongs[1] : 0, IS_CMDE(cmdport), IS_DATF(cmdport), + err); return err; } @@ -925,14 +924,14 @@ setmixer(struct cmdif *cif, short num, unsigned short rval, unsigned short lval) union cmdret rptr = CMDRET_ZERO; int i = 0; - snd_printdd("sent mixer %d: 0x%x 0x%x\n", num, rval, lval); + dev_dbg(cif->dev, "sent mixer %d: 0x%x 0x%x\n", num, rval, lval); do { SEND_SDGV(cif, num, num, rval, lval); SEND_RDGV(cif, num, num, &rptr); if (rptr.retwords[0] == lval && rptr.retwords[1] == rval) return 0; } while (i++ < MAX_WRITE_RETRY); - snd_printdd("sent mixer failed\n"); + dev_dbg(cif->dev, "sent mixer failed\n"); return -EIO; } @@ -963,7 +962,7 @@ getsourcesink(struct cmdif *cif, unsigned char source, unsigned char sink, return -EIO; *a = rptr.retbytes[0]; *b = rptr.retbytes[1]; - snd_printdd("getsourcesink 0x%x 0x%x\n", *a, *b); + dev_dbg(cif->dev, "%s 0x%x 0x%x\n", __func__, *a, *b); return 0; } @@ -990,11 +989,11 @@ getsamplerate(struct cmdif *cif, unsigned char *intdec, unsigned int *rate) } if (p[0]) { if (p[1] != p[0]) - snd_printdd("rates differ %d %d\n", p[0], p[1]); + dev_dbg(cif->dev, "rates differ %d %d\n", p[0], p[1]); *rate = (unsigned int)p[0]; } else *rate = (unsigned int)p[1]; - snd_printdd("getsampleformat %d %d %d\n", intdec[0], intdec[1], *rate); + dev_dbg(cif->dev, "getsampleformat %d %d %d\n", intdec[0], intdec[1], *rate); return 0; } @@ -1005,9 +1004,9 @@ setsampleformat(struct cmdif *cif, { unsigned char w, ch, sig, order; - snd_printdd - ("setsampleformat mixer: %d id: %d channels: %d format: %d\n", - mixer, id, channels, format); + dev_dbg(cif->dev, + "%s mixer: %d id: %d channels: %d format: %d\n", + __func__, mixer, id, channels, format); ch = channels == 1; w = snd_pcm_format_width(format) == 8; sig = snd_pcm_format_unsigned(format) != 0; @@ -1015,7 +1014,7 @@ setsampleformat(struct cmdif *cif, if (SEND_SETF(cif, mixer, w, ch, order, sig, id) && SEND_SETF(cif, mixer, w, ch, order, sig, id)) { - snd_printdd("setsampleformat failed\n"); + dev_dbg(cif->dev, "%s failed\n", __func__); return -EIO; } return 0; @@ -1028,8 +1027,8 @@ setsamplerate(struct cmdif *cif, unsigned char *intdec, unsigned int rate) union cmdret rptr = CMDRET_ZERO; int i; - snd_printdd("setsamplerate intdec: %d,%d rate: %d\n", intdec[0], - intdec[1], rate); + dev_dbg(cif->dev, "%s intdec: %d,%d rate: %d\n", __func__, + intdec[0], intdec[1], rate); D = 48000; M = ((rate == 48000) ? 47999 : rate) * 65536; N = M % D; @@ -1044,8 +1043,8 @@ setsamplerate(struct cmdif *cif, unsigned char *intdec, unsigned int rate) rptr.retwords[3] != N && i++ < MAX_WRITE_RETRY); if (i > MAX_WRITE_RETRY) { - snd_printdd("sent samplerate %d: %d failed\n", - *intdec, rate); + dev_dbg(cif->dev, "sent samplerate %d: %d failed\n", + *intdec, rate); return -EIO; } } @@ -1064,7 +1063,7 @@ getmixer(struct cmdif *cif, short num, unsigned short *rval, return -EIO; *rval = rptr.retwords[0]; *lval = rptr.retwords[1]; - snd_printdd("got mixer %d: 0x%x 0x%x\n", num, *rval, *lval); + dev_dbg(cif->dev, "got mixer %d: 0x%x 0x%x\n", num, *rval, *lval); return 0; } @@ -1107,8 +1106,8 @@ static irqreturn_t riptide_handleirq(int irq, void *dev_id) if ((flag & EOS_STATUS) && (data->state == ST_PLAY)) { data->state = ST_STOP; - snd_printk(KERN_ERR - "Riptide: DMA stopped unexpectedly\n"); + dev_err(cif->dev, + "Riptide: DMA stopped unexpectedly\n"); } c->dwStat_Ctl = cpu_to_le32(flag & @@ -1121,11 +1120,11 @@ static irqreturn_t riptide_handleirq(int irq, void *dev_id) period_bytes = frames_to_bytes(runtime, runtime->period_size); - snd_printdd - ("interrupt 0x%x after 0x%lx of 0x%lx frames in period\n", - READ_AUDIO_STATUS(cif->hwport), - bytes_to_frames(runtime, pos), - runtime->period_size); + dev_dbg(cif->dev, + "interrupt 0x%x after 0x%lx of 0x%lx frames in period\n", + READ_AUDIO_STATUS(cif->hwport), + bytes_to_frames(runtime, pos), + runtime->period_size); j = 0; if (pos >= period_bytes) { j++; @@ -1142,7 +1141,6 @@ static irqreturn_t riptide_handleirq(int irq, void *dev_id) return IRQ_HANDLED; } -#ifdef CONFIG_PM_SLEEP static int riptide_suspend(struct device *dev) { struct snd_card *card = dev_get_drvdata(dev); @@ -1166,11 +1164,7 @@ static int riptide_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(riptide_pm, riptide_suspend, riptide_resume); -#define RIPTIDE_PM_OPS &riptide_pm -#else -#define RIPTIDE_PM_OPS NULL -#endif /* CONFIG_PM_SLEEP */ +static DEFINE_SIMPLE_DEV_PM_OPS(riptide_pm, riptide_suspend, riptide_resume); static int try_to_load_firmware(struct cmdif *cif, struct snd_riptide *chip) { @@ -1191,23 +1185,23 @@ static int try_to_load_firmware(struct cmdif *cif, struct snd_riptide *chip) break; } if (!timeout) { - snd_printk(KERN_ERR - "Riptide: device not ready, audio status: 0x%x " - "ready: %d gerr: %d\n", - READ_AUDIO_STATUS(cif->hwport), - IS_READY(cif->hwport), IS_GERR(cif->hwport)); + dev_err(cif->dev, + "Riptide: device not ready, audio status: 0x%x ready: %d gerr: %d\n", + READ_AUDIO_STATUS(cif->hwport), + IS_READY(cif->hwport), IS_GERR(cif->hwport)); return -EIO; } else { - snd_printdd - ("Riptide: audio status: 0x%x ready: %d gerr: %d\n", - READ_AUDIO_STATUS(cif->hwport), - IS_READY(cif->hwport), IS_GERR(cif->hwport)); + dev_dbg(cif->dev, + "Riptide: audio status: 0x%x ready: %d gerr: %d\n", + READ_AUDIO_STATUS(cif->hwport), + IS_READY(cif->hwport), IS_GERR(cif->hwport)); } SEND_GETV(cif, &firmware.ret); - snd_printdd("Firmware version: ASIC: %d CODEC %d AUXDSP %d PROG %d\n", - firmware.firmware.ASIC, firmware.firmware.CODEC, - firmware.firmware.AUXDSP, firmware.firmware.PROG); + dev_dbg(cif->dev, + "Firmware version: ASIC: %d CODEC %d AUXDSP %d PROG %d\n", + firmware.firmware.ASIC, firmware.firmware.CODEC, + firmware.firmware.AUXDSP, firmware.firmware.PROG); if (!chip) return 1; @@ -1218,20 +1212,20 @@ static int try_to_load_firmware(struct cmdif *cif, struct snd_riptide *chip) } - snd_printdd("Writing Firmware\n"); + dev_dbg(cif->dev, "Writing Firmware\n"); if (!chip->fw_entry) { err = request_firmware(&chip->fw_entry, "riptide.hex", &chip->pci->dev); if (err) { - snd_printk(KERN_ERR - "Riptide: Firmware not available %d\n", err); + dev_err(cif->dev, + "Riptide: Firmware not available %d\n", err); return -EIO; } } err = loadfirmware(cif, chip->fw_entry->data, chip->fw_entry->size); if (err) { - snd_printk(KERN_ERR - "Riptide: Could not load firmware %d\n", err); + dev_err(cif->dev, + "Riptide: Could not load firmware %d\n", err); return err; } @@ -1264,7 +1258,7 @@ static int riptide_reset(struct cmdif *cif, struct snd_riptide *chip) SEND_SACR(cif, 0, AC97_RESET); SEND_RACR(cif, AC97_RESET, &rptr); - snd_printdd("AC97: 0x%x 0x%x\n", rptr.retlongs[0], rptr.retlongs[1]); + dev_dbg(cif->dev, "AC97: 0x%x 0x%x\n", rptr.retlongs[0], rptr.retlongs[1]); SEND_PLST(cif, 0); SEND_SLST(cif, 0); @@ -1357,11 +1351,11 @@ static snd_pcm_uframes_t snd_riptide_pointer(struct snd_pcm_substream SEND_GPOS(cif, 0, data->id, &rptr); if (data->size && runtime->period_size) { - snd_printdd - ("pointer stream %d position 0x%x(0x%x in buffer) bytes 0x%lx(0x%lx in period) frames\n", - data->id, rptr.retlongs[1], rptr.retlongs[1] % data->size, - bytes_to_frames(runtime, rptr.retlongs[1]), - bytes_to_frames(runtime, + dev_dbg(cif->dev, + "pointer stream %d position 0x%x(0x%x in buffer) bytes 0x%lx(0x%lx in period) frames\n", + data->id, rptr.retlongs[1], rptr.retlongs[1] % data->size, + bytes_to_frames(runtime, rptr.retlongs[1]), + bytes_to_frames(runtime, rptr.retlongs[1]) % runtime->period_size); if (rptr.retlongs[1] > data->pointer) ret = @@ -1372,8 +1366,9 @@ static snd_pcm_uframes_t snd_riptide_pointer(struct snd_pcm_substream bytes_to_frames(runtime, data->pointer % data->size); } else { - snd_printdd("stream not started or strange parms (%d %ld)\n", - data->size, runtime->period_size); + dev_dbg(cif->dev, + "stream not started or strange parms (%d %ld)\n", + data->size, runtime->period_size); ret = bytes_to_frames(runtime, 0); } return ret; @@ -1417,7 +1412,7 @@ static int snd_riptide_trigger(struct snd_pcm_substream *substream, int cmd) udelay(1); } while (i != rptr.retlongs[1] && j++ < MAX_WRITE_RETRY); if (j > MAX_WRITE_RETRY) - snd_printk(KERN_ERR "Riptide: Could not stop stream!"); + dev_err(cif->dev, "Riptide: Could not stop stream!"); break; case SNDRV_PCM_TRIGGER_PAUSE_PUSH: if (!(data->state & ST_PAUSE)) { @@ -1455,8 +1450,8 @@ static int snd_riptide_prepare(struct snd_pcm_substream *substream) if (snd_BUG_ON(!cif || !data)) return -EINVAL; - snd_printdd("prepare id %d ch: %d f:0x%x r:%d\n", data->id, - runtime->channels, runtime->format, runtime->rate); + dev_dbg(cif->dev, "prepare id %d ch: %d f:0x%x r:%d\n", data->id, + runtime->channels, runtime->format, runtime->rate); spin_lock_irq(&chip->lock); channels = runtime->channels; @@ -1476,8 +1471,7 @@ static int snd_riptide_prepare(struct snd_pcm_substream *substream) lbuspath = data->paths.stereo; break; } - snd_printdd("use sgdlist at 0x%p\n", - data->sgdlist.area); + dev_dbg(cif->dev, "use sgdlist at 0x%p\n", data->sgdlist.area); if (data->sgdlist.area) { unsigned int i, j, size, pages, f, pt, period; struct sgd *c, *p = NULL; @@ -1490,9 +1484,9 @@ static int snd_riptide_prepare(struct snd_pcm_substream *substream) pages = DIV_ROUND_UP(size, f); data->size = size; data->pages = pages; - snd_printdd - ("create sgd size: 0x%x pages %d of size 0x%x for period 0x%x\n", - size, pages, f, period); + dev_dbg(cif->dev, + "create sgd size: 0x%x pages %d of size 0x%x for period 0x%x\n", + size, pages, f, period); pt = 0; j = 0; for (i = 0; i < pages; i++) { @@ -1550,17 +1544,18 @@ snd_riptide_hw_params(struct snd_pcm_substream *substream, struct snd_dma_buffer *sgdlist = &data->sgdlist; int err; - snd_printdd("hw params id %d (sgdlist: 0x%p 0x%lx %d)\n", data->id, - sgdlist->area, (unsigned long)sgdlist->addr, - (int)sgdlist->bytes); + dev_dbg(chip->card->dev, "hw params id %d (sgdlist: 0x%p 0x%lx %d)\n", + data->id, sgdlist->area, (unsigned long)sgdlist->addr, + (int)sgdlist->bytes); if (sgdlist->area) snd_dma_free_pages(sgdlist); err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, &chip->pci->dev, sizeof(struct sgd) * (DESC_MAX_MASK + 1), sgdlist); if (err < 0) { - snd_printk(KERN_ERR "Riptide: failed to alloc %d dma bytes\n", - (int)sizeof(struct sgd) * (DESC_MAX_MASK + 1)); + dev_err(chip->card->dev, + "Riptide: failed to alloc %d dma bytes\n", + (int)sizeof(struct sgd) * (DESC_MAX_MASK + 1)); return err; } data->sgdbuf = (struct sgd *)sgdlist->area; @@ -1736,13 +1731,13 @@ snd_riptide_codec_write(struct snd_ac97 *ac97, unsigned short reg, if (snd_BUG_ON(!cif)) return; - snd_printdd("Write AC97 reg 0x%x 0x%x\n", reg, val); + dev_dbg(cif->dev, "Write AC97 reg 0x%x 0x%x\n", reg, val); do { SEND_SACR(cif, val, reg); SEND_RACR(cif, reg, &rptr); } while (rptr.retwords[1] != val && i++ < MAX_WRITE_RETRY); if (i > MAX_WRITE_RETRY) - snd_printdd("Write AC97 reg failed\n"); + dev_dbg(cif->dev, "Write AC97 reg failed\n"); } static unsigned short snd_riptide_codec_read(struct snd_ac97 *ac97, @@ -1757,7 +1752,7 @@ static unsigned short snd_riptide_codec_read(struct snd_ac97 *ac97, if (SEND_RACR(cif, reg, &rptr) != 0) SEND_RACR(cif, reg, &rptr); - snd_printdd("Read AC97 reg 0x%x got 0x%x\n", reg, rptr.retwords[1]); + dev_dbg(cif->dev, "Read AC97 reg 0x%x got 0x%x\n", reg, rptr.retwords[1]); return rptr.retwords[1]; } @@ -1775,6 +1770,7 @@ static int snd_riptide_initialize(struct snd_riptide *chip) cif = kzalloc(sizeof(struct cmdif), GFP_KERNEL); if (!cif) return -ENOMEM; + cif->dev = chip->card->dev; cif->hwport = (struct riptideport *)chip->port; spin_lock_init(&cif->lock); chip->cif = cif; @@ -1788,11 +1784,11 @@ static int snd_riptide_initialize(struct snd_riptide *chip) case 0x4310: case 0x4320: case 0x4330: - snd_printdd("Modem enable?\n"); + dev_dbg(cif->dev, "Modem enable?\n"); SEND_SETDPLL(cif); break; } - snd_printdd("Enabling MPU IRQs\n"); + dev_dbg(cif->dev, "Enabling MPU IRQs\n"); if (chip->rmidi) SET_EMPUIRQ(cif->hwport); return err; @@ -1845,8 +1841,8 @@ snd_riptide_create(struct snd_card *card, struct pci_dev *pci) snd_riptide_interrupt, riptide_handleirq, IRQF_SHARED, KBUILD_MODNAME, chip)) { - snd_printk(KERN_ERR "Riptide: unable to grab IRQ %d\n", - pci->irq); + dev_err(&pci->dev, "Riptide: unable to grab IRQ %d\n", + pci->irq); return -EBUSY; } chip->irq = pci->irq; @@ -1994,9 +1990,9 @@ snd_riptide_joystick_probe(struct pci_dev *pci, const struct pci_device_id *id) goto inc_dev; } if (!request_region(joystick_port[dev], 8, "Riptide gameport")) { - snd_printk(KERN_WARNING - "Riptide: cannot grab gameport 0x%x\n", - joystick_port[dev]); + dev_err(&pci->dev, + "Riptide: cannot grab gameport 0x%x\n", + joystick_port[dev]); gameport_free_port(gameport); ret = -EBUSY; goto inc_dev; @@ -2071,9 +2067,9 @@ __snd_card_riptide_probe(struct pci_dev *pci, const struct pci_device_id *pci_id val, MPU401_INFO_IRQ_HOOK, -1, &chip->rmidi); if (err < 0) - snd_printk(KERN_WARNING - "Riptide: Can't Allocate MPU at 0x%x\n", - val); + dev_warn(&pci->dev, + "Riptide: Can't Allocate MPU at 0x%x\n", + val); else chip->mpuaddr = val; } @@ -2083,15 +2079,15 @@ __snd_card_riptide_probe(struct pci_dev *pci, const struct pci_device_id *pci_id err = snd_opl3_create(card, val, val + 2, OPL3_HW_RIPTIDE, 0, &chip->opl3); if (err < 0) - snd_printk(KERN_WARNING - "Riptide: Can't Allocate OPL3 at 0x%x\n", - val); + dev_warn(&pci->dev, + "Riptide: Can't Allocate OPL3 at 0x%x\n", + val); else { chip->opladdr = val; err = snd_opl3_hwdep_new(chip->opl3, 0, 1, NULL); if (err < 0) - snd_printk(KERN_WARNING - "Riptide: Can't Allocate OPL3-HWDEP\n"); + dev_warn(&pci->dev, + "Riptide: Can't Allocate OPL3-HWDEP\n"); } } #ifdef SUPPORT_JOYSTICK @@ -2135,7 +2131,7 @@ static struct pci_driver driver = { .id_table = snd_riptide_ids, .probe = snd_card_riptide_probe, .driver = { - .pm = RIPTIDE_PM_OPS, + .pm = &riptide_pm, }, }; diff --git a/sound/pci/rme32.c b/sound/pci/rme32.c index 02144bbee6d5..a8c2ceaadef5 100644 --- a/sound/pci/rme32.c +++ b/sound/pci/rme32.c @@ -256,8 +256,10 @@ static int snd_rme32_playback_copy(struct snd_pcm_substream *substream, { struct rme32 *rme32 = snd_pcm_substream_chip(substream); - return copy_from_iter_toio(rme32->iobase + RME32_IO_DATA_BUFFER + pos, - src, count); + if (copy_from_iter_toio(rme32->iobase + RME32_IO_DATA_BUFFER + pos, + count, src) != count) + return -EFAULT; + return 0; } /* copy callback for halfduplex mode */ @@ -267,9 +269,10 @@ static int snd_rme32_capture_copy(struct snd_pcm_substream *substream, { struct rme32 *rme32 = snd_pcm_substream_chip(substream); - return copy_to_iter_fromio(dst, - rme32->iobase + RME32_IO_DATA_BUFFER + pos, - count); + if (copy_to_iter_fromio(rme32->iobase + RME32_IO_DATA_BUFFER + pos, + count, dst) != count) + return -EFAULT; + return 0; } /* diff --git a/sound/pci/rme96.c b/sound/pci/rme96.c index 6b5ffb18197b..1265a7efac60 100644 --- a/sound/pci/rme96.c +++ b/sound/pci/rme96.c @@ -220,12 +220,10 @@ struct rme96 { u8 rev; /* card revision number */ -#ifdef CONFIG_PM_SLEEP u32 playback_pointer; u32 capture_pointer; void *playback_suspend_buffer; void *capture_suspend_buffer; -#endif struct snd_pcm_substream *playback_substream; struct snd_pcm_substream *capture_substream; @@ -324,8 +322,10 @@ snd_rme96_playback_copy(struct snd_pcm_substream *substream, { struct rme96 *rme96 = snd_pcm_substream_chip(substream); - return copy_from_iter_toio(rme96->iobase + RME96_IO_PLAY_BUFFER + pos, - src, count); + if (copy_from_iter_toio(rme96->iobase + RME96_IO_PLAY_BUFFER + pos, + count, src) != count) + return -EFAULT; + return 0; } static int @@ -335,9 +335,10 @@ snd_rme96_capture_copy(struct snd_pcm_substream *substream, { struct rme96 *rme96 = snd_pcm_substream_chip(substream); - return copy_to_iter_fromio(dst, - rme96->iobase + RME96_IO_REC_BUFFER + pos, - count); + if (copy_to_iter_fromio(rme96->iobase + RME96_IO_REC_BUFFER + pos, + count, dst) != count) + return -EFAULT; + return 0; } /* @@ -1543,10 +1544,8 @@ snd_rme96_free(struct rme96 *rme96) rme96->areg &= ~RME96_AR_DAC_EN; writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG); } -#ifdef CONFIG_PM_SLEEP vfree(rme96->playback_suspend_buffer); vfree(rme96->capture_suspend_buffer); -#endif } static void @@ -2329,8 +2328,6 @@ snd_rme96_create_switches(struct snd_card *card, * Card initialisation */ -#ifdef CONFIG_PM_SLEEP - static int rme96_suspend(struct device *dev) { struct snd_card *card = dev_get_drvdata(dev); @@ -2392,11 +2389,7 @@ static int rme96_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(rme96_pm, rme96_suspend, rme96_resume); -#define RME96_PM_OPS &rme96_pm -#else -#define RME96_PM_OPS NULL -#endif /* CONFIG_PM_SLEEP */ +static DEFINE_SIMPLE_DEV_PM_OPS(rme96_pm, rme96_suspend, rme96_resume); static void snd_rme96_card_free(struct snd_card *card) { @@ -2432,14 +2425,14 @@ __snd_rme96_probe(struct pci_dev *pci, if (err) return err; -#ifdef CONFIG_PM_SLEEP - rme96->playback_suspend_buffer = vmalloc(RME96_BUFFER_SIZE); - if (!rme96->playback_suspend_buffer) - return -ENOMEM; - rme96->capture_suspend_buffer = vmalloc(RME96_BUFFER_SIZE); - if (!rme96->capture_suspend_buffer) - return -ENOMEM; -#endif + if (IS_ENABLED(CONFIG_PM_SLEEP)) { + rme96->playback_suspend_buffer = vmalloc(RME96_BUFFER_SIZE); + if (!rme96->playback_suspend_buffer) + return -ENOMEM; + rme96->capture_suspend_buffer = vmalloc(RME96_BUFFER_SIZE); + if (!rme96->capture_suspend_buffer) + return -ENOMEM; + } strcpy(card->driver, "Digi96"); switch (rme96->pci->device) { @@ -2483,7 +2476,7 @@ static struct pci_driver rme96_driver = { .id_table = snd_rme96_ids, .probe = snd_rme96_probe, .driver = { - .pm = RME96_PM_OPS, + .pm = &rme96_pm, }, }; diff --git a/sound/pci/rme9652/Makefile b/sound/pci/rme9652/Makefile index a3351447ddc0..cc99ae892211 100644 --- a/sound/pci/rme9652/Makefile +++ b/sound/pci/rme9652/Makefile @@ -4,9 +4,9 @@ # Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz> # -snd-rme9652-objs := rme9652.o -snd-hdsp-objs := hdsp.o -snd-hdspm-objs := hdspm.o +snd-rme9652-y := rme9652.o +snd-hdsp-y := hdsp.o +snd-hdspm-y := hdspm.o # Toplevel Module Dependency obj-$(CONFIG_SND_RME9652) += snd-rme9652.o diff --git a/sound/pci/rme9652/hdsp.c b/sound/pci/rme9652/hdsp.c index e7d1b43471a2..fd3dfbad397a 100644 --- a/sound/pci/rme9652/hdsp.c +++ b/sound/pci/rme9652/hdsp.c @@ -1298,8 +1298,10 @@ static int snd_hdsp_midi_output_possible (struct hdsp *hdsp, int id) static void snd_hdsp_flush_midi_input (struct hdsp *hdsp, int id) { - while (snd_hdsp_midi_input_available (hdsp, id)) - snd_hdsp_midi_read_byte (hdsp, id); + int count = 256; + + while (snd_hdsp_midi_input_available(hdsp, id) && --count) + snd_hdsp_midi_read_byte(hdsp, id); } static int snd_hdsp_midi_output_write (struct hdsp_midi *hmidi) @@ -3442,7 +3444,7 @@ snd_hdsp_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer) snd_iprintf(buffer, "MIDI1 Input status: 0x%x\n", hdsp_read(hdsp, HDSP_midiStatusIn0)); snd_iprintf(buffer, "MIDI2 Output status: 0x%x\n", hdsp_read(hdsp, HDSP_midiStatusOut1)); snd_iprintf(buffer, "MIDI2 Input status: 0x%x\n", hdsp_read(hdsp, HDSP_midiStatusIn1)); - snd_iprintf(buffer, "Use Midi Tasklet: %s\n", hdsp->use_midi_work ? "on" : "off"); + snd_iprintf(buffer, "Use Midi Tasklet: %s\n", str_on_off(hdsp->use_midi_work)); snd_iprintf(buffer, "\n"); @@ -3450,8 +3452,8 @@ snd_hdsp_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer) snd_iprintf(buffer, "Buffer Size (Latency): %d samples (2 periods of %lu bytes)\n", x, (unsigned long) hdsp->period_bytes); snd_iprintf(buffer, "Hardware pointer (frames): %ld\n", hdsp_hw_pointer(hdsp)); - snd_iprintf(buffer, "Precise pointer: %s\n", hdsp->precise_ptr ? "on" : "off"); - snd_iprintf(buffer, "Line out: %s\n", (hdsp->control_register & HDSP_LineOut) ? "on" : "off"); + snd_iprintf(buffer, "Precise pointer: %s\n", str_on_off(hdsp->precise_ptr)); + snd_iprintf(buffer, "Line out: %s\n", str_on_off(hdsp->control_register & HDSP_LineOut)); snd_iprintf(buffer, "Firmware version: %d\n", (status2&HDSP_version0)|(status2&HDSP_version1)<<1|(status2&HDSP_version2)<<2); @@ -3748,8 +3750,8 @@ 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_toggle_setting(hdsp, HDSP_XLRBreakoutCable) ? - "yes" : "no"); + str_yes_no(hdsp_toggle_setting(hdsp, + HDSP_XLRBreakoutCable))); if (hdsp->control_register & HDSP_AnalogExtensionBoard) snd_iprintf(buffer, "AEB : on (ADAT1 internal)\n"); @@ -4299,14 +4301,6 @@ static const struct snd_pcm_hw_constraint_list hdsp_hw_constraints_period_sizes .mask = 0 }; -static const unsigned int hdsp_9632_sample_rates[] = { 32000, 44100, 48000, 64000, 88200, 96000, 128000, 176400, 192000 }; - -static const struct snd_pcm_hw_constraint_list hdsp_hw_constraints_9632_sample_rates = { - .count = ARRAY_SIZE(hdsp_9632_sample_rates), - .list = hdsp_9632_sample_rates, - .mask = 0 -}; - static int snd_hdsp_hw_rule_in_channels(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { @@ -4497,8 +4491,9 @@ static int snd_hdsp_playback_open(struct snd_pcm_substream *substream) runtime->hw.rate_min = runtime->hw.rate_max = hdsp->system_sample_rate; } else if (hdsp->io_type == H9632) { runtime->hw.rate_max = 192000; - runtime->hw.rates = SNDRV_PCM_RATE_KNOT; - snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hdsp_hw_constraints_9632_sample_rates); + runtime->hw.rates |= (SNDRV_PCM_RATE_128000 | + SNDRV_PCM_RATE_176400 | + SNDRV_PCM_RATE_192000); } if (hdsp->io_type == H9632) { runtime->hw.channels_min = hdsp->qs_out_channels; @@ -4573,8 +4568,9 @@ static int snd_hdsp_capture_open(struct snd_pcm_substream *substream) runtime->hw.channels_min = hdsp->qs_in_channels; runtime->hw.channels_max = hdsp->ss_in_channels; runtime->hw.rate_max = 192000; - runtime->hw.rates = SNDRV_PCM_RATE_KNOT; - snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hdsp_hw_constraints_9632_sample_rates); + runtime->hw.rates |= (SNDRV_PCM_RATE_128000 | + SNDRV_PCM_RATE_176400 | + SNDRV_PCM_RATE_192000); } snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, snd_hdsp_hw_rule_in_channels, hdsp, diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c index 267c7848974a..f89718b19d23 100644 --- a/sound/pci/rme9652/hdspm.c +++ b/sound/pci/rme9652/hdspm.c @@ -1838,8 +1838,10 @@ static inline int snd_hdspm_midi_output_possible (struct hdspm *hdspm, int id) static void snd_hdspm_flush_midi_input(struct hdspm *hdspm, int id) { - while (snd_hdspm_midi_input_available (hdspm, id)) - snd_hdspm_midi_read_byte (hdspm, id); + int count = 256; + + while (snd_hdspm_midi_input_available(hdspm, id) && --count) + snd_hdspm_midi_read_byte(hdspm, id); } static int snd_hdspm_midi_output_write (struct hdspm_midi *hmidi) @@ -3081,7 +3083,7 @@ static int snd_hdspm_get_autosync_ref(struct snd_kcontrol *kcontrol, -#define HDSPM_TCO_VIDEO_INPUT_FORMAT(xname, xindex) \ +#define HDSPM_TCO_VIDEO_INPUT_FORMAT(xname) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ .name = xname, \ .access = SNDRV_CTL_ELEM_ACCESS_READ |\ @@ -3127,7 +3129,7 @@ static int snd_hdspm_get_tco_video_input_format(struct snd_kcontrol *kcontrol, -#define HDSPM_TCO_LTC_FRAMES(xname, xindex) \ +#define HDSPM_TCO_LTC_FRAMES(xname) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ .name = xname, \ .access = SNDRV_CTL_ELEM_ACCESS_READ |\ @@ -4628,8 +4630,8 @@ static const struct snd_kcontrol_new snd_hdspm_controls_tco[] = { HDSPM_TCO_WORD_TERM("TCO Word Term", 0), HDSPM_TCO_LOCK_CHECK("TCO Input Check", 11), HDSPM_TCO_LOCK_CHECK("TCO LTC Valid", 12), - HDSPM_TCO_LTC_FRAMES("TCO Detected Frame Rate", 0), - HDSPM_TCO_VIDEO_INPUT_FORMAT("Video Input Format", 0) + HDSPM_TCO_LTC_FRAMES("TCO Detected Frame Rate"), + HDSPM_TCO_VIDEO_INPUT_FORMAT("Video Input Format") }; @@ -4925,14 +4927,14 @@ snd_hdspm_proc_read_madi(struct snd_info_entry *entry, x, (unsigned long) hdspm->period_bytes); snd_iprintf(buffer, "Line out: %s\n", - (hdspm->control_register & HDSPM_LineOut) ? "on " : "off"); + str_on_off(hdspm->control_register & HDSPM_LineOut)); snd_iprintf(buffer, "ClearTrackMarker = %s, Transmit in %s Channel Mode, " "Auto Input %s\n", - (hdspm->control_register & HDSPM_clr_tms) ? "on" : "off", + str_on_off(hdspm->control_register & HDSPM_clr_tms), (hdspm->control_register & HDSPM_TX_64ch) ? "64" : "56", - (hdspm->control_register & HDSPM_AutoInp) ? "on" : "off"); + str_on_off(hdspm->control_register & HDSPM_AutoInp)); if (!(hdspm->control_register & HDSPM_ClockModeMaster)) @@ -5086,12 +5088,9 @@ snd_hdspm_proc_read_aes32(struct snd_info_entry * entry, snd_iprintf(buffer, "ClearTrackMarker %s, Emphasis %s, Dolby %s\n", - (hdspm-> - control_register & HDSPM_clr_tms) ? "on" : "off", - (hdspm-> - control_register & HDSPM_Emphasis) ? "on" : "off", - (hdspm-> - control_register & HDSPM_Dolby) ? "on" : "off"); + str_on_off(hdspm->control_register & HDSPM_clr_tms), + str_on_off(hdspm->control_register & HDSPM_Emphasis), + str_on_off(hdspm->control_register & HDSPM_Dolby)); pref_syncref = hdspm_pref_sync_ref(hdspm); @@ -5610,15 +5609,13 @@ static int snd_hdspm_hw_params(struct snd_pcm_substream *substream, /* dev_dbg(hdspm->card->dev, "Allocated sample buffer for %s at 0x%08X\n", - substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? - "playback" : "capture", + snd_pcm_direction_name(substream->stream), snd_pcm_sgbuf_get_addr(substream, 0)); */ /* dev_dbg(hdspm->card->dev, "set_hwparams: %s %d Hz, %d channels, bs = %d\n", - substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? - "playback" : "capture", + snd_pcm_direction_name(substream->stream), params_rate(params), params_channels(params), params_buffer_size(params)); */ @@ -6032,18 +6029,6 @@ static int snd_hdspm_hw_rule_out_channels(struct snd_pcm_hw_params *params, return snd_interval_list(c, 3, list, 0); } - -static const unsigned int hdspm_aes32_sample_rates[] = { - 32000, 44100, 48000, 64000, 88200, 96000, 128000, 176400, 192000 -}; - -static const struct snd_pcm_hw_constraint_list -hdspm_hw_constraints_aes32_sample_rates = { - .count = ARRAY_SIZE(hdspm_aes32_sample_rates), - .list = hdspm_aes32_sample_rates, - .mask = 0 -}; - static int snd_hdspm_open(struct snd_pcm_substream *substream) { struct hdspm *hdspm = snd_pcm_substream_chip(substream); @@ -6096,9 +6081,7 @@ static int snd_hdspm_open(struct snd_pcm_substream *substream) } if (AES32 == hdspm->io_type) { - runtime->hw.rates |= SNDRV_PCM_RATE_KNOT; - snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, - &hdspm_hw_constraints_aes32_sample_rates); + runtime->hw.rates |= SNDRV_PCM_RATE_128000; } else { snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, (playback ? diff --git a/sound/pci/rme9652/rme9652.c b/sound/pci/rme9652/rme9652.c index d066c70ae160..5b8dd7b0a02c 100644 --- a/sound/pci/rme9652/rme9652.c +++ b/sound/pci/rme9652/rme9652.c @@ -1561,8 +1561,7 @@ snd_rme9652_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buff x, (unsigned long) rme9652->period_bytes); snd_iprintf(buffer, "Hardware pointer (frames): %ld\n", rme9652_hw_pointer(rme9652)); - snd_iprintf(buffer, "Passthru: %s\n", - rme9652->passthru ? "yes" : "no"); + snd_iprintf(buffer, "Passthru: %s\n", str_yes_no(rme9652->passthru)); if ((rme9652->control_register & (RME9652_Master | RME9652_wsel)) == 0) { snd_iprintf(buffer, "Clock mode: autosync\n"); @@ -1685,7 +1684,7 @@ snd_rme9652_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buff snd_iprintf(buffer, "\n"); snd_iprintf(buffer, "Timecode signal: %s\n", - (status & RME9652_tc_valid) ? "yes" : "no"); + str_yes_no(status & RME9652_tc_valid)); /* thru modes */ diff --git a/sound/pci/sis7019.c b/sound/pci/sis7019.c index fabe393607f8..53206beb2cb5 100644 --- a/sound/pci/sis7019.c +++ b/sound/pci/sis7019.c @@ -90,11 +90,7 @@ struct voice { * we're not doing power management, we still need to allocate a page * for the silence buffer. */ -#ifdef CONFIG_PM_SLEEP #define SIS_SUSPEND_PAGES 4 -#else -#define SIS_SUSPEND_PAGES 1 -#endif struct sis7019 { unsigned long ioport; @@ -1152,7 +1148,6 @@ static int sis_chip_init(struct sis7019 *sis) return 0; } -#ifdef CONFIG_PM_SLEEP static int sis_suspend(struct device *dev) { struct snd_card *card = dev_get_drvdata(dev); @@ -1231,11 +1226,7 @@ error: return -EIO; } -static SIMPLE_DEV_PM_OPS(sis_pm, sis_suspend, sis_resume); -#define SIS_PM_OPS &sis_pm -#else -#define SIS_PM_OPS NULL -#endif /* CONFIG_PM_SLEEP */ +static DEFINE_SIMPLE_DEV_PM_OPS(sis_pm, sis_suspend, sis_resume); static int sis_alloc_suspend(struct sis7019 *sis) { @@ -1397,7 +1388,7 @@ static struct pci_driver sis7019_driver = { .id_table = snd_sis7019_ids, .probe = snd_sis7019_probe, .driver = { - .pm = SIS_PM_OPS, + .pm = &sis_pm, }, }; diff --git a/sound/pci/sonicvibes.c b/sound/pci/sonicvibes.c index f91cbf6eeca0..c30eaf1038e7 100644 --- a/sound/pci/sonicvibes.c +++ b/sound/pci/sonicvibes.c @@ -1118,7 +1118,7 @@ static void snd_sonicvibes_proc_read(struct snd_info_entry *entry, tmp = sonic->srs_space & 0x0f; snd_iprintf(buffer, "SRS 3D : %s\n", - sonic->srs_space & 0x80 ? "off" : "on"); + str_off_on(sonic->srs_space & 0x80)); snd_iprintf(buffer, "SRS Space : %s\n", tmp == 0x00 ? "100%" : tmp == 0x01 ? "75%" : @@ -1135,9 +1135,9 @@ static void snd_sonicvibes_proc_read(struct snd_info_entry *entry, tmp == 0x00 ? "on-board ROM" : tmp == 0x01 ? "PCI bus" : "on-board ROM + PCI bus"); tmp = sonic->mpu_switch; - snd_iprintf(buffer, "Onboard synth : %s\n", tmp & 0x01 ? "on" : "off"); - snd_iprintf(buffer, "Ext. Rx to synth : %s\n", tmp & 0x02 ? "on" : "off"); - snd_iprintf(buffer, "MIDI to ext. Tx : %s\n", tmp & 0x04 ? "on" : "off"); + snd_iprintf(buffer, "Onboard synth : %s\n", str_on_off(tmp & 0x01)); + snd_iprintf(buffer, "Ext. Rx to synth : %s\n", str_on_off(tmp & 0x02)); + snd_iprintf(buffer, "MIDI to ext. Tx : %s\n", str_on_off(tmp & 0x04)); } static void snd_sonicvibes_proc_init(struct sonicvibes *sonic) diff --git a/sound/pci/trident/Makefile b/sound/pci/trident/Makefile index e8975bc37fcb..476d16abcfc9 100644 --- a/sound/pci/trident/Makefile +++ b/sound/pci/trident/Makefile @@ -4,7 +4,7 @@ # Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz> # -snd-trident-objs := trident.o trident_main.o trident_memory.o +snd-trident-y := trident.o trident_main.o trident_memory.o # Toplevel Module Dependency obj-$(CONFIG_SND_TRIDENT) += snd-trident.o diff --git a/sound/pci/trident/trident.h b/sound/pci/trident/trident.h index 9768a7fc2349..ed2d4eecc704 100644 --- a/sound/pci/trident/trident.h +++ b/sound/pci/trident/trident.h @@ -406,7 +406,6 @@ int snd_trident_create_gameport(struct snd_trident *trident); int snd_trident_pcm(struct snd_trident *trident, int device); int snd_trident_foldback_pcm(struct snd_trident *trident, int device); int snd_trident_spdif_pcm(struct snd_trident *trident, int device); -int snd_trident_attach_synthesizer(struct snd_trident * trident); struct snd_trident_voice *snd_trident_alloc_voice(struct snd_trident * trident, int type, int client, int port); void snd_trident_free_voice(struct snd_trident * trident, struct snd_trident_voice *voice); @@ -419,9 +418,5 @@ extern const struct dev_pm_ops snd_trident_pm; struct snd_util_memblk *snd_trident_alloc_pages(struct snd_trident *trident, struct snd_pcm_substream *substream); int snd_trident_free_pages(struct snd_trident *trident, struct snd_util_memblk *blk); -struct snd_util_memblk *snd_trident_synth_alloc(struct snd_trident *trident, unsigned int size); -int snd_trident_synth_free(struct snd_trident *trident, struct snd_util_memblk *blk); -int snd_trident_synth_copy_from_user(struct snd_trident *trident, struct snd_util_memblk *blk, - int offset, const char __user *data, int size); #endif /* __SOUND_TRIDENT_H */ diff --git a/sound/pci/trident/trident_main.c b/sound/pci/trident/trident_main.c index e98eea1e6d81..8039f445bee2 100644 --- a/sound/pci/trident/trident_main.c +++ b/sound/pci/trident/trident_main.c @@ -3278,9 +3278,9 @@ static void snd_trident_proc_read(struct snd_info_entry *entry, snd_iprintf(buffer, "Spurious IRQs : %d\n", trident->spurious_irq_count); snd_iprintf(buffer, "Spurious IRQ dlta: %d\n", trident->spurious_irq_max_delta); if (trident->device == TRIDENT_DEVICE_ID_NX || trident->device == TRIDENT_DEVICE_ID_SI7018) - snd_iprintf(buffer, "IEC958 Mixer Out : %s\n", trident->spdif_ctrl == 0x28 ? "on" : "off"); + snd_iprintf(buffer, "IEC958 Mixer Out : %s\n", str_on_off(trident->spdif_ctrl == 0x28)); if (trident->device == TRIDENT_DEVICE_ID_NX) { - snd_iprintf(buffer, "Rear Speakers : %s\n", trident->ac97_ctrl & 0x00000010 ? "on" : "off"); + snd_iprintf(buffer, "Rear Speakers : %s\n", str_on_off(trident->ac97_ctrl & 0x00000010)); if (trident->tlb.entries) { snd_iprintf(buffer,"\nVirtual Memory\n"); snd_iprintf(buffer, "Memory Maximum : %d\n", trident->tlb.memhdr->size); diff --git a/sound/pci/trident/trident_memory.c b/sound/pci/trident/trident_memory.c index 05de2b9f4ed7..4a36f194c7f8 100644 --- a/sound/pci/trident/trident_memory.c +++ b/sound/pci/trident/trident_memory.c @@ -137,14 +137,14 @@ __found_pages: /* * check if the given pointer is valid for pages */ -static int is_valid_page(unsigned long ptr) +static int is_valid_page(struct snd_trident *trident, unsigned long ptr) { if (ptr & ~0x3fffffffUL) { - snd_printk(KERN_ERR "max memory size is 1GB!!\n"); + dev_err(trident->card->dev, "max memory size is 1GB!!\n"); return 0; } if (ptr & (SNDRV_TRIDENT_PAGE_SIZE-1)) { - snd_printk(KERN_ERR "page is not aligned\n"); + dev_err(trident->card->dev, "page is not aligned\n"); return 0; } return 1; @@ -184,7 +184,7 @@ snd_trident_alloc_sg_pages(struct snd_trident *trident, for (page = firstpg(blk); page <= lastpg(blk); page++, idx++) { unsigned long ofs = idx << PAGE_SHIFT; dma_addr_t addr = snd_pcm_sgbuf_get_addr(substream, ofs); - if (! is_valid_page(addr)) { + if (!is_valid_page(trident, addr)) { __snd_util_mem_free(hdr, blk); mutex_unlock(&hdr->block_mutex); return NULL; @@ -227,7 +227,7 @@ snd_trident_alloc_cont_pages(struct snd_trident *trident, addr = runtime->dma_addr; for (page = firstpg(blk); page <= lastpg(blk); page++, addr += SNDRV_TRIDENT_PAGE_SIZE) { - if (! is_valid_page(addr)) { + if (!is_valid_page(trident, addr)) { __snd_util_mem_free(hdr, blk); mutex_unlock(&hdr->block_mutex); return NULL; diff --git a/sound/pci/via82xx.c b/sound/pci/via82xx.c index d8666ff7bdfa..89838b4fb118 100644 --- a/sound/pci/via82xx.c +++ b/sound/pci/via82xx.c @@ -347,13 +347,11 @@ struct via82xx { unsigned char old_legacy; unsigned char old_legacy_cfg; -#ifdef CONFIG_PM_SLEEP unsigned char legacy_saved; unsigned char legacy_cfg_saved; unsigned char spdif_ctrl_saved; unsigned char capture_src_saved[2]; unsigned int mpu_port_saved; -#endif unsigned char playback_volume[4][2]; /* for VIA8233/C/8235; default = 0 */ unsigned char playback_volume_c[2]; /* for VIA8233/C/8235; default = 0 */ @@ -2031,9 +2029,7 @@ static int snd_via686_init_misc(struct via82xx *chip) if (mpu_port >= 0x200) { /* force MIDI */ mpu_port &= 0xfffc; pci_write_config_dword(chip->pci, 0x18, mpu_port | 0x01); -#ifdef CONFIG_PM_SLEEP chip->mpu_port_saved = mpu_port; -#endif } else { mpu_port = pci_resource_start(chip->pci, 2); } @@ -2085,10 +2081,8 @@ static int snd_via686_init_misc(struct via82xx *chip) snd_via686_create_gameport(chip, &legacy); -#ifdef CONFIG_PM_SLEEP chip->legacy_saved = legacy; chip->legacy_cfg_saved = legacy_cfg; -#endif return 0; } @@ -2234,7 +2228,6 @@ static int snd_via82xx_chip_init(struct via82xx *chip) return 0; } -#ifdef CONFIG_PM_SLEEP /* * power management */ @@ -2287,11 +2280,7 @@ static int snd_via82xx_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(snd_via82xx_pm, snd_via82xx_suspend, snd_via82xx_resume); -#define SND_VIA82XX_PM_OPS &snd_via82xx_pm -#else -#define SND_VIA82XX_PM_OPS NULL -#endif /* CONFIG_PM_SLEEP */ +static DEFINE_SIMPLE_DEV_PM_OPS(snd_via82xx_pm, snd_via82xx_suspend, snd_via82xx_resume); static void snd_via82xx_free(struct snd_card *card) { @@ -2576,7 +2565,7 @@ static struct pci_driver via82xx_driver = { .id_table = snd_via82xx_ids, .probe = snd_via82xx_probe, .driver = { - .pm = SND_VIA82XX_PM_OPS, + .pm = &snd_via82xx_pm, }, }; diff --git a/sound/pci/via82xx_modem.c b/sound/pci/via82xx_modem.c index ca7f024bf8ec..a0a49b8d1511 100644 --- a/sound/pci/via82xx_modem.c +++ b/sound/pci/via82xx_modem.c @@ -1008,7 +1008,6 @@ static int snd_via82xx_chip_init(struct via82xx_modem *chip) return 0; } -#ifdef CONFIG_PM_SLEEP /* * power management */ @@ -1042,11 +1041,7 @@ static int snd_via82xx_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(snd_via82xx_pm, snd_via82xx_suspend, snd_via82xx_resume); -#define SND_VIA82XX_PM_OPS &snd_via82xx_pm -#else -#define SND_VIA82XX_PM_OPS NULL -#endif /* CONFIG_PM_SLEEP */ +static DEFINE_SIMPLE_DEV_PM_OPS(snd_via82xx_pm, snd_via82xx_suspend, snd_via82xx_resume); static void snd_via82xx_free(struct snd_card *card) { @@ -1168,7 +1163,7 @@ static struct pci_driver via82xx_modem_driver = { .id_table = snd_via82xx_modem_ids, .probe = snd_via82xx_probe, .driver = { - .pm = SND_VIA82XX_PM_OPS, + .pm = &snd_via82xx_pm, }, }; diff --git a/sound/pci/vx222/Makefile b/sound/pci/vx222/Makefile index dda900e45385..6889137eb438 100644 --- a/sound/pci/vx222/Makefile +++ b/sound/pci/vx222/Makefile @@ -4,6 +4,6 @@ # Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz> # -snd-vx222-objs := vx222.o vx222_ops.o +snd-vx222-y := vx222.o vx222_ops.o obj-$(CONFIG_SND_VX222) += snd-vx222.o diff --git a/sound/pci/ymfpci/Makefile b/sound/pci/ymfpci/Makefile index 40a1d83e1a9e..2d7856403371 100644 --- a/sound/pci/ymfpci/Makefile +++ b/sound/pci/ymfpci/Makefile @@ -4,7 +4,7 @@ # Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz> # -snd-ymfpci-objs := ymfpci.o ymfpci_main.o +snd-ymfpci-y := ymfpci.o ymfpci_main.o # Toplevel Module Dependency obj-$(CONFIG_SND_YMFPCI) += snd-ymfpci.o |