diff options
Diffstat (limited to 'sound/pci/azt3328.c')
| -rw-r--r-- | sound/pci/azt3328.c | 1261 |
1 files changed, 757 insertions, 504 deletions
diff --git a/sound/pci/azt3328.c b/sound/pci/azt3328.c index 4679ed83a43..c9216c0a9c8 100644 --- a/sound/pci/azt3328.c +++ b/sound/pci/azt3328.c @@ -1,6 +1,5 @@ -/* - * azt3328.c - driver for Aztech AZF3328 based soundcards (e.g. PCI168). - * Copyright (C) 2002, 2005 - 2009 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) @@ -134,7 +140,7 @@ * Possible remedies: * - use speaker (amplifier) output instead of headphone output * (in case crackling is due to overloaded output clipping) - * - plug card into a different PCI slot, preferrably one that isn't shared + * - plug card into a different PCI slot, preferably one that isn't shared * too much (this helps a lot, but not completely!) * - get rid of PCI VGA card, use AGP instead * - upgrade or downgrade BIOS @@ -175,11 +181,12 @@ #include <asm/io.h> #include <linux/init.h> +#include <linux/bug.h> /* WARN_ONCE */ #include <linux/pci.h> #include <linux/delay.h> #include <linux/slab.h> #include <linux/gameport.h> -#include <linux/moduleparam.h> +#include <linux/module.h> #include <linux/dma-mapping.h> #include <sound/core.h> #include <sound/control.h> @@ -188,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>"); @@ -201,14 +218,15 @@ MODULE_SUPPORTED_DEVICE("{{Aztech,AZF3328}}"); /* === Debug settings === Further diagnostic functionality than the settings below - does not need to be provided, since one can easily write a bash script + does not need to be provided, since one can easily write a POSIX shell script to dump the card's I/O ports (those listed in lspci -v -v): - function dump() + dump() { local descr=$1; local addr=$2; local count=$3 echo "${descr}: ${count} @ ${addr}:" - dd if=/dev/port skip=$[${addr}] count=${count} bs=1 2>/dev/null| hexdump -C + dd if=/dev/port skip=`printf %d ${addr}` count=${count} bs=1 \ + 2>/dev/null| hexdump -C } and then use something like "dump joy200 0x200 8", "dump mpu388 0x388 4", "dump joy 0xb400 8", @@ -216,65 +234,10 @@ MODULE_SUPPORTED_DEVICE("{{Aztech,AZF3328}}"); possibly within a "while true; do ... sleep 1; done" loop. Tweaking ports could be done using VALSTRING="`printf "%02x" $value`" - printf "\x""$VALSTRING"|dd of=/dev/port seek=$[${addr}] bs=1 2>/dev/null + printf "\x""$VALSTRING"|dd of=/dev/port seek=`printf %d ${addr}` bs=1 \ + 2>/dev/null */ -#define DEBUG_MISC 0 -#define DEBUG_CALLS 0 -#define DEBUG_MIXER 0 -#define DEBUG_CODEC 0 -#define DEBUG_IO 0 -#define DEBUG_TIMER 0 -#define DEBUG_GAME 0 -#define DEBUG_PM 0 -#define MIXER_TESTING 0 - -#if DEBUG_MISC -#define snd_azf3328_dbgmisc(format, args...) printk(KERN_DEBUG format, ##args) -#else -#define snd_azf3328_dbgmisc(format, args...) -#endif - -#if DEBUG_CALLS -#define snd_azf3328_dbgcalls(format, args...) printk(format, ##args) -#define snd_azf3328_dbgcallenter() printk(KERN_DEBUG "--> %s\n", __func__) -#define snd_azf3328_dbgcallleave() printk(KERN_DEBUG "<-- %s\n", __func__) -#else -#define snd_azf3328_dbgcalls(format, args...) -#define snd_azf3328_dbgcallenter() -#define snd_azf3328_dbgcallleave() -#endif - -#if DEBUG_MIXER -#define snd_azf3328_dbgmixer(format, args...) printk(KERN_DEBUG format, ##args) -#else -#define snd_azf3328_dbgmixer(format, args...) -#endif - -#if DEBUG_CODEC -#define snd_azf3328_dbgcodec(format, args...) printk(KERN_DEBUG format, ##args) -#else -#define snd_azf3328_dbgcodec(format, args...) -#endif - -#if DEBUG_MISC -#define snd_azf3328_dbgtimer(format, args...) printk(KERN_DEBUG format, ##args) -#else -#define snd_azf3328_dbgtimer(format, args...) -#endif - -#if DEBUG_GAME -#define snd_azf3328_dbggame(format, args...) printk(KERN_DEBUG format, ##args) -#else -#define snd_azf3328_dbggame(format, args...) -#endif - -#if DEBUG_PM -#define snd_azf3328_dbgpm(format, args...) printk(KERN_DEBUG format, ##args) -#else -#define snd_azf3328_dbgpm(format, args...) -#endif - static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ module_param_array(index, int, NULL, 0444); MODULE_PARM_DESC(index, "Index value for AZF3328 soundcard."); @@ -283,7 +246,7 @@ static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ module_param_array(id, charp, NULL, 0444); MODULE_PARM_DESC(id, "ID string for AZF3328 soundcard."); -static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */ +static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */ module_param_array(enable, bool, NULL, 0444); MODULE_PARM_DESC(enable, "Enable AZF3328 soundcard."); @@ -291,19 +254,23 @@ static int seqtimer_scaling = 128; module_param(seqtimer_scaling, int, 0444); MODULE_PARM_DESC(seqtimer_scaling, "Set 1024000Hz sequencer timer scale factor (lockup danger!). Default 128."); -struct snd_azf3328_codec_data { - unsigned long io_base; - struct snd_pcm_substream *substream; - bool running; - const char *name; -}; - enum snd_azf3328_codec_type { + /* warning: fixed indices (also used for bitmask checks!) */ AZF_CODEC_PLAYBACK = 0, AZF_CODEC_CAPTURE = 1, AZF_CODEC_I2S_OUT = 2, }; +struct snd_azf3328_codec_data { + unsigned long io_base; /* keep first! (avoid offset calc) */ + unsigned int dma_base; /* helper to avoid an indirection in hotpath */ + spinlock_t *lock; /* TODO: convert to our own per-codec lock member */ + struct snd_pcm_substream *substream; + bool running; + enum snd_azf3328_codec_type type; + const char *name; +}; + struct snd_azf3328 { /* often-used fields towards beginning, then grouped */ @@ -322,6 +289,10 @@ struct snd_azf3328 { /* 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; @@ -339,7 +310,7 @@ struct snd_azf3328 { * CONFIG_PM register storage below, but that's slightly difficult. */ u16 shadow_reg_ctrl_6AH; -#ifdef CONFIG_PM +#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]; @@ -362,6 +333,9 @@ MODULE_DEVICE_TABLE(pci, snd_azf3328_ids); static int snd_azf3328_io_reg_setb(unsigned reg, u8 mask, bool do_set) { + /* Well, strictly spoken, the inb/outb sequence isn't atomic + and would need locking. However we currently don't care + since it potentially complicates matters. */ u8 prev = inb(reg), new; new = (do_set) ? (prev|mask) : (prev & ~mask); @@ -413,6 +387,21 @@ snd_azf3328_codec_outl(const struct snd_azf3328_codec_data *codec, outl(value, codec->io_base + reg); } +static inline void +snd_azf3328_codec_outl_multi(const struct snd_azf3328_codec_data *codec, + unsigned reg, const void *buffer, int count +) +{ + unsigned long addr = codec->io_base + reg; + if (count) { + const u32 *buf = buffer; + do { + outl(*buf++, addr); + addr += 4; + } while (--count); + } +} + static inline u32 snd_azf3328_codec_inl(const struct snd_azf3328_codec_data *codec, unsigned reg) { @@ -431,6 +420,12 @@ snd_azf3328_ctrl_inb(const struct snd_azf3328 *chip, unsigned reg) return inb(chip->ctrl_io + reg); } +static inline u16 +snd_azf3328_ctrl_inw(const struct snd_azf3328 *chip, unsigned reg) +{ + return inw(chip->ctrl_io + reg); +} + static inline void snd_azf3328_ctrl_outw(const struct snd_azf3328 *chip, unsigned reg, u16 value) { @@ -482,7 +477,7 @@ snd_azf3328_mixer_inw(const struct snd_azf3328 *chip, unsigned reg) #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 ) { @@ -497,6 +492,321 @@ snd_azf3328_mixer_set_mute(const struct snd_azf3328 *chip, 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(const struct snd_azf3328 *chip, + unsigned short reg, const char *mode) +{ + /* need to add some more or less clever emulation? */ + dev_warn(chip->card->dev, + "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 = false; + + dev_dbg(chip->card->dev, "snd_azf3328_mixer_ac97_read reg_ac97 %u\n", + reg_ac97); + if (reg_azf & AZF_AC97_REG_UNSUPPORTED) + unsupported = true; + 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 = true; + break; + } + } + } + if (unsupported) + snd_azf3328_mixer_ac97_map_unsupported(chip, 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 = false; + + dev_dbg(chip->card->dev, + "snd_azf3328_mixer_ac97_write reg_ac97 %u val %u\n", + reg_ac97, val); + if (reg_azf & AZF_AC97_REG_UNSUPPORTED) + unsupported = true; + 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 = true; + break; + } + } + } + if (unsupported) + snd_azf3328_mixer_ac97_map_unsupported(chip, reg_ac97, "write"); +} + +static int +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) + dev_err(chip->card->dev, "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, @@ -509,8 +819,6 @@ snd_azf3328_mixer_write_volume_gradually(const struct snd_azf3328 *chip, unsigned char curr_vol_left = 0, curr_vol_right = 0; int left_change = 0, right_change = 0; - snd_azf3328_dbgcallenter(); - if (chan_sel & SET_CHAN_LEFT) { curr_vol_left = inb(portbase + 1); @@ -551,7 +859,6 @@ snd_azf3328_mixer_write_volume_gradually(const struct snd_azf3328 *chip, if (delay) mdelay(delay); } while ((left_change) || (right_change)); - snd_azf3328_dbgcallleave(); } /* @@ -629,14 +936,12 @@ snd_azf3328_info_mixer(struct snd_kcontrol *kcontrol, { struct azf3328_mixer_reg reg; - snd_azf3328_dbgcallenter(); snd_azf3328_mixer_reg_decode(®, kcontrol->private_value); uinfo->type = reg.mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; uinfo->count = reg.stereo + 1; uinfo->value.integer.min = 0; uinfo->value.integer.max = reg.mask; - snd_azf3328_dbgcallleave(); return 0; } @@ -648,7 +953,6 @@ snd_azf3328_get_mixer(struct snd_kcontrol *kcontrol, struct azf3328_mixer_reg reg; u16 oreg, val; - snd_azf3328_dbgcallenter(); snd_azf3328_mixer_reg_decode(®, kcontrol->private_value); oreg = snd_azf3328_mixer_inw(chip, reg.reg); @@ -662,12 +966,11 @@ snd_azf3328_get_mixer(struct snd_kcontrol *kcontrol, val = reg.mask - val; ucontrol->value.integer.value[1] = val; } - snd_azf3328_dbgmixer("get: %02x is %04x -> vol %02lx|%02lx " - "(shift %02d|%02d, mask %02x, inv. %d, stereo %d)\n", + dev_dbg(chip->card->dev, + "get: %02x is %04x -> vol %02lx|%02lx (shift %02d|%02d, mask %02x, inv. %d, stereo %d)\n", reg.reg, oreg, ucontrol->value.integer.value[0], ucontrol->value.integer.value[1], reg.lchan_shift, reg.rchan_shift, reg.mask, reg.invert, reg.stereo); - snd_azf3328_dbgcallleave(); return 0; } @@ -679,7 +982,6 @@ snd_azf3328_put_mixer(struct snd_kcontrol *kcontrol, struct azf3328_mixer_reg reg; u16 oreg, nreg, val; - snd_azf3328_dbgcallenter(); snd_azf3328_mixer_reg_decode(®, kcontrol->private_value); oreg = snd_azf3328_mixer_inw(chip, reg.reg); val = ucontrol->value.integer.value[0] & reg.mask; @@ -703,12 +1005,11 @@ snd_azf3328_put_mixer(struct snd_kcontrol *kcontrol, else snd_azf3328_mixer_outw(chip, reg.reg, nreg); - snd_azf3328_dbgmixer("put: %02x to %02lx|%02lx, " - "oreg %04x; shift %02d|%02d -> nreg %04x; after: %04x\n", + dev_dbg(chip->card->dev, + "put: %02x to %02lx|%02lx, oreg %04x; shift %02d|%02d -> nreg %04x; after: %04x\n", reg.reg, ucontrol->value.integer.value[0], ucontrol->value.integer.value[1], oreg, reg.lchan_shift, reg.rchan_shift, nreg, snd_azf3328_mixer_inw(chip, reg.reg)); - snd_azf3328_dbgcallleave(); return (nreg != oreg); } @@ -774,7 +1075,8 @@ snd_azf3328_get_mixer_enum(struct snd_kcontrol *kcontrol, } else ucontrol->value.enumerated.item[0] = (val >> reg.lchan_shift) & (reg.enum_c - 1); - snd_azf3328_dbgmixer("get_enum: %02x is %04x -> %d|%d (shift %02d, enum_c %d)\n", + dev_dbg(chip->card->dev, + "get_enum: %02x is %04x -> %d|%d (shift %02d, enum_c %d)\n", reg.reg, val, ucontrol->value.enumerated.item[0], ucontrol->value.enumerated.item[1], reg.lchan_shift, reg.enum_c); return 0; @@ -806,11 +1108,12 @@ snd_azf3328_put_mixer_enum(struct snd_kcontrol *kcontrol, snd_azf3328_mixer_outw(chip, reg.reg, val); nreg = val; - snd_azf3328_dbgmixer("put_enum: %02x to %04x, oreg %04x\n", reg.reg, val, oreg); + dev_dbg(chip->card->dev, + "put_enum: %02x to %04x, oreg %04x\n", reg.reg, val, oreg); return (nreg != oreg); } -static struct snd_kcontrol_new snd_azf3328_mixer_controls[] __devinitdata = { +static struct snd_kcontrol_new snd_azf3328_mixer_controls[] = { AZF3328_MIXER_SWITCH("Master Playback Switch", IDX_MIXER_PLAY_MASTER, 15, 1), AZF3328_MIXER_VOL_STEREO("Master Playback Volume", IDX_MIXER_PLAY_MASTER, 0x1f, 1), AZF3328_MIXER_SWITCH("PCM Playback Switch", IDX_MIXER_WAVEOUT, 15, 1), @@ -868,7 +1171,7 @@ static struct snd_kcontrol_new snd_azf3328_mixer_controls[] __devinitdata = { #endif }; -static u16 __devinitdata snd_azf3328_init_values[][2] = { +static u16 snd_azf3328_init_values[][2] = { { IDX_MIXER_PLAY_MASTER, MIXER_MUTE_MASK|0x1f1f }, { IDX_MIXER_MODEMOUT, MIXER_MUTE_MASK|0x1f1f }, { IDX_MIXER_BASSTREBLE, 0x0000 }, @@ -884,7 +1187,7 @@ static u16 __devinitdata snd_azf3328_init_values[][2] = { { IDX_MIXER_REC_VOLUME, MIXER_MUTE_MASK|0x0707 }, }; -static int __devinit +static int snd_azf3328_mixer_new(struct snd_azf3328 *chip) { struct snd_card *card; @@ -892,7 +1195,6 @@ snd_azf3328_mixer_new(struct snd_azf3328 *chip) unsigned int idx; int err; - snd_azf3328_dbgcallenter(); if (snd_BUG_ON(!chip || !chip->card)) return -EINVAL; @@ -918,63 +1220,55 @@ snd_azf3328_mixer_new(struct snd_azf3328 *chip) snd_component_add(card, "AZF3328 mixer"); strcpy(card->mixername, "AZF3328 mixer"); - snd_azf3328_dbgcallleave(); return 0; } +#endif /* AZF_USE_AC97_LAYER */ static int snd_azf3328_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { - int res; - snd_azf3328_dbgcallenter(); - res = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); - snd_azf3328_dbgcallleave(); - return res; + return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); } static int snd_azf3328_hw_free(struct snd_pcm_substream *substream) { - snd_azf3328_dbgcallenter(); snd_pcm_lib_free_pages(substream); - snd_azf3328_dbgcallleave(); return 0; } static void -snd_azf3328_codec_setfmt(struct snd_azf3328 *chip, - enum snd_azf3328_codec_type codec_type, +snd_azf3328_codec_setfmt(struct snd_azf3328_codec_data *codec, enum azf_freq_t bitrate, unsigned int format_width, unsigned int channels ) { unsigned long flags; - const struct snd_azf3328_codec_data *codec = &chip->codecs[codec_type]; u16 val = 0xff00; + u8 freq = 0; - snd_azf3328_dbgcallenter(); switch (bitrate) { - case AZF_FREQ_4000: val |= SOUNDFORMAT_FREQ_SUSPECTED_4000; break; - case AZF_FREQ_4800: val |= SOUNDFORMAT_FREQ_SUSPECTED_4800; break; + case AZF_FREQ_4000: freq = SOUNDFORMAT_FREQ_SUSPECTED_4000; break; + case AZF_FREQ_4800: freq = SOUNDFORMAT_FREQ_SUSPECTED_4800; break; case AZF_FREQ_5512: /* the AZF3328 names it "5510" for some strange reason */ - val |= SOUNDFORMAT_FREQ_5510; break; - case AZF_FREQ_6620: val |= SOUNDFORMAT_FREQ_6620; break; - case AZF_FREQ_8000: val |= SOUNDFORMAT_FREQ_8000; break; - case AZF_FREQ_9600: val |= SOUNDFORMAT_FREQ_9600; break; - case AZF_FREQ_11025: val |= SOUNDFORMAT_FREQ_11025; break; - case AZF_FREQ_13240: val |= SOUNDFORMAT_FREQ_SUSPECTED_13240; break; - case AZF_FREQ_16000: val |= SOUNDFORMAT_FREQ_16000; break; - case AZF_FREQ_22050: val |= SOUNDFORMAT_FREQ_22050; break; - case AZF_FREQ_32000: val |= SOUNDFORMAT_FREQ_32000; break; + freq = SOUNDFORMAT_FREQ_5510; break; + case AZF_FREQ_6620: freq = SOUNDFORMAT_FREQ_6620; break; + case AZF_FREQ_8000: freq = SOUNDFORMAT_FREQ_8000; break; + case AZF_FREQ_9600: freq = SOUNDFORMAT_FREQ_9600; break; + case AZF_FREQ_11025: freq = SOUNDFORMAT_FREQ_11025; break; + case AZF_FREQ_13240: freq = SOUNDFORMAT_FREQ_SUSPECTED_13240; break; + case AZF_FREQ_16000: freq = SOUNDFORMAT_FREQ_16000; break; + 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); /* fall-through */ - case AZF_FREQ_44100: val |= SOUNDFORMAT_FREQ_44100; break; - case AZF_FREQ_48000: val |= SOUNDFORMAT_FREQ_48000; break; - case AZF_FREQ_66200: val |= SOUNDFORMAT_FREQ_SUSPECTED_66200; break; + case AZF_FREQ_44100: freq = SOUNDFORMAT_FREQ_44100; break; + case AZF_FREQ_48000: freq = SOUNDFORMAT_FREQ_48000; break; + case AZF_FREQ_66200: freq = SOUNDFORMAT_FREQ_SUSPECTED_66200; break; } /* val = 0xff07; 3m27.993s (65301Hz; -> 64000Hz???) hmm, 66120, 65967, 66123 */ /* val = 0xff09; 17m15.098s (13123,478Hz; -> 12000Hz???) hmm, 13237.2Hz? */ @@ -986,13 +1280,15 @@ snd_azf3328_codec_setfmt(struct snd_azf3328 *chip, /* val = 0xff0d; 41m23.135s (5523,600Hz; -> 5512Hz???) */ /* val = 0xff0e; 28m30.777s (8017Hz; -> 8000Hz???) */ + val |= freq; + if (channels == 2) val |= SOUNDFORMAT_FLAG_2CHANNELS; if (format_width == 16) val |= SOUNDFORMAT_FLAG_16BIT; - spin_lock_irqsave(&chip->reg_lock, flags); + spin_lock_irqsave(codec->lock, flags); /* set bitrate/format */ snd_azf3328_codec_outw(codec, IDX_IO_CODEC_SOUNDFORMAT, val); @@ -1004,7 +1300,8 @@ snd_azf3328_codec_setfmt(struct snd_azf3328 *chip, * (FIXME: yes, it works, but what exactly am I doing here?? :) * FIXME: does this have some side effects for full-duplex * or other dramatic side effects? */ - if (codec_type == AZF_CODEC_PLAYBACK) /* only do it for playback */ + /* do it for non-capture codecs only */ + if (codec->type != AZF_CODEC_CAPTURE) snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, snd_azf3328_codec_inw(codec, IDX_IO_CODEC_DMA_FLAGS) | DMA_RUN_SOMETHING1 | @@ -1014,20 +1311,18 @@ snd_azf3328_codec_setfmt(struct snd_azf3328 *chip, DMA_SOMETHING_ELSE ); - spin_unlock_irqrestore(&chip->reg_lock, flags); - snd_azf3328_dbgcallleave(); + spin_unlock_irqrestore(codec->lock, flags); } static inline void -snd_azf3328_codec_setfmt_lowpower(struct snd_azf3328 *chip, - enum snd_azf3328_codec_type codec_type +snd_azf3328_codec_setfmt_lowpower(struct snd_azf3328_codec_data *codec ) { /* choose lowest frequency for low power consumption. * While this will cause louder noise due to rather coarse frequency, * it should never matter since output should always * get disabled properly when idle anyway. */ - snd_azf3328_codec_setfmt(chip, codec_type, AZF_FREQ_4000, 8, 1); + snd_azf3328_codec_setfmt(codec, AZF_FREQ_4000, 8, 1); } static void @@ -1041,15 +1336,16 @@ snd_azf3328_ctrl_reg_6AH_update(struct snd_azf3328 *chip, chip->shadow_reg_ctrl_6AH |= bitmask; else chip->shadow_reg_ctrl_6AH &= ~bitmask; - snd_azf3328_dbgcodec("6AH_update mask 0x%04x do_mask %d: val 0x%04x\n", - bitmask, do_mask, chip->shadow_reg_ctrl_6AH); + dev_dbg(chip->card->dev, + "6AH_update mask 0x%04x do_mask %d: val 0x%04x\n", + bitmask, do_mask, chip->shadow_reg_ctrl_6AH); snd_azf3328_ctrl_outw(chip, IDX_IO_6AH, chip->shadow_reg_ctrl_6AH); } static inline void snd_azf3328_ctrl_enable_codecs(struct snd_azf3328 *chip, bool enable) { - snd_azf3328_dbgcodec("codec_enable %d\n", enable); + dev_dbg(chip->card->dev, "codec_enable %d\n", enable); /* no idea what exactly is being done here, but I strongly assume it's * PM related */ snd_azf3328_ctrl_reg_6AH_update( @@ -1066,7 +1362,7 @@ snd_azf3328_ctrl_codec_activity(struct snd_azf3328 *chip, struct snd_azf3328_codec_data *codec = &chip->codecs[codec_type]; bool need_change = (codec->running != enable); - snd_azf3328_dbgcodec( + dev_dbg(chip->card->dev, "codec_activity: %s codec, enable %d, need_change %d\n", codec->name, enable, need_change ); @@ -1101,106 +1397,118 @@ snd_azf3328_ctrl_codec_activity(struct snd_azf3328 *chip, /* ...and adjust clock, too * (reduce noise and power consumption) */ if (!enable) - snd_azf3328_codec_setfmt_lowpower( - chip, - codec_type - ); + snd_azf3328_codec_setfmt_lowpower(codec); codec->running = enable; } } static void snd_azf3328_codec_setdmaa(struct snd_azf3328 *chip, - enum snd_azf3328_codec_type codec_type, - unsigned long addr, - unsigned int count, - unsigned int size + struct snd_azf3328_codec_data *codec, + unsigned long addr, + unsigned int period_bytes, + unsigned int buffer_bytes ) { - const struct snd_azf3328_codec_data *codec = &chip->codecs[codec_type]; - snd_azf3328_dbgcallenter(); + WARN_ONCE(period_bytes & 1, "odd period length!?\n"); + WARN_ONCE(buffer_bytes != 2 * period_bytes, + "missed our input expectations! %u vs. %u\n", + buffer_bytes, period_bytes); if (!codec->running) { /* AZF3328 uses a two buffer pointer DMA transfer approach */ - unsigned long flags, addr_area2; + unsigned long flags; /* width 32bit (prevent overflow): */ - u32 count_areas, lengths; + u32 area_length; + struct codec_setup_io { + u32 dma_start_1; + u32 dma_start_2; + u32 dma_lengths; + } __attribute__((packed)) setup_io; + + area_length = buffer_bytes/2; - count_areas = size/2; - addr_area2 = addr+count_areas; - count_areas--; /* max. index */ - snd_azf3328_dbgcodec("setdma: buffers %08lx[%u] / %08lx[%u]\n", - addr, count_areas, addr_area2, count_areas); + setup_io.dma_start_1 = addr; + setup_io.dma_start_2 = addr+area_length; + + dev_dbg(chip->card->dev, + "setdma: buffers %08x[%u] / %08x[%u], %u, %u\n", + setup_io.dma_start_1, area_length, + setup_io.dma_start_2, area_length, + period_bytes, buffer_bytes); + + /* Hmm, are we really supposed to decrement this by 1?? + Most definitely certainly not: configuring full length does + work properly (i.e. likely better), and BTW we + violated possibly differing frame sizes with this... + + area_length--; |* max. index *| + */ /* build combined I/O buffer length word */ - lengths = (count_areas << 16) | (count_areas); - spin_lock_irqsave(&chip->reg_lock, flags); - snd_azf3328_codec_outl(codec, IDX_IO_CODEC_DMA_START_1, addr); - snd_azf3328_codec_outl(codec, IDX_IO_CODEC_DMA_START_2, - addr_area2); - snd_azf3328_codec_outl(codec, IDX_IO_CODEC_DMA_LENGTHS, - lengths); - spin_unlock_irqrestore(&chip->reg_lock, flags); + setup_io.dma_lengths = (area_length << 16) | (area_length); + + spin_lock_irqsave(codec->lock, flags); + snd_azf3328_codec_outl_multi( + codec, IDX_IO_CODEC_DMA_START_1, &setup_io, 3 + ); + spin_unlock_irqrestore(codec->lock, flags); } - snd_azf3328_dbgcallleave(); } static int -snd_azf3328_codec_prepare(struct snd_pcm_substream *substream) +snd_azf3328_pcm_prepare(struct snd_pcm_substream *substream) { -#if 0 - struct snd_azf3328 *chip = snd_pcm_substream_chip(substream); struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_azf3328_codec_data *codec = runtime->private_data; +#if 0 unsigned int size = snd_pcm_lib_buffer_bytes(substream); unsigned int count = snd_pcm_lib_period_bytes(substream); #endif - snd_azf3328_dbgcallenter(); + codec->dma_base = runtime->dma_addr; + #if 0 - snd_azf3328_codec_setfmt(chip, AZF_CODEC_..., + snd_azf3328_codec_setfmt(codec, runtime->rate, snd_pcm_format_width(runtime->format), runtime->channels); - snd_azf3328_codec_setdmaa(chip, AZF_CODEC_..., + snd_azf3328_codec_setdmaa(chip, codec, runtime->dma_addr, count, size); #endif - snd_azf3328_dbgcallleave(); return 0; } static int -snd_azf3328_codec_trigger(enum snd_azf3328_codec_type codec_type, - struct snd_pcm_substream *substream, int cmd) +snd_azf3328_pcm_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_azf3328 *chip = snd_pcm_substream_chip(substream); - const struct snd_azf3328_codec_data *codec = &chip->codecs[codec_type]; struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_azf3328_codec_data *codec = runtime->private_data; int result = 0; u16 flags1; - bool previously_muted = 0; - bool is_playback_codec = (AZF_CODEC_PLAYBACK == codec_type); - - snd_azf3328_dbgcalls("snd_azf3328_codec_trigger cmd %d\n", cmd); + bool previously_muted = false; + bool is_main_mixer_playback_codec = (AZF_CODEC_PLAYBACK == codec->type); switch (cmd) { case SNDRV_PCM_TRIGGER_START: - snd_azf3328_dbgcodec("START %s\n", codec->name); + dev_dbg(chip->card->dev, "START PCM %s\n", codec->name); - if (is_playback_codec) { + 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 ); } - snd_azf3328_codec_setfmt(chip, codec_type, + snd_azf3328_codec_setfmt(codec, runtime->rate, snd_pcm_format_width(runtime->format), runtime->channels); - spin_lock(&chip->reg_lock); + spin_lock(codec->lock); /* first, remember current value: */ flags1 = snd_azf3328_codec_inw(codec, IDX_IO_CODEC_DMA_FLAGS); @@ -1210,14 +1518,14 @@ snd_azf3328_codec_trigger(enum snd_azf3328_codec_type codec_type, /* FIXME: clear interrupts or what??? */ snd_azf3328_codec_outw(codec, IDX_IO_CODEC_IRQTYPE, 0xffff); - spin_unlock(&chip->reg_lock); + spin_unlock(codec->lock); - snd_azf3328_codec_setdmaa(chip, codec_type, runtime->dma_addr, + snd_azf3328_codec_setdmaa(chip, codec, runtime->dma_addr, snd_pcm_lib_period_bytes(substream), snd_pcm_lib_buffer_bytes(substream) ); - spin_lock(&chip->reg_lock); + spin_lock(codec->lock); #ifdef WIN9X /* FIXME: enable playback/recording??? */ flags1 |= DMA_RUN_SOMETHING1 | DMA_RUN_SOMETHING2; @@ -1241,43 +1549,43 @@ snd_azf3328_codec_trigger(enum snd_azf3328_codec_type codec_type, DMA_EPILOGUE_SOMETHING | DMA_SOMETHING_ELSE); #endif - spin_unlock(&chip->reg_lock); - snd_azf3328_ctrl_codec_activity(chip, codec_type, 1); + spin_unlock(codec->lock); + snd_azf3328_ctrl_codec_activity(chip, codec->type, 1); - if (is_playback_codec) { + 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 ); } - snd_azf3328_dbgcodec("STARTED %s\n", codec->name); + dev_dbg(chip->card->dev, "PCM STARTED %s\n", codec->name); break; case SNDRV_PCM_TRIGGER_RESUME: - snd_azf3328_dbgcodec("RESUME %s\n", codec->name); + dev_dbg(chip->card->dev, "PCM RESUME %s\n", codec->name); /* resume codec if we were active */ - spin_lock(&chip->reg_lock); + spin_lock(codec->lock); if (codec->running) snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, snd_azf3328_codec_inw( codec, IDX_IO_CODEC_DMA_FLAGS ) | DMA_RESUME ); - spin_unlock(&chip->reg_lock); + spin_unlock(codec->lock); break; case SNDRV_PCM_TRIGGER_STOP: - snd_azf3328_dbgcodec("STOP %s\n", codec->name); + dev_dbg(chip->card->dev, "PCM STOP %s\n", codec->name); - if (is_playback_codec) { + 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 ); } - spin_lock(&chip->reg_lock); + spin_lock(codec->lock); /* first, remember current value: */ flags1 = snd_azf3328_codec_inw(codec, IDX_IO_CODEC_DMA_FLAGS); @@ -1292,21 +1600,21 @@ snd_azf3328_codec_trigger(enum snd_azf3328_codec_type codec_type, flags1 &= ~DMA_RUN_SOMETHING1; snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, flags1); - spin_unlock(&chip->reg_lock); - snd_azf3328_ctrl_codec_activity(chip, codec_type, 0); + spin_unlock(codec->lock); + snd_azf3328_ctrl_codec_activity(chip, codec->type, 0); - if (is_playback_codec) { + 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 ); } - snd_azf3328_dbgcodec("STOPPED %s\n", codec->name); + dev_dbg(chip->card->dev, "PCM STOPPED %s\n", codec->name); break; case SNDRV_PCM_TRIGGER_SUSPEND: - snd_azf3328_dbgcodec("SUSPEND %s\n", codec->name); + dev_dbg(chip->card->dev, "PCM SUSPEND %s\n", codec->name); /* make sure codec is stopped */ snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, snd_azf3328_codec_inw( @@ -1315,81 +1623,42 @@ snd_azf3328_codec_trigger(enum snd_azf3328_codec_type codec_type, ); break; case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - snd_printk(KERN_ERR "FIXME: SNDRV_PCM_TRIGGER_PAUSE_PUSH NIY!\n"); + WARN(1, "FIXME: SNDRV_PCM_TRIGGER_PAUSE_PUSH NIY!\n"); break; case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - snd_printk(KERN_ERR "FIXME: SNDRV_PCM_TRIGGER_PAUSE_RELEASE NIY!\n"); + WARN(1, "FIXME: SNDRV_PCM_TRIGGER_PAUSE_RELEASE NIY!\n"); break; default: - snd_printk(KERN_ERR "FIXME: unknown trigger mode!\n"); + WARN(1, "FIXME: unknown trigger mode!\n"); return -EINVAL; } - snd_azf3328_dbgcallleave(); return result; } -static int -snd_azf3328_codec_playback_trigger(struct snd_pcm_substream *substream, int cmd) -{ - return snd_azf3328_codec_trigger(AZF_CODEC_PLAYBACK, substream, cmd); -} - -static int -snd_azf3328_codec_capture_trigger(struct snd_pcm_substream *substream, int cmd) -{ - return snd_azf3328_codec_trigger(AZF_CODEC_CAPTURE, substream, cmd); -} - -static int -snd_azf3328_codec_i2s_out_trigger(struct snd_pcm_substream *substream, int cmd) -{ - return snd_azf3328_codec_trigger(AZF_CODEC_I2S_OUT, substream, cmd); -} - static snd_pcm_uframes_t -snd_azf3328_codec_pointer(struct snd_pcm_substream *substream, - enum snd_azf3328_codec_type codec_type +snd_azf3328_pcm_pointer(struct snd_pcm_substream *substream ) { - const struct snd_azf3328 *chip = snd_pcm_substream_chip(substream); - const struct snd_azf3328_codec_data *codec = &chip->codecs[codec_type]; - unsigned long bufptr, result; + const struct snd_azf3328_codec_data *codec = + substream->runtime->private_data; + unsigned long result; snd_pcm_uframes_t frmres; -#ifdef QUERY_HARDWARE - bufptr = snd_azf3328_codec_inl(codec, IDX_IO_CODEC_DMA_START_1); -#else - bufptr = substream->runtime->dma_addr; -#endif result = snd_azf3328_codec_inl(codec, IDX_IO_CODEC_DMA_CURRPOS); /* calculate offset */ - result -= bufptr; +#ifdef QUERY_HARDWARE + result -= snd_azf3328_codec_inl(codec, IDX_IO_CODEC_DMA_START_1); +#else + result -= codec->dma_base; +#endif frmres = bytes_to_frames( substream->runtime, result); - snd_azf3328_dbgcodec("%s @ 0x%8lx, frames %8ld\n", - codec->name, result, frmres); + dev_dbg(substream->pcm->card->dev, "%08li %s @ 0x%8lx, frames %8ld\n", + jiffies, codec->name, result, frmres); return frmres; } -static snd_pcm_uframes_t -snd_azf3328_codec_playback_pointer(struct snd_pcm_substream *substream) -{ - return snd_azf3328_codec_pointer(substream, AZF_CODEC_PLAYBACK); -} - -static snd_pcm_uframes_t -snd_azf3328_codec_capture_pointer(struct snd_pcm_substream *substream) -{ - return snd_azf3328_codec_pointer(substream, AZF_CODEC_CAPTURE); -} - -static snd_pcm_uframes_t -snd_azf3328_codec_i2s_out_pointer(struct snd_pcm_substream *substream) -{ - return snd_azf3328_codec_pointer(substream, AZF_CODEC_I2S_OUT); -} - /******************************************************************/ #ifdef SUPPORT_GAMEPORT @@ -1449,7 +1718,7 @@ snd_azf3328_gameport_interrupt(struct snd_azf3328 *chip) * skeleton handler only * (we do not want axis reading in interrupt handler - too much load!) */ - snd_azf3328_dbggame("gameport irq\n"); + dev_dbg(chip->card->dev, "gameport irq\n"); /* this should ACK the gameport IRQ properly, hopefully. */ snd_azf3328_game_inw(chip, IDX_GAME_AXIS_VALUE); @@ -1461,7 +1730,7 @@ snd_azf3328_gameport_open(struct gameport *gameport, int mode) struct snd_azf3328 *chip = gameport_get_port_data(gameport); int res; - snd_azf3328_dbggame("gameport_open, mode %d\n", mode); + dev_dbg(chip->card->dev, "gameport_open, mode %d\n", mode); switch (mode) { case GAMEPORT_MODE_COOKED: case GAMEPORT_MODE_RAW: @@ -1484,7 +1753,7 @@ snd_azf3328_gameport_close(struct gameport *gameport) { struct snd_azf3328 *chip = gameport_get_port_data(gameport); - snd_azf3328_dbggame("gameport_close\n"); + dev_dbg(chip->card->dev, "gameport_close\n"); snd_azf3328_gameport_set_counter_frequency(chip, GAME_HWCFG_ADC_COUNTER_FREQ_1_200); snd_azf3328_gameport_axis_circuit_enable(chip, 0); @@ -1531,7 +1800,7 @@ snd_azf3328_gameport_cooked_read(struct gameport *gameport, } } - /* trigger next axes sampling, to be evaluated the next time we + /* trigger next sampling of axes, to be evaluated the next time we * enter this function */ /* for some very, very strange reason we cannot enable @@ -1549,21 +1818,20 @@ snd_azf3328_gameport_cooked_read(struct gameport *gameport, axes[i] = -1; } - snd_azf3328_dbggame("cooked_read: axes %d %d %d %d buttons %d\n", - axes[0], axes[1], axes[2], axes[3], *buttons - ); + dev_dbg(chip->card->dev, "cooked_read: axes %d %d %d %d buttons %d\n", + axes[0], axes[1], axes[2], axes[3], *buttons); return 0; } -static int __devinit +static int snd_azf3328_gameport(struct snd_azf3328 *chip, int dev) { struct gameport *gp; chip->gameport = gp = gameport_allocate_port(); if (!gp) { - printk(KERN_ERR "azt3328: cannot alloc memory for gameport\n"); + dev_err(chip->card->dev, "cannot alloc memory for gameport\n"); return -ENOMEM; } @@ -1607,57 +1875,55 @@ snd_azf3328_gameport_free(struct snd_azf3328 *chip) { } static inline void snd_azf3328_gameport_interrupt(struct snd_azf3328 *chip) { - printk(KERN_WARNING "huh, game port IRQ occurred!?\n"); + dev_warn(chip->card->dev, "huh, game port IRQ occurred!?\n"); } #endif /* SUPPORT_GAMEPORT */ /******************************************************************/ static inline void -snd_azf3328_irq_log_unknown_type(u8 which) +snd_azf3328_irq_log_unknown_type(struct snd_azf3328 *chip, u8 which) { - snd_azf3328_dbgcodec( - "azt3328: unknown IRQ type (%x) occurred, please report!\n", - which - ); + dev_dbg(chip->card->dev, + "unknown IRQ type (%x) occurred, please report!\n", + which); } static inline void -snd_azf3328_codec_interrupt(struct snd_azf3328 *chip, u8 status) +snd_azf3328_pcm_interrupt(struct snd_azf3328 *chip, + const struct snd_azf3328_codec_data *first_codec, + u8 status +) { u8 which; enum snd_azf3328_codec_type codec_type; - const struct snd_azf3328_codec_data *codec; + const struct snd_azf3328_codec_data *codec = first_codec; for (codec_type = AZF_CODEC_PLAYBACK; codec_type <= AZF_CODEC_I2S_OUT; - ++codec_type) { + ++codec_type, ++codec) { /* skip codec if there's no interrupt for it */ if (!(status & (1 << codec_type))) continue; - codec = &chip->codecs[codec_type]; - - spin_lock(&chip->reg_lock); + spin_lock(codec->lock); which = snd_azf3328_codec_inb(codec, IDX_IO_CODEC_IRQTYPE); /* ack all IRQ types immediately */ snd_azf3328_codec_outb(codec, IDX_IO_CODEC_IRQTYPE, which); - spin_unlock(&chip->reg_lock); + spin_unlock(codec->lock); - if ((chip->pcm[codec_type]) && (codec->substream)) { + if (codec->substream) { snd_pcm_period_elapsed(codec->substream); - snd_azf3328_dbgcodec("%s period done (#%x), @ %x\n", + dev_dbg(chip->card->dev, "%s period done (#%x), @ %x\n", codec->name, which, snd_azf3328_codec_inl( - codec, IDX_IO_CODEC_DMA_CURRPOS - ) - ); + codec, IDX_IO_CODEC_DMA_CURRPOS)); } else - printk(KERN_WARNING "azt3328: irq handler problem!\n"); + dev_warn(chip->card->dev, "irq handler problem!\n"); if (which & IRQ_SOMETHING) - snd_azf3328_irq_log_unknown_type(which); + snd_azf3328_irq_log_unknown_type(chip, which); } } @@ -1666,9 +1932,7 @@ snd_azf3328_interrupt(int irq, void *dev_id) { struct snd_azf3328 *chip = dev_id; u8 status; -#if DEBUG_CODEC static unsigned long irq_count; -#endif status = snd_azf3328_ctrl_inb(chip, IDX_IO_IRQSTATUS); @@ -1679,14 +1943,13 @@ snd_azf3328_interrupt(int irq, void *dev_id) )) return IRQ_NONE; /* must be interrupt for another device */ - snd_azf3328_dbgcodec( + dev_dbg(chip->card->dev, "irq_count %ld! IDX_IO_IRQSTATUS %04x\n", irq_count++ /* debug-only */, - status - ); + status); if (status & IRQ_TIMER) { - /* snd_azf3328_dbgcodec("timer %ld\n", + /* dev_dbg(chip->card->dev, "timer %ld\n", snd_azf3328_codec_inl(chip, IDX_IO_TIMER_VALUE) & TIMER_VALUE_MASK ); */ @@ -1696,11 +1959,11 @@ snd_azf3328_interrupt(int irq, void *dev_id) spin_lock(&chip->reg_lock); snd_azf3328_ctrl_outb(chip, IDX_IO_TIMER_VALUE + 3, 0x07); spin_unlock(&chip->reg_lock); - snd_azf3328_dbgcodec("azt3328: timer IRQ\n"); + dev_dbg(chip->card->dev, "timer IRQ\n"); } if (status & (IRQ_PLAYBACK|IRQ_RECORDING|IRQ_I2S_OUT)) - snd_azf3328_codec_interrupt(chip, status); + snd_azf3328_pcm_interrupt(chip, chip->codecs, status); if (status & IRQ_GAMEPORT) snd_azf3328_gameport_interrupt(chip); @@ -1712,7 +1975,7 @@ snd_azf3328_interrupt(int irq, void *dev_id) /* hmm, do we have to ack the IRQ here somehow? * If so, then I don't know how yet... */ - snd_azf3328_dbgcodec("azt3328: MPU401 IRQ\n"); + dev_dbg(chip->card->dev, "MPU401 IRQ\n"); } return IRQ_HANDLED; } @@ -1740,11 +2003,15 @@ static const struct snd_pcm_hardware snd_azf3328_hardware = .rate_max = AZF_FREQ_66200, .channels_min = 1, .channels_max = 2, - .buffer_bytes_max = 65536, - .period_bytes_min = 64, - .period_bytes_max = 65536, - .periods_min = 1, - .periods_max = 1024, + .buffer_bytes_max = (64*1024), + .period_bytes_min = 1024, + .period_bytes_max = (32*1024), + /* We simply have two DMA areas (instead of a list of descriptors + such as other cards); I believe that this is a fixed hardware + attribute and there isn't much driver magic to be done to expand it. + Thus indicate that we have at least and at most 2 periods. */ + .periods_min = 2, + .periods_max = 2, /* FIXME: maybe that card actually has a FIFO? * Hmm, it seems newer revisions do have one, but we still don't know * its size... */ @@ -1784,104 +2051,84 @@ snd_azf3328_pcm_open(struct snd_pcm_substream *substream, { struct snd_azf3328 *chip = snd_pcm_substream_chip(substream); struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_azf3328_codec_data *codec = &chip->codecs[codec_type]; - snd_azf3328_dbgcallenter(); - chip->codecs[codec_type].substream = substream; + codec->substream = substream; /* same parameters for all our codecs - at least we think so... */ runtime->hw = snd_azf3328_hardware; snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &snd_azf3328_hw_constraints_rates); - snd_azf3328_dbgcallleave(); + runtime->private_data = codec; return 0; } static int -snd_azf3328_playback_open(struct snd_pcm_substream *substream) +snd_azf3328_pcm_playback_open(struct snd_pcm_substream *substream) { return snd_azf3328_pcm_open(substream, AZF_CODEC_PLAYBACK); } static int -snd_azf3328_capture_open(struct snd_pcm_substream *substream) +snd_azf3328_pcm_capture_open(struct snd_pcm_substream *substream) { return snd_azf3328_pcm_open(substream, AZF_CODEC_CAPTURE); } static int -snd_azf3328_i2s_out_open(struct snd_pcm_substream *substream) +snd_azf3328_pcm_i2s_out_open(struct snd_pcm_substream *substream) { return snd_azf3328_pcm_open(substream, AZF_CODEC_I2S_OUT); } static int -snd_azf3328_pcm_close(struct snd_pcm_substream *substream, - enum snd_azf3328_codec_type codec_type +snd_azf3328_pcm_close(struct snd_pcm_substream *substream ) { - struct snd_azf3328 *chip = snd_pcm_substream_chip(substream); + struct snd_azf3328_codec_data *codec = + substream->runtime->private_data; - snd_azf3328_dbgcallenter(); - chip->codecs[codec_type].substream = NULL; - snd_azf3328_dbgcallleave(); + codec->substream = NULL; return 0; } -static int -snd_azf3328_playback_close(struct snd_pcm_substream *substream) -{ - return snd_azf3328_pcm_close(substream, AZF_CODEC_PLAYBACK); -} - -static int -snd_azf3328_capture_close(struct snd_pcm_substream *substream) -{ - return snd_azf3328_pcm_close(substream, AZF_CODEC_CAPTURE); -} - -static int -snd_azf3328_i2s_out_close(struct snd_pcm_substream *substream) -{ - return snd_azf3328_pcm_close(substream, AZF_CODEC_I2S_OUT); -} - /******************************************************************/ static struct snd_pcm_ops snd_azf3328_playback_ops = { - .open = snd_azf3328_playback_open, - .close = snd_azf3328_playback_close, + .open = snd_azf3328_pcm_playback_open, + .close = snd_azf3328_pcm_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = snd_azf3328_hw_params, .hw_free = snd_azf3328_hw_free, - .prepare = snd_azf3328_codec_prepare, - .trigger = snd_azf3328_codec_playback_trigger, - .pointer = snd_azf3328_codec_playback_pointer + .prepare = snd_azf3328_pcm_prepare, + .trigger = snd_azf3328_pcm_trigger, + .pointer = snd_azf3328_pcm_pointer }; static struct snd_pcm_ops snd_azf3328_capture_ops = { - .open = snd_azf3328_capture_open, - .close = snd_azf3328_capture_close, + .open = snd_azf3328_pcm_capture_open, + .close = snd_azf3328_pcm_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = snd_azf3328_hw_params, .hw_free = snd_azf3328_hw_free, - .prepare = snd_azf3328_codec_prepare, - .trigger = snd_azf3328_codec_capture_trigger, - .pointer = snd_azf3328_codec_capture_pointer + .prepare = snd_azf3328_pcm_prepare, + .trigger = snd_azf3328_pcm_trigger, + .pointer = snd_azf3328_pcm_pointer }; static struct snd_pcm_ops snd_azf3328_i2s_out_ops = { - .open = snd_azf3328_i2s_out_open, - .close = snd_azf3328_i2s_out_close, + .open = snd_azf3328_pcm_i2s_out_open, + .close = snd_azf3328_pcm_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = snd_azf3328_hw_params, .hw_free = snd_azf3328_hw_free, - .prepare = snd_azf3328_codec_prepare, - .trigger = snd_azf3328_codec_i2s_out_trigger, - .pointer = snd_azf3328_codec_i2s_out_pointer + .prepare = snd_azf3328_pcm_prepare, + .trigger = snd_azf3328_pcm_trigger, + .pointer = snd_azf3328_pcm_pointer }; -static int __devinit +static int snd_azf3328_pcm(struct snd_azf3328 *chip) { enum { AZF_PCMDEV_STD, AZF_PCMDEV_I2S_OUT, NUM_AZF_PCMDEVS }; /* pcm devices */ @@ -1889,8 +2136,6 @@ enum { AZF_PCMDEV_STD, AZF_PCMDEV_I2S_OUT, NUM_AZF_PCMDEVS }; /* pcm devices */ struct snd_pcm *pcm; int err; - snd_azf3328_dbgcallenter(); - err = snd_pcm_new(chip->card, "AZF3328 DSP", AZF_PCMDEV_STD, 1, 1, &pcm); if (err < 0) @@ -1927,7 +2172,6 @@ enum { AZF_PCMDEV_STD, AZF_PCMDEV_I2S_OUT, NUM_AZF_PCMDEVS }; /* pcm devices */ snd_dma_pci_data(chip->pci), 64*1024, 64*1024); - snd_azf3328_dbgcallleave(); return 0; } @@ -1950,7 +2194,6 @@ snd_azf3328_timer_start(struct snd_timer *timer) unsigned long flags; unsigned int delay; - snd_azf3328_dbgcallenter(); chip = snd_timer_chip(timer); delay = ((timer->sticks * seqtimer_scaling) - 1) & TIMER_VALUE_MASK; if (delay < 49) { @@ -1958,15 +2201,14 @@ snd_azf3328_timer_start(struct snd_timer *timer) * this timing tweak * (we need to do it to avoid a lockup, though) */ - snd_azf3328_dbgtimer("delay was too low (%d)!\n", delay); + dev_dbg(chip->card->dev, "delay was too low (%d)!\n", delay); delay = 49; /* minimum time is 49 ticks */ } - snd_azf3328_dbgtimer("setting timer countdown value %d, add COUNTDOWN|IRQ\n", delay); + dev_dbg(chip->card->dev, "setting timer countdown value %d\n", delay); delay |= TIMER_COUNTDOWN_ENABLE | TIMER_IRQ_ENABLE; spin_lock_irqsave(&chip->reg_lock, flags); snd_azf3328_ctrl_outl(chip, IDX_IO_TIMER_VALUE, delay); spin_unlock_irqrestore(&chip->reg_lock, flags); - snd_azf3328_dbgcallleave(); return 0; } @@ -1976,14 +2218,17 @@ snd_azf3328_timer_stop(struct snd_timer *timer) struct snd_azf3328 *chip; unsigned long flags; - snd_azf3328_dbgcallenter(); chip = snd_timer_chip(timer); spin_lock_irqsave(&chip->reg_lock, flags); /* disable timer countdown and interrupt */ - /* FIXME: should we write TIMER_IRQ_ACK here? */ - snd_azf3328_ctrl_outb(chip, IDX_IO_TIMER_VALUE + 3, 0); + /* Hmm, should we write TIMER_IRQ_ACK here? + YES indeed, otherwise a rogue timer operation - which prompts + ALSA(?) to call repeated stop() in vain, but NOT start() - + will never end (value 0x03 is kept shown in control byte). + Simply manually poking 0x04 _once_ immediately successfully stops + the hardware/ALSA interrupt activity. */ + snd_azf3328_ctrl_outb(chip, IDX_IO_TIMER_VALUE + 3, 0x04); spin_unlock_irqrestore(&chip->reg_lock, flags); - snd_azf3328_dbgcallleave(); return 0; } @@ -1992,10 +2237,8 @@ static int snd_azf3328_timer_precise_resolution(struct snd_timer *timer, unsigned long *num, unsigned long *den) { - snd_azf3328_dbgcallenter(); *num = 1; *den = 1024000 / seqtimer_scaling; - snd_azf3328_dbgcallleave(); return 0; } @@ -2008,14 +2251,13 @@ static struct snd_timer_hardware snd_azf3328_timer_hw = { .precise_resolution = snd_azf3328_timer_precise_resolution, }; -static int __devinit +static int snd_azf3328_timer(struct snd_azf3328 *chip, int device) { struct snd_timer *timer = NULL; struct snd_timer_id tid; int err; - snd_azf3328_dbgcallenter(); tid.dev_class = SNDRV_TIMER_CLASS_CARD; tid.dev_sclass = SNDRV_TIMER_SCLASS_NONE; tid.card = chip->card->number; @@ -2040,7 +2282,6 @@ snd_azf3328_timer(struct snd_azf3328 *chip, int device) err = 0; out: - snd_azf3328_dbgcallleave(); return err; } @@ -2052,11 +2293,7 @@ snd_azf3328_free(struct snd_azf3328 *chip) 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); @@ -2106,34 +2343,34 @@ snd_azf3328_test_bit(unsigned unsigned reg, int bit) static inline void snd_azf3328_debug_show_ports(const struct snd_azf3328 *chip) { -#if DEBUG_MISC u16 tmp; - snd_azf3328_dbgmisc( + dev_dbg(chip->card->dev, "ctrl_io 0x%lx, game_io 0x%lx, mpu_io 0x%lx, " "opl3_io 0x%lx, mixer_io 0x%lx, irq %d\n", chip->ctrl_io, chip->game_io, chip->mpu_io, - chip->opl3_io, chip->mixer_io, chip->irq - ); + chip->opl3_io, chip->mixer_io, chip->irq); - snd_azf3328_dbgmisc("game %02x %02x %02x %02x %02x %02x\n", + dev_dbg(chip->card->dev, + "game %02x %02x %02x %02x %02x %02x\n", snd_azf3328_game_inb(chip, 0), snd_azf3328_game_inb(chip, 1), snd_azf3328_game_inb(chip, 2), snd_azf3328_game_inb(chip, 3), snd_azf3328_game_inb(chip, 4), - snd_azf3328_game_inb(chip, 5) - ); + snd_azf3328_game_inb(chip, 5)); for (tmp = 0; tmp < 0x07; tmp += 1) - snd_azf3328_dbgmisc("mpu_io 0x%04x\n", inb(chip->mpu_io + tmp)); + dev_dbg(chip->card->dev, + "mpu_io 0x%04x\n", inb(chip->mpu_io + tmp)); for (tmp = 0; tmp <= 0x07; tmp += 1) - snd_azf3328_dbgmisc("0x%02x: game200 0x%04x, game208 0x%04x\n", + dev_dbg(chip->card->dev, + "0x%02x: game200 0x%04x, game208 0x%04x\n", tmp, inb(0x200 + tmp), inb(0x208 + tmp)); for (tmp = 0; tmp <= 0x01; tmp += 1) - snd_azf3328_dbgmisc( + dev_dbg(chip->card->dev, "0x%02x: mpu300 0x%04x, mpu310 0x%04x, mpu320 0x%04x, " "mpu330 0x%04x opl388 0x%04x opl38c 0x%04x\n", tmp, @@ -2142,22 +2379,20 @@ snd_azf3328_debug_show_ports(const struct snd_azf3328 *chip) inb(0x320 + tmp), inb(0x330 + tmp), inb(0x388 + tmp), - inb(0x38c + tmp) - ); + inb(0x38c + tmp)); for (tmp = 0; tmp < AZF_IO_SIZE_CTRL; tmp += 2) - snd_azf3328_dbgmisc("ctrl 0x%02x: 0x%04x\n", - tmp, snd_azf3328_ctrl_inw(chip, tmp) - ); + dev_dbg(chip->card->dev, + "ctrl 0x%02x: 0x%04x\n", + tmp, snd_azf3328_ctrl_inw(chip, tmp)); for (tmp = 0; tmp < AZF_IO_SIZE_MIXER; tmp += 2) - snd_azf3328_dbgmisc("mixer 0x%02x: 0x%04x\n", - tmp, snd_azf3328_mixer_inw(chip, tmp) - ); -#endif /* DEBUG_MISC */ + dev_dbg(chip->card->dev, + "mixer 0x%02x: 0x%04x\n", + tmp, snd_azf3328_mixer_inw(chip, tmp)); } -static int __devinit +static int snd_azf3328_create(struct snd_card *card, struct pci_dev *pci, unsigned long device_type, @@ -2170,6 +2405,7 @@ snd_azf3328_create(struct snd_card *card, }; u8 dma_init; enum snd_azf3328_codec_type codec_type; + struct snd_azf3328_codec_data *codec_setup; *rchip = NULL; @@ -2190,8 +2426,8 @@ snd_azf3328_create(struct snd_card *card, /* check if we can restrict PCI DMA transfers to 24 bits */ if (pci_set_dma_mask(pci, DMA_BIT_MASK(24)) < 0 || pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(24)) < 0) { - snd_printk(KERN_ERR "architecture does not support " - "24bit PCI busmaster DMA\n" + dev_err(card->dev, + "architecture does not support 24bit PCI busmaster DMA\n" ); err = -ENXIO; goto out_err; @@ -2207,19 +2443,27 @@ snd_azf3328_create(struct snd_card *card, chip->opl3_io = pci_resource_start(pci, 3); chip->mixer_io = pci_resource_start(pci, 4); - chip->codecs[AZF_CODEC_PLAYBACK].io_base = - chip->ctrl_io + AZF_IO_OFFS_CODEC_PLAYBACK; - chip->codecs[AZF_CODEC_PLAYBACK].name = "PLAYBACK"; - chip->codecs[AZF_CODEC_CAPTURE].io_base = - chip->ctrl_io + AZF_IO_OFFS_CODEC_CAPTURE; - chip->codecs[AZF_CODEC_CAPTURE].name = "CAPTURE"; - chip->codecs[AZF_CODEC_I2S_OUT].io_base = - chip->ctrl_io + AZF_IO_OFFS_CODEC_I2S_OUT; - chip->codecs[AZF_CODEC_I2S_OUT].name = "I2S_OUT"; + codec_setup = &chip->codecs[AZF_CODEC_PLAYBACK]; + codec_setup->io_base = chip->ctrl_io + AZF_IO_OFFS_CODEC_PLAYBACK; + codec_setup->lock = &chip->reg_lock; + codec_setup->type = AZF_CODEC_PLAYBACK; + codec_setup->name = "PLAYBACK"; + + codec_setup = &chip->codecs[AZF_CODEC_CAPTURE]; + codec_setup->io_base = chip->ctrl_io + AZF_IO_OFFS_CODEC_CAPTURE; + codec_setup->lock = &chip->reg_lock; + codec_setup->type = AZF_CODEC_CAPTURE; + codec_setup->name = "CAPTURE"; + + codec_setup = &chip->codecs[AZF_CODEC_I2S_OUT]; + codec_setup->io_base = chip->ctrl_io + AZF_IO_OFFS_CODEC_I2S_OUT; + codec_setup->lock = &chip->reg_lock; + codec_setup->type = AZF_CODEC_I2S_OUT; + codec_setup->name = "I2S_OUT"; if (request_irq(pci->irq, snd_azf3328_interrupt, - IRQF_SHARED, card->shortname, chip)) { - snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq); + IRQF_SHARED, KBUILD_MODNAME, chip)) { + dev_err(card->dev, "unable to grab IRQ %d\n", pci->irq); err = -EBUSY; goto out_err; } @@ -2247,19 +2491,17 @@ snd_azf3328_create(struct snd_card *card, struct snd_azf3328_codec_data *codec = &chip->codecs[codec_type]; - /* shutdown codecs to save power */ + /* shutdown codecs to reduce power / noise */ /* have ...ctrl_codec_activity() act properly */ codec->running = 1; snd_azf3328_ctrl_codec_activity(chip, codec_type, 0); - spin_lock_irq(&chip->reg_lock); + spin_lock_irq(codec->lock); snd_azf3328_codec_outb(codec, IDX_IO_CODEC_DMA_FLAGS, dma_init); - spin_unlock_irq(&chip->reg_lock); + spin_unlock_irq(codec->lock); } - snd_card_set_dev(card, &pci->dev); - *rchip = chip; err = 0; @@ -2274,7 +2516,7 @@ out: return err; } -static int __devinit +static int snd_azf3328_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) { static int dev; @@ -2283,17 +2525,20 @@ snd_azf3328_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) struct snd_opl3 *opl3; int err; - snd_azf3328_dbgcallenter(); - if (dev >= SNDRV_CARDS) - return -ENODEV; + if (dev >= SNDRV_CARDS) { + err = -ENODEV; + goto out; + } if (!enable[dev]) { dev++; - return -ENOENT; + err = -ENOENT; + goto out; } - err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card); + err = snd_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE, + 0, &card); if (err < 0) - return err; + goto out; strcpy(card->driver, "AZF3328"); strcpy(card->shortname, "Aztech AZF3328 (PCI168)"); @@ -2308,11 +2553,12 @@ snd_azf3328_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) since our hardware ought to be similar, thus use same ID. */ err = snd_mpu401_uart_new( card, 0, - MPU401_HW_AZT2320, chip->mpu_io, MPU401_INFO_INTEGRATED, - pci->irq, 0, &chip->rmidi + MPU401_HW_AZT2320, chip->mpu_io, + MPU401_INFO_INTEGRATED | MPU401_INFO_IRQ_HOOK, + -1, &chip->rmidi ); if (err < 0) { - snd_printk(KERN_ERR "azf3328: no MPU-401 device at 0x%lx?\n", + dev_err(card->dev, "no MPU-401 device at 0x%lx?\n", chip->mpu_io ); goto out_err; @@ -2328,7 +2574,7 @@ snd_azf3328_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) if (snd_opl3_create(card, chip->opl3_io, chip->opl3_io+2, OPL3_HW_AUTO, 1, &opl3) < 0) { - snd_printk(KERN_ERR "azf3328: no OPL3 device at 0x%lx-0x%lx?\n", + dev_err(card->dev, "no OPL3 device at 0x%lx-0x%lx?\n", chip->opl3_io, chip->opl3_io+2 ); } else { @@ -2339,10 +2585,9 @@ snd_azf3328_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) err = snd_opl3_hwdep_new(opl3, 0, 1, NULL); if (err < 0) goto out_err; + opl3->private_data = chip; } - opl3->private_data = chip; - sprintf(card->longname, "%s at 0x%lx, irq %i", card->shortname, chip->ctrl_io, chip->irq); @@ -2351,12 +2596,15 @@ snd_azf3328_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) goto out_err; #ifdef MODULE - printk(KERN_INFO -"azt3328: Sound driver for Aztech AZF3328-based soundcards such as PCI168.\n" -"azt3328: Hardware was completely undocumented, unfortunately.\n" -"azt3328: Feel free to contact andi AT lisas.de for bug reports etc.!\n" -"azt3328: User-scalable sequencer timer set to %dHz (1024000Hz / %d).\n", - 1024000 / seqtimer_scaling, seqtimer_scaling); + dev_info(card->dev, + "Sound driver for Aztech AZF3328-based soundcards such as PCI168.\n"); + dev_info(card->dev, + "Hardware was completely undocumented, unfortunately.\n"); + dev_info(card->dev, + "Feel free to contact andi AT lisas.de for bug reports etc.!\n"); + dev_info(card->dev, + "User-scalable sequencer timer set to %dHz (1024000Hz / %d).\n", + 1024000 / seqtimer_scaling, seqtimer_scaling); #endif snd_azf3328_gameport(chip, dev); @@ -2368,163 +2616,168 @@ snd_azf3328_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) goto out; out_err: - snd_printk(KERN_ERR "azf3328: something failed, exiting\n"); + dev_err(card->dev, "something failed, exiting\n"); snd_card_free(card); out: - snd_azf3328_dbgcallleave(); return err; } -static void __devexit +static void snd_azf3328_remove(struct pci_dev *pci) { - snd_azf3328_dbgcallenter(); snd_card_free(pci_get_drvdata(pci)); - pci_set_drvdata(pci, NULL); - snd_azf3328_dbgcallleave(); } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static inline void -snd_azf3328_suspend_regs(unsigned long io_addr, unsigned count, u32 *saved_regs) +snd_azf3328_suspend_regs(const struct snd_azf3328 *chip, + unsigned long io_addr, unsigned count, u32 *saved_regs) { unsigned reg; for (reg = 0; reg < count; ++reg) { *saved_regs = inl(io_addr); - snd_azf3328_dbgpm("suspend: io 0x%04lx: 0x%08x\n", + dev_dbg(chip->card->dev, "suspend: io 0x%04lx: 0x%08x\n", io_addr, *saved_regs); ++saved_regs; io_addr += sizeof(*saved_regs); } } +static inline void +snd_azf3328_resume_regs(const struct snd_azf3328 *chip, + const u32 *saved_regs, + unsigned long io_addr, + unsigned count +) +{ + unsigned reg; + + for (reg = 0; reg < count; ++reg) { + outl(*saved_regs, io_addr); + dev_dbg(chip->card->dev, + "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, 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, 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) +snd_azf3328_suspend(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct snd_azf3328 *chip = card->private_data; u16 *saved_regs_ctrl_u16; snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); + /* same pcm object for playback/capture */ 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); + snd_azf3328_suspend_ac97(chip); - /* 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_regs(chip->ctrl_io, + snd_azf3328_suspend_regs(chip, chip->ctrl_io, ARRAY_SIZE(chip->saved_regs_ctrl), chip->saved_regs_ctrl); /* manually store the one currently relevant write-only reg, too */ saved_regs_ctrl_u16 = (u16 *)chip->saved_regs_ctrl; saved_regs_ctrl_u16[IDX_IO_6AH / 2] = chip->shadow_reg_ctrl_6AH; - snd_azf3328_suspend_regs(chip->game_io, + snd_azf3328_suspend_regs(chip, chip->game_io, ARRAY_SIZE(chip->saved_regs_game), chip->saved_regs_game); - snd_azf3328_suspend_regs(chip->mpu_io, + snd_azf3328_suspend_regs(chip, chip->mpu_io, ARRAY_SIZE(chip->saved_regs_mpu), chip->saved_regs_mpu); - snd_azf3328_suspend_regs(chip->opl3_io, + snd_azf3328_suspend_regs(chip, chip->opl3_io, ARRAY_SIZE(chip->saved_regs_opl3), chip->saved_regs_opl3); pci_disable_device(pci); pci_save_state(pci); - pci_set_power_state(pci, pci_choose_state(pci, state)); + pci_set_power_state(pci, PCI_D3hot); 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) +snd_azf3328_resume(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); const struct snd_azf3328 *chip = card->private_data; pci_set_power_state(pci, PCI_D0); pci_restore_state(pci); if (pci_enable_device(pci) < 0) { - printk(KERN_ERR "azt3328: pci_enable_device failed, " - "disabling device\n"); + dev_err(dev, "pci_enable_device failed, disabling device\n"); snd_card_disconnect(card); return -EIO; } pci_set_master(pci); - snd_azf3328_resume_regs(chip->saved_regs_game, chip->game_io, + snd_azf3328_resume_regs(chip, chip->saved_regs_game, chip->game_io, ARRAY_SIZE(chip->saved_regs_game)); - snd_azf3328_resume_regs(chip->saved_regs_mpu, chip->mpu_io, + snd_azf3328_resume_regs(chip, chip->saved_regs_mpu, chip->mpu_io, ARRAY_SIZE(chip->saved_regs_mpu)); - snd_azf3328_resume_regs(chip->saved_regs_opl3, chip->opl3_io, + snd_azf3328_resume_regs(chip, 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)); + snd_azf3328_resume_ac97(chip); - /* 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_regs(chip->saved_regs_ctrl, chip->ctrl_io, + snd_azf3328_resume_regs(chip, chip->saved_regs_ctrl, chip->ctrl_io, ARRAY_SIZE(chip->saved_regs_ctrl)); snd_power_change_state(card, SNDRV_CTL_POWER_D0); return 0; } -#endif /* CONFIG_PM */ +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 struct pci_driver driver = { - .name = "AZF3328", +static struct pci_driver azf3328_driver = { + .name = KBUILD_MODNAME, .id_table = snd_azf3328_ids, .probe = snd_azf3328_probe, - .remove = __devexit_p(snd_azf3328_remove), -#ifdef CONFIG_PM - .suspend = snd_azf3328_suspend, - .resume = snd_azf3328_resume, -#endif + .remove = snd_azf3328_remove, + .driver = { + .pm = SND_AZF3328_PM_OPS, + }, }; -static int __init -alsa_card_azf3328_init(void) -{ - int err; - snd_azf3328_dbgcallenter(); - err = pci_register_driver(&driver); - snd_azf3328_dbgcallleave(); - return err; -} - -static void __exit -alsa_card_azf3328_exit(void) -{ - snd_azf3328_dbgcallenter(); - pci_unregister_driver(&driver); - snd_azf3328_dbgcallleave(); -} - -module_init(alsa_card_azf3328_init) -module_exit(alsa_card_azf3328_exit) +module_pci_driver(azf3328_driver); |
