[alsa-devel] [PATCH v2] sound azt3328: add custom AC97 semi-emulation, to be able to use standard ALSA AC97 layer
Takashi Iwai
tiwai at suse.de
Sat Feb 19 16:20:05 CET 2011
At Sat, 19 Feb 2011 00:49:32 +0100,
Andreas Mohr wrote:
>
> ChangeLog:
>
> Make use of the very flexible ALSA ac97 layer (hooks for custom I/O!)
> on this weird AC97 copycat hardware,
> via semi-extended I/O translation/emulation.
>
> Some 5kB binary/loaded size saved (well... additional huge AC97 module
> penalty not factored in, of course ;-P).
> Given that the driver previously had 20kB that's not bad,
> but the much more important thing is to have AC97 layer stress-tested
> with a thoroughly weird AC97 copycat (or, simply put, if it were not for
> this AC97 test aspect, this effort would merely have been a nut job ;).
>
> v2:
> - select Kconfig SND_AC97_CODEC, unconditionally activate AC97 mode
> - took the liberty of adding a driver features description
> during this Kconfig update
>
> Signed-off-by: Andreas Mohr <andi at lisas.de>
> ---
> The semi-emulated AC97 constellation appears to be working fine.
>
> Planning to remove old custom mixer parts if AC97-based operation holds
> water.
>
> Patch created on -rc3, tested and checkpatch.pl'd.
> GIT history seems clean, should thus apply easily.
>
> Thanks!
Applied now. Thanks.
Takashi
>
> Index: linux-2.6/sound/pci/azt3328.c
> ===================================================================
> --- linux-2.6.orig/sound/pci/azt3328.c 2011-02-16 23:18:59.000000000 +0100
> +++ linux-2.6/sound/pci/azt3328.c 2011-02-19 00:23:46.000000000 +0100
> @@ -1,6 +1,5 @@
> -/*
> - * azt3328.c - driver for Aztech AZF3328 based soundcards (e.g. PCI168).
> - * Copyright (C) 2002, 2005 - 2010 by Andreas Mohr <andi AT lisas.de>
> +/* azt3328.c - driver for Aztech AZF3328 based soundcards (e.g. PCI168).
> + * Copyright (C) 2002, 2005 - 2011 by Andreas Mohr <andi AT lisas.de>
> *
> * Framework borrowed from Bart Hartgers's als4000.c.
> * Driver developed on PCI168 AP(W) version (PCI rev. 10, subsystem ID 1801),
> @@ -66,6 +65,13 @@
> * addresses illegally. So far unfortunately it looks like the very flexible
> * ALSA AC97 support is still not enough to easily compensate for such a
> * grave layout violation despite all tweaks and quirks mechanisms it offers.
> + * Well, not quite: now ac97 layer is much improved (bus-specific ops!),
> + * thus I was able to implement support - it's actually working quite well.
> + * An interesting item might be Aztech AMR 2800-W, since it's an AC97
> + * modem card which might reveal the Aztech-specific codec ID which
> + * we might want to pretend, too. Dito PCI168's brother, PCI368,
> + * where the advertising datasheet says it's AC97-based and has a
> + * Digital Enhanced Game Port.
> * - builtin genuine OPL3 - verified to work fine, 20080506
> * - full duplex 16bit playback/record at independent sampling rate
> * - MPU401 (+ legacy address support, claimed by one official spec sheet)
> @@ -189,6 +195,16 @@
> #include <sound/mpu401.h>
> #include <sound/opl3.h>
> #include <sound/initval.h>
> +/*
> + * Config switch, to use ALSA's AC97 layer instead of old custom mixer crap.
> + * If the AC97 compatibility parts we needed to implement locally turn out
> + * to work nicely, then remove the old implementation eventually.
> + */
> +#define AZF_USE_AC97_LAYER 1
> +
> +#ifdef AZF_USE_AC97_LAYER
> +#include <sound/ac97_codec.h>
> +#endif
> #include "azt3328.h"
>
> MODULE_AUTHOR("Andreas Mohr <andi AT lisas.de>");
> @@ -328,6 +344,10 @@
> /* playback, recording and I2S out codecs */
> struct snd_azf3328_codec_data codecs[3];
>
> +#ifdef AZF_USE_AC97_LAYER
> + struct snd_ac97 *ac97;
> +#endif
> +
> struct snd_card *card;
> struct snd_rawmidi *rmidi;
>
> @@ -506,7 +526,7 @@
> #define AZF_MUTE_BIT 0x80
>
> static bool
> -snd_azf3328_mixer_set_mute(const struct snd_azf3328 *chip,
> +snd_azf3328_mixer_mute_control(const struct snd_azf3328 *chip,
> unsigned reg, bool do_mute
> )
> {
> @@ -521,6 +541,323 @@
> return (do_mute) ? !updated : updated;
> }
>
> +static inline bool
> +snd_azf3328_mixer_mute_control_master(const struct snd_azf3328 *chip,
> + bool do_mute
> +)
> +{
> + return snd_azf3328_mixer_mute_control(
> + chip,
> + IDX_MIXER_PLAY_MASTER,
> + do_mute
> + );
> +}
> +
> +static inline bool
> +snd_azf3328_mixer_mute_control_pcm(const struct snd_azf3328 *chip,
> + bool do_mute
> +)
> +{
> + return snd_azf3328_mixer_mute_control(
> + chip,
> + IDX_MIXER_WAVEOUT,
> + do_mute
> + );
> +}
> +
> +static inline void
> +snd_azf3328_mixer_reset(const struct snd_azf3328 *chip)
> +{
> + /* reset (close) mixer:
> + * first mute master volume, then reset
> + */
> + snd_azf3328_mixer_mute_control_master(chip, 1);
> + snd_azf3328_mixer_outw(chip, IDX_MIXER_RESET, 0x0000);
> +}
> +
> +#ifdef AZF_USE_AC97_LAYER
> +
> +static inline void
> +snd_azf3328_mixer_ac97_map_unsupported(unsigned short reg, const char *mode)
> +{
> + /* need to add some more or less clever emulation? */
> + printk(KERN_WARNING
> + "azt3328: missing %s emulation for AC97 register 0x%02x!\n",
> + mode, reg);
> +}
> +
> +/*
> + * Need to have _special_ AC97 mixer hardware register index mapper,
> + * to compensate for the issue of a rather AC97-incompatible hardware layout.
> + */
> +#define AZF_REG_MASK 0x3f
> +#define AZF_AC97_REG_UNSUPPORTED 0x8000
> +#define AZF_AC97_REG_REAL_IO_READ 0x4000
> +#define AZF_AC97_REG_REAL_IO_WRITE 0x2000
> +#define AZF_AC97_REG_REAL_IO_RW \
> + (AZF_AC97_REG_REAL_IO_READ | AZF_AC97_REG_REAL_IO_WRITE)
> +#define AZF_AC97_REG_EMU_IO_READ 0x0400
> +#define AZF_AC97_REG_EMU_IO_WRITE 0x0200
> +#define AZF_AC97_REG_EMU_IO_RW \
> + (AZF_AC97_REG_EMU_IO_READ | AZF_AC97_REG_EMU_IO_WRITE)
> +static unsigned short
> +snd_azf3328_mixer_ac97_map_reg_idx(unsigned short reg)
> +{
> + static const struct {
> + unsigned short azf_reg;
> + } azf_reg_mapper[] = {
> + /* Especially when taking into consideration
> + * mono/stereo-based sequence of azf vs. AC97 control series,
> + * it's quite obvious that azf simply got rid
> + * of the AC97_HEADPHONE control at its intended offset,
> + * thus shifted _all_ controls by one,
> + * and _then_ simply added it as an FMSYNTH control at the end,
> + * to make up for the offset.
> + * This means we'll have to translate indices here as
> + * needed and then do some tiny AC97 patch action
> + * (snd_ac97_rename_vol_ctl() etc.) - that's it.
> + */
> + { /* AC97_RESET */ IDX_MIXER_RESET
> + | AZF_AC97_REG_REAL_IO_WRITE
> + | AZF_AC97_REG_EMU_IO_READ },
> + { /* AC97_MASTER */ IDX_MIXER_PLAY_MASTER },
> + /* note large shift: AC97_HEADPHONE to IDX_MIXER_FMSYNTH! */
> + { /* AC97_HEADPHONE */ IDX_MIXER_FMSYNTH },
> + { /* AC97_MASTER_MONO */ IDX_MIXER_MODEMOUT },
> + { /* AC97_MASTER_TONE */ IDX_MIXER_BASSTREBLE },
> + { /* AC97_PC_BEEP */ IDX_MIXER_PCBEEP },
> + { /* AC97_PHONE */ IDX_MIXER_MODEMIN },
> + { /* AC97_MIC */ IDX_MIXER_MIC },
> + { /* AC97_LINE */ IDX_MIXER_LINEIN },
> + { /* AC97_CD */ IDX_MIXER_CDAUDIO },
> + { /* AC97_VIDEO */ IDX_MIXER_VIDEO },
> + { /* AC97_AUX */ IDX_MIXER_AUX },
> + { /* AC97_PCM */ IDX_MIXER_WAVEOUT },
> + { /* AC97_REC_SEL */ IDX_MIXER_REC_SELECT },
> + { /* AC97_REC_GAIN */ IDX_MIXER_REC_VOLUME },
> + { /* AC97_REC_GAIN_MIC */ AZF_AC97_REG_EMU_IO_RW },
> + { /* AC97_GENERAL_PURPOSE */ IDX_MIXER_ADVCTL2 },
> + { /* AC97_3D_CONTROL */ IDX_MIXER_ADVCTL1 },
> + };
> +
> + unsigned short reg_azf = AZF_AC97_REG_UNSUPPORTED;
> +
> + /* azf3328 supports the low-numbered and low-spec:ed range
> + of AC97 regs only */
> + if (reg <= AC97_3D_CONTROL) {
> + unsigned short reg_idx = reg / 2;
> + reg_azf = azf_reg_mapper[reg_idx].azf_reg;
> + /* a translation-only entry means it's real read/write: */
> + if (!(reg_azf & ~AZF_REG_MASK))
> + reg_azf |= AZF_AC97_REG_REAL_IO_RW;
> + } else {
> + switch (reg) {
> + case AC97_POWERDOWN:
> + reg_azf = AZF_AC97_REG_EMU_IO_RW;
> + break;
> + case AC97_EXTENDED_ID:
> + reg_azf = AZF_AC97_REG_EMU_IO_READ;
> + break;
> + case AC97_EXTENDED_STATUS:
> + /* I don't know what the h*ll AC97 layer
> + * would consult this _extended_ register for
> + * given a base-AC97-advertised card,
> + * but let's just emulate it anyway :-P
> + */
> + reg_azf = AZF_AC97_REG_EMU_IO_RW;
> + break;
> + case AC97_VENDOR_ID1:
> + case AC97_VENDOR_ID2:
> + reg_azf = AZF_AC97_REG_EMU_IO_READ;
> + break;
> + }
> + }
> + return reg_azf;
> +}
> +
> +static const unsigned short
> +azf_emulated_ac97_caps =
> + AC97_BC_DEDICATED_MIC |
> + AC97_BC_BASS_TREBLE |
> + /* Headphone is an FM Synth control here */
> + AC97_BC_HEADPHONE |
> + /* no AC97_BC_LOUDNESS! */
> + /* mask 0x7c00 is
> + vendor-specific 3D enhancement
> + vendor indicator.
> + Since there actually _is_ an
> + entry for Aztech Labs
> + (13), make damn sure
> + to indicate it. */
> + (13 << 10);
> +
> +static const unsigned short
> +azf_emulated_ac97_powerdown =
> + /* pretend everything to be active */
> + AC97_PD_ADC_STATUS |
> + AC97_PD_DAC_STATUS |
> + AC97_PD_MIXER_STATUS |
> + AC97_PD_VREF_STATUS;
> +
> +/*
> + * Emulated, _inofficial_ vendor ID
> + * (there might be some devices such as the MR 2800-W
> + * which could reveal the real Aztech AC97 ID).
> + * We choose to use "AZT" prefix, and then use 1 to indicate PCI168
> + * (better don't use 0x68 since there's a PCI368 as well).
> + */
> +static const unsigned int
> +azf_emulated_ac97_vendor_id = 0x415a5401;
> +
> +static unsigned short
> +snd_azf3328_mixer_ac97_read(struct snd_ac97 *ac97, unsigned short reg_ac97)
> +{
> + const struct snd_azf3328 *chip = ac97->private_data;
> + unsigned short reg_azf = snd_azf3328_mixer_ac97_map_reg_idx(reg_ac97);
> + unsigned short reg_val = 0;
> + bool unsupported = 0;
> +
> + snd_azf3328_dbgmixer(
> + "snd_azf3328_mixer_ac97_read reg_ac97 %u\n",
> + reg_ac97
> + );
> + if (reg_azf & AZF_AC97_REG_UNSUPPORTED)
> + unsupported = 1;
> + else {
> + if (reg_azf & AZF_AC97_REG_REAL_IO_READ)
> + reg_val = snd_azf3328_mixer_inw(chip,
> + reg_azf & AZF_REG_MASK);
> + else {
> + /*
> + * Proceed with dummy I/O read,
> + * to ensure compatible timing where this may matter.
> + * (ALSA AC97 layer usually doesn't call I/O functions
> + * due to intelligent I/O caching anyway)
> + * Choose a mixer register that's thoroughly unrelated
> + * to common audio (try to minimize distortion).
> + */
> + snd_azf3328_mixer_inw(chip, IDX_MIXER_SOMETHING30H);
> + }
> +
> + if (reg_azf & AZF_AC97_REG_EMU_IO_READ) {
> + switch (reg_ac97) {
> + case AC97_RESET:
> + reg_val |= azf_emulated_ac97_caps;
> + break;
> + case AC97_POWERDOWN:
> + reg_val |= azf_emulated_ac97_powerdown;
> + break;
> + case AC97_EXTENDED_ID:
> + case AC97_EXTENDED_STATUS:
> + /* AFAICS we simply can't support anything: */
> + reg_val |= 0;
> + break;
> + case AC97_VENDOR_ID1:
> + reg_val = azf_emulated_ac97_vendor_id >> 16;
> + break;
> + case AC97_VENDOR_ID2:
> + reg_val = azf_emulated_ac97_vendor_id & 0xffff;
> + break;
> + default:
> + unsupported = 1;
> + break;
> + }
> + }
> + }
> + if (unsupported)
> + snd_azf3328_mixer_ac97_map_unsupported(reg_ac97, "read");
> +
> + return reg_val;
> +}
> +
> +static void
> +snd_azf3328_mixer_ac97_write(struct snd_ac97 *ac97,
> + unsigned short reg_ac97, unsigned short val)
> +{
> + const struct snd_azf3328 *chip = ac97->private_data;
> + unsigned short reg_azf = snd_azf3328_mixer_ac97_map_reg_idx(reg_ac97);
> + bool unsupported = 0;
> +
> + snd_azf3328_dbgmixer(
> + "snd_azf3328_mixer_ac97_write reg_ac97 %u val %u\n",
> + reg_ac97, val
> + );
> + if (reg_azf & AZF_AC97_REG_UNSUPPORTED)
> + unsupported = 1;
> + else {
> + if (reg_azf & AZF_AC97_REG_REAL_IO_WRITE)
> + snd_azf3328_mixer_outw(
> + chip,
> + reg_azf & AZF_REG_MASK,
> + val
> + );
> + else
> + if (reg_azf & AZF_AC97_REG_EMU_IO_WRITE) {
> + switch (reg_ac97) {
> + case AC97_REC_GAIN_MIC:
> + case AC97_POWERDOWN:
> + case AC97_EXTENDED_STATUS:
> + /*
> + * Silently swallow these writes.
> + * Since for most registers our card doesn't
> + * actually support a comparable feature,
> + * this is exactly what we should do here.
> + * The AC97 layer's I/O caching probably
> + * automatically takes care of all the rest...
> + * (remembers written values etc.)
> + */
> + break;
> + default:
> + unsupported = 1;
> + break;
> + }
> + }
> + }
> + if (unsupported)
> + snd_azf3328_mixer_ac97_map_unsupported(reg_ac97, "write");
> +}
> +
> +static int __devinit
> +snd_azf3328_mixer_new(struct snd_azf3328 *chip)
> +{
> + struct snd_ac97_bus *bus;
> + struct snd_ac97_template ac97;
> + static struct snd_ac97_bus_ops ops = {
> + .write = snd_azf3328_mixer_ac97_write,
> + .read = snd_azf3328_mixer_ac97_read,
> + };
> + int rc;
> +
> + memset(&ac97, 0, sizeof(ac97));
> + ac97.scaps = AC97_SCAP_SKIP_MODEM
> + | AC97_SCAP_AUDIO /* we support audio! */
> + | AC97_SCAP_NO_SPDIF;
> + ac97.private_data = chip;
> + ac97.pci = chip->pci;
> +
> + /*
> + * ALSA's AC97 layer has terrible init crackling issues,
> + * unfortunately, and since it makes use of AC97_RESET,
> + * there's no use trying to mute Master Playback proactively.
> + */
> +
> + rc = snd_ac97_bus(chip->card, 0, &ops, NULL, &bus);
> + if (!rc)
> + rc = snd_ac97_mixer(bus, &ac97, &chip->ac97);
> + /*
> + * Make sure to complain loudly in case of AC97 init failure,
> + * since failure may happen quite often,
> + * due to this card being a very quirky AC97 "lookalike".
> + */
> + if (rc)
> + printk(KERN_ERR "azt3328: AC97 init failed, err %d!\n", rc);
> +
> + /* If we return an error here, then snd_card_free() should
> + * free up any ac97 codecs that got created, as well as the bus.
> + */
> + return rc;
> +}
> +#else /* AZF_USE_AC97_LAYER */
> static void
> snd_azf3328_mixer_write_volume_gradually(const struct snd_azf3328 *chip,
> unsigned reg,
> @@ -945,6 +1282,7 @@
> snd_azf3328_dbgcallleave();
> return 0;
> }
> +#endif /* AZF_USE_AC97_LAYER */
>
> static int
> snd_azf3328_hw_params(struct snd_pcm_substream *substream,
> @@ -1233,8 +1571,8 @@
> if (is_main_mixer_playback_codec) {
> /* mute WaveOut (avoid clicking during setup) */
> previously_muted =
> - snd_azf3328_mixer_set_mute(
> - chip, IDX_MIXER_WAVEOUT, 1
> + snd_azf3328_mixer_mute_control_pcm(
> + chip, 1
> );
> }
>
> @@ -1290,8 +1628,8 @@
> if (is_main_mixer_playback_codec) {
> /* now unmute WaveOut */
> if (!previously_muted)
> - snd_azf3328_mixer_set_mute(
> - chip, IDX_MIXER_WAVEOUT, 0
> + snd_azf3328_mixer_mute_control_pcm(
> + chip, 0
> );
> }
>
> @@ -1315,8 +1653,8 @@
> if (is_main_mixer_playback_codec) {
> /* mute WaveOut (avoid clicking during setup) */
> previously_muted =
> - snd_azf3328_mixer_set_mute(
> - chip, IDX_MIXER_WAVEOUT, 1
> + snd_azf3328_mixer_mute_control_pcm(
> + chip, 1
> );
> }
>
> @@ -1341,8 +1679,8 @@
> if (is_main_mixer_playback_codec) {
> /* now unmute WaveOut */
> if (!previously_muted)
> - snd_azf3328_mixer_set_mute(
> - chip, IDX_MIXER_WAVEOUT, 0
> + snd_azf3328_mixer_mute_control_pcm(
> + chip, 0
> );
> }
>
> @@ -2050,11 +2388,7 @@
> if (chip->irq < 0)
> goto __end_hw;
>
> - /* reset (close) mixer:
> - * first mute master volume, then reset
> - */
> - snd_azf3328_mixer_set_mute(chip, IDX_MIXER_PLAY_MASTER, 1);
> - snd_azf3328_mixer_outw(chip, IDX_MIXER_RESET, 0x0000);
> + snd_azf3328_mixer_reset(chip);
>
> snd_azf3328_timer_stop(chip->timer);
> snd_azf3328_gameport_free(chip);
> @@ -2407,6 +2741,55 @@
> }
> }
>
> +static inline void
> +snd_azf3328_resume_regs(const u32 *saved_regs,
> + unsigned long io_addr,
> + unsigned count
> +)
> +{
> + unsigned reg;
> +
> + for (reg = 0; reg < count; ++reg) {
> + outl(*saved_regs, io_addr);
> + snd_azf3328_dbgpm("resume: io 0x%04lx: 0x%08x --> 0x%08x\n",
> + io_addr, *saved_regs, inl(io_addr));
> + ++saved_regs;
> + io_addr += sizeof(*saved_regs);
> + }
> +}
> +
> +static inline void
> +snd_azf3328_suspend_ac97(struct snd_azf3328 *chip)
> +{
> +#ifdef AZF_USE_AC97_LAYER
> + snd_ac97_suspend(chip->ac97);
> +#else
> + snd_azf3328_suspend_regs(chip->mixer_io,
> + ARRAY_SIZE(chip->saved_regs_mixer), chip->saved_regs_mixer);
> +
> + /* make sure to disable master volume etc. to prevent looping sound */
> + snd_azf3328_mixer_mute_control_master(chip, 1);
> + snd_azf3328_mixer_mute_control_pcm(chip, 1);
> +#endif /* AZF_USE_AC97_LAYER */
> +}
> +
> +static inline void
> +snd_azf3328_resume_ac97(const struct snd_azf3328 *chip)
> +{
> +#ifdef AZF_USE_AC97_LAYER
> + snd_ac97_resume(chip->ac97);
> +#else
> + snd_azf3328_resume_regs(chip->saved_regs_mixer, chip->mixer_io,
> + ARRAY_SIZE(chip->saved_regs_mixer));
> +
> + /* unfortunately with 32bit transfers, IDX_MIXER_PLAY_MASTER (0x02)
> + and IDX_MIXER_RESET (offset 0x00) get touched at the same time,
> + resulting in a mixer reset condition persisting until _after_
> + master vol was restored. Thus master vol needs an extra restore. */
> + outw(((u16 *)chip->saved_regs_mixer)[1], chip->mixer_io + 2);
> +#endif /* AZF_USE_AC97_LAYER */
> +}
> +
> static int
> snd_azf3328_suspend(struct pci_dev *pci, pm_message_t state)
> {
> @@ -2420,12 +2803,7 @@
> snd_pcm_suspend_all(chip->pcm[AZF_CODEC_PLAYBACK]);
> snd_pcm_suspend_all(chip->pcm[AZF_CODEC_I2S_OUT]);
>
> - snd_azf3328_suspend_regs(chip->mixer_io,
> - ARRAY_SIZE(chip->saved_regs_mixer), chip->saved_regs_mixer);
> -
> - /* make sure to disable master volume etc. to prevent looping sound */
> - snd_azf3328_mixer_set_mute(chip, IDX_MIXER_PLAY_MASTER, 1);
> - snd_azf3328_mixer_set_mute(chip, IDX_MIXER_WAVEOUT, 1);
> + snd_azf3328_suspend_ac97(chip);
>
> snd_azf3328_suspend_regs(chip->ctrl_io,
> ARRAY_SIZE(chip->saved_regs_ctrl), chip->saved_regs_ctrl);
> @@ -2447,23 +2825,6 @@
> return 0;
> }
>
> -static inline void
> -snd_azf3328_resume_regs(const u32 *saved_regs,
> - unsigned long io_addr,
> - unsigned count
> -)
> -{
> - unsigned reg;
> -
> - for (reg = 0; reg < count; ++reg) {
> - outl(*saved_regs, io_addr);
> - snd_azf3328_dbgpm("resume: io 0x%04lx: 0x%08x --> 0x%08x\n",
> - io_addr, *saved_regs, inl(io_addr));
> - ++saved_regs;
> - io_addr += sizeof(*saved_regs);
> - }
> -}
> -
> static int
> snd_azf3328_resume(struct pci_dev *pci)
> {
> @@ -2487,14 +2848,7 @@
> snd_azf3328_resume_regs(chip->saved_regs_opl3, chip->opl3_io,
> ARRAY_SIZE(chip->saved_regs_opl3));
>
> - snd_azf3328_resume_regs(chip->saved_regs_mixer, chip->mixer_io,
> - ARRAY_SIZE(chip->saved_regs_mixer));
> -
> - /* unfortunately with 32bit transfers, IDX_MIXER_PLAY_MASTER (0x02)
> - and IDX_MIXER_RESET (offset 0x00) get touched at the same time,
> - resulting in a mixer reset condition persisting until _after_
> - master vol was restored. Thus master vol needs an extra restore. */
> - outw(((u16 *)chip->saved_regs_mixer)[1], chip->mixer_io + 2);
> + snd_azf3328_resume_ac97(chip);
>
> snd_azf3328_resume_regs(chip->saved_regs_ctrl, chip->ctrl_io,
> ARRAY_SIZE(chip->saved_regs_ctrl));
> Index: linux-2.6/sound/pci/Kconfig
> ===================================================================
> --- linux-2.6.orig/sound/pci/Kconfig 2011-02-18 23:40:38.000000000 +0100
> +++ linux-2.6/sound/pci/Kconfig 2011-02-19 00:15:03.000000000 +0100
> @@ -152,10 +152,16 @@
> select SND_MPU401_UART
> select SND_PCM
> select SND_RAWMIDI
> + select SND_AC97_CODEC
> help
> Say Y here to include support for Aztech AZF3328 (PCI168)
> soundcards.
>
> + Supported features: AC97-"conformant" mixer, MPU401/OPL3, analog I/O
> + (16bit/8bit, many sample rates [<= 66.2kHz], NO hardware mixing),
> + Digital Enhanced Game Port, 1.024MHz multimedia sequencer timer,
> + ext. codec (I2S port), onboard amp (4W/4Ohms/ch), suspend/resume.
> +
> To compile this driver as a module, choose M here: the module
> will be called snd-azt3328.
>
>
More information about the Alsa-devel
mailing list