Commit b81e5ab3 authored by Takashi Iwai's avatar Takashi Iwai

Merge branch 'topic/azt3328' into for-linus

* topic/azt3328:
  ALSA: azt3328: fix previous breakage, improve suspend, cleanups
  ALSA: azt3328: large codec cleanup, add I2S port etc.
  ALSA: azt3328: fix Kconfig entry
parents e0b3032b 78df617a
...@@ -135,11 +135,11 @@ config SND_AW2 ...@@ -135,11 +135,11 @@ config SND_AW2
config SND_AZT3328 config SND_AZT3328
tristate "Aztech AZF3328 / PCI168 (EXPERIMENTAL)" tristate "Aztech AZF3328 / PCI168"
depends on EXPERIMENTAL
select SND_OPL3_LIB select SND_OPL3_LIB
select SND_MPU401_UART select SND_MPU401_UART
select SND_PCM select SND_PCM
select SND_RAWMIDI
help help
Say Y here to include support for Aztech AZF3328 (PCI168) Say Y here to include support for Aztech AZF3328 (PCI168)
soundcards. soundcards.
......
/* /*
* azt3328.c - driver for Aztech AZF3328 based soundcards (e.g. PCI168). * azt3328.c - driver for Aztech AZF3328 based soundcards (e.g. PCI168).
* Copyright (C) 2002, 2005 - 2008 by Andreas Mohr <andi AT lisas.de> * Copyright (C) 2002, 2005 - 2009 by Andreas Mohr <andi AT lisas.de>
* *
* Framework borrowed from Bart Hartgers's als4000.c. * Framework borrowed from Bart Hartgers's als4000.c.
* Driver developed on PCI168 AP(W) version (PCI rev. 10, subsystem ID 1801), * Driver developed on PCI168 AP(W) version (PCI rev. 10, subsystem ID 1801),
...@@ -10,6 +10,13 @@ ...@@ -10,6 +10,13 @@
* PCI168 A/AP, sub ID 8000 * PCI168 A/AP, sub ID 8000
* Please give me feedback in case you try my driver with one of these!! * Please give me feedback in case you try my driver with one of these!!
* *
* Keywords: Windows XP Vista 168nt4-125.zip 168win95-125.zip PCI 168 download
* (XP/Vista do not support this card at all but every Linux distribution
* has very good support out of the box;
* just to make sure that the right people hit this and get to know that,
* despite the high level of Internet ignorance - as usual :-P -
* about very good support for this card - on Linux!)
*
* GPL LICENSE * GPL LICENSE
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
...@@ -71,10 +78,11 @@ ...@@ -71,10 +78,11 @@
* - built-in General DirectX timer having a 20 bits counter * - built-in General DirectX timer having a 20 bits counter
* with 1us resolution (see below!) * with 1us resolution (see below!)
* - I2S serial output port for external DAC * - I2S serial output port for external DAC
* [FIXME: 3.3V or 5V level? maximum rate is 66.2kHz right?]
* - supports 33MHz PCI spec 2.1, PCI power management 1.0, compliant with ACPI * - supports 33MHz PCI spec 2.1, PCI power management 1.0, compliant with ACPI
* - supports hardware volume control * - supports hardware volume control
* - single chip low cost solution (128 pin QFP) * - single chip low cost solution (128 pin QFP)
* - supports programmable Sub-vendor and Sub-system ID * - supports programmable Sub-vendor and Sub-system ID [24C02 SEEPROM chip]
* required for Microsoft's logo compliance (FIXME: where?) * required for Microsoft's logo compliance (FIXME: where?)
* At least the Trident 4D Wave DX has one bit somewhere * At least the Trident 4D Wave DX has one bit somewhere
* to enable writes to PCI subsystem VID registers, that should be it. * to enable writes to PCI subsystem VID registers, that should be it.
...@@ -82,6 +90,7 @@ ...@@ -82,6 +90,7 @@
* some custom data starting at 0x80. What kind of config settings * some custom data starting at 0x80. What kind of config settings
* are located in our extended PCI space anyway?? * are located in our extended PCI space anyway??
* - PCI168 AP(W) card: power amplifier with 4 Watts/channel at 4 Ohms * - PCI168 AP(W) card: power amplifier with 4 Watts/channel at 4 Ohms
* [TDA1517P chip]
* *
* Note that this driver now is actually *better* than the Windows driver, * Note that this driver now is actually *better* than the Windows driver,
* since it additionally supports the card's 1MHz DirectX timer - just try * since it additionally supports the card's 1MHz DirectX timer - just try
...@@ -146,10 +155,15 @@ ...@@ -146,10 +155,15 @@
* to read the Digital Enhanced Game Port. Not sure whether it is fixable. * to read the Digital Enhanced Game Port. Not sure whether it is fixable.
* *
* TODO * TODO
* - use PCI_VDEVICE
* - verify driver status on x86_64
* - test multi-card driver operation
* - (ab)use 1MHz DirectX timer as kernel clocksource
* - test MPU401 MIDI playback etc. * - test MPU401 MIDI playback etc.
* - add more power micro-management (disable various units of the card * - add more power micro-management (disable various units of the card
* as long as they're unused). However this requires more I/O ports which I * as long as they're unused, to improve audio quality and save power).
* haven't figured out yet and which thus might not even exist... * However this requires more I/O ports which I haven't figured out yet
* and which thus might not even exist...
* The standard suspend/resume functionality could probably make use of * The standard suspend/resume functionality could probably make use of
* some improvement, too... * some improvement, too...
* - figure out what all unknown port bits are responsible for * - figure out what all unknown port bits are responsible for
...@@ -185,25 +199,46 @@ MODULE_SUPPORTED_DEVICE("{{Aztech,AZF3328}}"); ...@@ -185,25 +199,46 @@ MODULE_SUPPORTED_DEVICE("{{Aztech,AZF3328}}");
#define SUPPORT_GAMEPORT 1 #define SUPPORT_GAMEPORT 1
#endif #endif
/* === Debug settings ===
Further diagnostic functionality than the settings below
does not need to be provided, since one can easily write a bash script
to dump the card's I/O ports (those listed in lspci -v -v):
function 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
}
and then use something like
"dump joy200 0x200 8", "dump mpu388 0x388 4", "dump joy 0xb400 8",
"dump codec00 0xa800 32", "dump mixer 0xb800 64", "dump synth 0xbc00 8",
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
*/
#define DEBUG_MISC 0 #define DEBUG_MISC 0
#define DEBUG_CALLS 0 #define DEBUG_CALLS 0
#define DEBUG_MIXER 0 #define DEBUG_MIXER 0
#define DEBUG_PLAY_REC 0 #define DEBUG_CODEC 0
#define DEBUG_IO 0 #define DEBUG_IO 0
#define DEBUG_TIMER 0 #define DEBUG_TIMER 0
#define DEBUG_GAME 0 #define DEBUG_GAME 0
#define DEBUG_PM 0
#define MIXER_TESTING 0 #define MIXER_TESTING 0
#if DEBUG_MISC #if DEBUG_MISC
#define snd_azf3328_dbgmisc(format, args...) printk(KERN_ERR format, ##args) #define snd_azf3328_dbgmisc(format, args...) printk(KERN_DEBUG format, ##args)
#else #else
#define snd_azf3328_dbgmisc(format, args...) #define snd_azf3328_dbgmisc(format, args...)
#endif #endif
#if DEBUG_CALLS #if DEBUG_CALLS
#define snd_azf3328_dbgcalls(format, args...) printk(format, ##args) #define snd_azf3328_dbgcalls(format, args...) printk(format, ##args)
#define snd_azf3328_dbgcallenter() printk(KERN_ERR "--> %s\n", __func__) #define snd_azf3328_dbgcallenter() printk(KERN_DEBUG "--> %s\n", __func__)
#define snd_azf3328_dbgcallleave() printk(KERN_ERR "<-- %s\n", __func__) #define snd_azf3328_dbgcallleave() printk(KERN_DEBUG "<-- %s\n", __func__)
#else #else
#define snd_azf3328_dbgcalls(format, args...) #define snd_azf3328_dbgcalls(format, args...)
#define snd_azf3328_dbgcallenter() #define snd_azf3328_dbgcallenter()
...@@ -216,10 +251,10 @@ MODULE_SUPPORTED_DEVICE("{{Aztech,AZF3328}}"); ...@@ -216,10 +251,10 @@ MODULE_SUPPORTED_DEVICE("{{Aztech,AZF3328}}");
#define snd_azf3328_dbgmixer(format, args...) #define snd_azf3328_dbgmixer(format, args...)
#endif #endif
#if DEBUG_PLAY_REC #if DEBUG_CODEC
#define snd_azf3328_dbgplay(format, args...) printk(KERN_DEBUG format, ##args) #define snd_azf3328_dbgcodec(format, args...) printk(KERN_DEBUG format, ##args)
#else #else
#define snd_azf3328_dbgplay(format, args...) #define snd_azf3328_dbgcodec(format, args...)
#endif #endif
#if DEBUG_MISC #if DEBUG_MISC
...@@ -234,6 +269,12 @@ MODULE_SUPPORTED_DEVICE("{{Aztech,AZF3328}}"); ...@@ -234,6 +269,12 @@ MODULE_SUPPORTED_DEVICE("{{Aztech,AZF3328}}");
#define snd_azf3328_dbggame(format, args...) #define snd_azf3328_dbggame(format, args...)
#endif #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 */ static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
module_param_array(index, int, NULL, 0444); module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for AZF3328 soundcard."); MODULE_PARM_DESC(index, "Index value for AZF3328 soundcard.");
...@@ -250,22 +291,23 @@ static int seqtimer_scaling = 128; ...@@ -250,22 +291,23 @@ static int seqtimer_scaling = 128;
module_param(seqtimer_scaling, int, 0444); module_param(seqtimer_scaling, int, 0444);
MODULE_PARM_DESC(seqtimer_scaling, "Set 1024000Hz sequencer timer scale factor (lockup danger!). Default 128."); MODULE_PARM_DESC(seqtimer_scaling, "Set 1024000Hz sequencer timer scale factor (lockup danger!). Default 128.");
struct snd_azf3328_audio_stream { struct snd_azf3328_codec_data {
unsigned long io_base;
struct snd_pcm_substream *substream; struct snd_pcm_substream *substream;
int enabled; bool running;
int running; const char *name;
unsigned long portbase;
}; };
enum snd_azf3328_stream_index { enum snd_azf3328_codec_type {
AZF_PLAYBACK = 0, AZF_CODEC_PLAYBACK = 0,
AZF_CAPTURE = 1, AZF_CODEC_CAPTURE = 1,
AZF_CODEC_I2S_OUT = 2,
}; };
struct snd_azf3328 { struct snd_azf3328 {
/* often-used fields towards beginning, then grouped */ /* often-used fields towards beginning, then grouped */
unsigned long codec_io; /* usually 0xb000, size 128 */ unsigned long ctrl_io; /* usually 0xb000, size 128 */
unsigned long game_io; /* usually 0xb400, size 8 */ unsigned long game_io; /* usually 0xb400, size 8 */
unsigned long mpu_io; /* usually 0xb800, size 4 */ unsigned long mpu_io; /* usually 0xb800, size 4 */
unsigned long opl3_io; /* usually 0xbc00, size 8 */ unsigned long opl3_io; /* usually 0xbc00, size 8 */
...@@ -275,15 +317,17 @@ struct snd_azf3328 { ...@@ -275,15 +317,17 @@ struct snd_azf3328 {
struct snd_timer *timer; struct snd_timer *timer;
struct snd_pcm *pcm; struct snd_pcm *pcm[3];
struct snd_azf3328_audio_stream audio_stream[2];
/* playback, recording and I2S out codecs */
struct snd_azf3328_codec_data codecs[3];
struct snd_card *card; struct snd_card *card;
struct snd_rawmidi *rmidi; struct snd_rawmidi *rmidi;
#ifdef SUPPORT_GAMEPORT #ifdef SUPPORT_GAMEPORT
struct gameport *gameport; struct gameport *gameport;
int axes[4]; u16 axes[4];
#endif #endif
struct pci_dev *pci; struct pci_dev *pci;
...@@ -293,16 +337,16 @@ struct snd_azf3328 { ...@@ -293,16 +337,16 @@ struct snd_azf3328 {
* If we need to add more registers here, then we might try to fold this * If we need to add more registers here, then we might try to fold this
* into some transparent combined shadow register handling with * into some transparent combined shadow register handling with
* CONFIG_PM register storage below, but that's slightly difficult. */ * CONFIG_PM register storage below, but that's slightly difficult. */
u16 shadow_reg_codec_6AH; u16 shadow_reg_ctrl_6AH;
#ifdef CONFIG_PM #ifdef CONFIG_PM
/* register value containers for power management /* register value containers for power management
* Note: not always full I/O range preserved (just like Win driver!) */ * Note: not always full I/O range preserved (similar to Win driver!) */
u16 saved_regs_codec[AZF_IO_SIZE_CODEC_PM / 2]; u32 saved_regs_ctrl[AZF_ALIGN(AZF_IO_SIZE_CTRL_PM) / 4];
u16 saved_regs_game [AZF_IO_SIZE_GAME_PM / 2]; u32 saved_regs_game[AZF_ALIGN(AZF_IO_SIZE_GAME_PM) / 4];
u16 saved_regs_mpu [AZF_IO_SIZE_MPU_PM / 2]; u32 saved_regs_mpu[AZF_ALIGN(AZF_IO_SIZE_MPU_PM) / 4];
u16 saved_regs_opl3 [AZF_IO_SIZE_OPL3_PM / 2]; u32 saved_regs_opl3[AZF_ALIGN(AZF_IO_SIZE_OPL3_PM) / 4];
u16 saved_regs_mixer[AZF_IO_SIZE_MIXER_PM / 2]; u32 saved_regs_mixer[AZF_ALIGN(AZF_IO_SIZE_MIXER_PM) / 4];
#endif #endif
}; };
...@@ -316,7 +360,7 @@ MODULE_DEVICE_TABLE(pci, snd_azf3328_ids); ...@@ -316,7 +360,7 @@ MODULE_DEVICE_TABLE(pci, snd_azf3328_ids);
static int static int
snd_azf3328_io_reg_setb(unsigned reg, u8 mask, int do_set) snd_azf3328_io_reg_setb(unsigned reg, u8 mask, bool do_set)
{ {
u8 prev = inb(reg), new; u8 prev = inb(reg), new;
...@@ -331,39 +375,72 @@ snd_azf3328_io_reg_setb(unsigned reg, u8 mask, int do_set) ...@@ -331,39 +375,72 @@ snd_azf3328_io_reg_setb(unsigned reg, u8 mask, int do_set)
} }
static inline void static inline void
snd_azf3328_codec_outb(const struct snd_azf3328 *chip, unsigned reg, u8 value) snd_azf3328_codec_outb(const struct snd_azf3328_codec_data *codec,
unsigned reg,
u8 value
)
{ {
outb(value, chip->codec_io + reg); outb(value, codec->io_base + reg);
} }
static inline u8 static inline u8
snd_azf3328_codec_inb(const struct snd_azf3328 *chip, unsigned reg) snd_azf3328_codec_inb(const struct snd_azf3328_codec_data *codec, unsigned reg)
{ {
return inb(chip->codec_io + reg); return inb(codec->io_base + reg);
} }
static inline void static inline void
snd_azf3328_codec_outw(const struct snd_azf3328 *chip, unsigned reg, u16 value) snd_azf3328_codec_outw(const struct snd_azf3328_codec_data *codec,
unsigned reg,
u16 value
)
{ {
outw(value, chip->codec_io + reg); outw(value, codec->io_base + reg);
} }
static inline u16 static inline u16
snd_azf3328_codec_inw(const struct snd_azf3328 *chip, unsigned reg) snd_azf3328_codec_inw(const struct snd_azf3328_codec_data *codec, unsigned reg)
{ {
return inw(chip->codec_io + reg); return inw(codec->io_base + reg);
} }
static inline void static inline void
snd_azf3328_codec_outl(const struct snd_azf3328 *chip, unsigned reg, u32 value) snd_azf3328_codec_outl(const struct snd_azf3328_codec_data *codec,
unsigned reg,
u32 value
)
{ {
outl(value, chip->codec_io + reg); outl(value, codec->io_base + reg);
} }
static inline u32 static inline u32
snd_azf3328_codec_inl(const struct snd_azf3328 *chip, unsigned reg) snd_azf3328_codec_inl(const struct snd_azf3328_codec_data *codec, unsigned reg)
{
return inl(codec->io_base + reg);
}
static inline void
snd_azf3328_ctrl_outb(const struct snd_azf3328 *chip, unsigned reg, u8 value)
{
outb(value, chip->ctrl_io + reg);
}
static inline u8
snd_azf3328_ctrl_inb(const struct snd_azf3328 *chip, unsigned reg)
{
return inb(chip->ctrl_io + reg);
}
static inline void
snd_azf3328_ctrl_outw(const struct snd_azf3328 *chip, unsigned reg, u16 value)
{
outw(value, chip->ctrl_io + reg);
}
static inline void
snd_azf3328_ctrl_outl(const struct snd_azf3328 *chip, unsigned reg, u32 value)
{ {
return inl(chip->codec_io + reg); outl(value, chip->ctrl_io + reg);
} }
static inline void static inline void
...@@ -404,13 +481,13 @@ snd_azf3328_mixer_inw(const struct snd_azf3328 *chip, unsigned reg) ...@@ -404,13 +481,13 @@ snd_azf3328_mixer_inw(const struct snd_azf3328 *chip, unsigned reg)
#define AZF_MUTE_BIT 0x80 #define AZF_MUTE_BIT 0x80
static int static bool
snd_azf3328_mixer_set_mute(const struct snd_azf3328 *chip, snd_azf3328_mixer_set_mute(const struct snd_azf3328 *chip,
unsigned reg, int do_mute unsigned reg, bool do_mute
) )
{ {
unsigned long portbase = chip->mixer_io + reg + 1; unsigned long portbase = chip->mixer_io + reg + 1;
int updated; bool updated;
/* the mute bit is on the *second* (i.e. right) register of a /* the mute bit is on the *second* (i.e. right) register of a
* left/right channel setting */ * left/right channel setting */
...@@ -569,7 +646,7 @@ snd_azf3328_get_mixer(struct snd_kcontrol *kcontrol, ...@@ -569,7 +646,7 @@ snd_azf3328_get_mixer(struct snd_kcontrol *kcontrol,
{ {
struct snd_azf3328 *chip = snd_kcontrol_chip(kcontrol); struct snd_azf3328 *chip = snd_kcontrol_chip(kcontrol);
struct azf3328_mixer_reg reg; struct azf3328_mixer_reg reg;
unsigned int oreg, val; u16 oreg, val;
snd_azf3328_dbgcallenter(); snd_azf3328_dbgcallenter();
snd_azf3328_mixer_reg_decode(&reg, kcontrol->private_value); snd_azf3328_mixer_reg_decode(&reg, kcontrol->private_value);
...@@ -600,7 +677,7 @@ snd_azf3328_put_mixer(struct snd_kcontrol *kcontrol, ...@@ -600,7 +677,7 @@ snd_azf3328_put_mixer(struct snd_kcontrol *kcontrol,
{ {
struct snd_azf3328 *chip = snd_kcontrol_chip(kcontrol); struct snd_azf3328 *chip = snd_kcontrol_chip(kcontrol);
struct azf3328_mixer_reg reg; struct azf3328_mixer_reg reg;
unsigned int oreg, nreg, val; u16 oreg, nreg, val;
snd_azf3328_dbgcallenter(); snd_azf3328_dbgcallenter();
snd_azf3328_mixer_reg_decode(&reg, kcontrol->private_value); snd_azf3328_mixer_reg_decode(&reg, kcontrol->private_value);
...@@ -709,7 +786,7 @@ snd_azf3328_put_mixer_enum(struct snd_kcontrol *kcontrol, ...@@ -709,7 +786,7 @@ snd_azf3328_put_mixer_enum(struct snd_kcontrol *kcontrol,
{ {
struct snd_azf3328 *chip = snd_kcontrol_chip(kcontrol); struct snd_azf3328 *chip = snd_kcontrol_chip(kcontrol);
struct azf3328_mixer_reg reg; struct azf3328_mixer_reg reg;
unsigned int oreg, nreg, val; u16 oreg, nreg, val;
snd_azf3328_mixer_reg_decode(&reg, kcontrol->private_value); snd_azf3328_mixer_reg_decode(&reg, kcontrol->private_value);
oreg = snd_azf3328_mixer_inw(chip, reg.reg); oreg = snd_azf3328_mixer_inw(chip, reg.reg);
...@@ -867,14 +944,15 @@ snd_azf3328_hw_free(struct snd_pcm_substream *substream) ...@@ -867,14 +944,15 @@ snd_azf3328_hw_free(struct snd_pcm_substream *substream)
static void static void
snd_azf3328_codec_setfmt(struct snd_azf3328 *chip, snd_azf3328_codec_setfmt(struct snd_azf3328 *chip,
unsigned reg, enum snd_azf3328_codec_type codec_type,
enum azf_freq_t bitrate, enum azf_freq_t bitrate,
unsigned int format_width, unsigned int format_width,
unsigned int channels unsigned int channels
) )
{ {
u16 val = 0xff00;
unsigned long flags; unsigned long flags;
const struct snd_azf3328_codec_data *codec = &chip->codecs[codec_type];
u16 val = 0xff00;
snd_azf3328_dbgcallenter(); snd_azf3328_dbgcallenter();
switch (bitrate) { switch (bitrate) {
...@@ -917,7 +995,7 @@ snd_azf3328_codec_setfmt(struct snd_azf3328 *chip, ...@@ -917,7 +995,7 @@ snd_azf3328_codec_setfmt(struct snd_azf3328 *chip,
spin_lock_irqsave(&chip->reg_lock, flags); spin_lock_irqsave(&chip->reg_lock, flags);
/* set bitrate/format */ /* set bitrate/format */
snd_azf3328_codec_outw(chip, reg, val); snd_azf3328_codec_outw(codec, IDX_IO_CODEC_SOUNDFORMAT, val);
/* changing the bitrate/format settings switches off the /* changing the bitrate/format settings switches off the
* audio output with an annoying click in case of 8/16bit format change * audio output with an annoying click in case of 8/16bit format change
...@@ -926,11 +1004,11 @@ snd_azf3328_codec_setfmt(struct snd_azf3328 *chip, ...@@ -926,11 +1004,11 @@ snd_azf3328_codec_setfmt(struct snd_azf3328 *chip,
* (FIXME: yes, it works, but what exactly am I doing here?? :) * (FIXME: yes, it works, but what exactly am I doing here?? :)
* FIXME: does this have some side effects for full-duplex * FIXME: does this have some side effects for full-duplex
* or other dramatic side effects? */ * or other dramatic side effects? */
if (reg == IDX_IO_PLAY_SOUNDFORMAT) /* only do it for playback */ if (codec_type == AZF_CODEC_PLAYBACK) /* only do it for playback */
snd_azf3328_codec_outw(chip, IDX_IO_PLAY_FLAGS, snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS,
snd_azf3328_codec_inw(chip, IDX_IO_PLAY_FLAGS) | snd_azf3328_codec_inw(codec, IDX_IO_CODEC_DMA_FLAGS) |
DMA_PLAY_SOMETHING1 | DMA_RUN_SOMETHING1 |
DMA_PLAY_SOMETHING2 | DMA_RUN_SOMETHING2 |
SOMETHING_ALMOST_ALWAYS_SET | SOMETHING_ALMOST_ALWAYS_SET |
DMA_EPILOGUE_SOMETHING | DMA_EPILOGUE_SOMETHING |
DMA_SOMETHING_ELSE DMA_SOMETHING_ELSE
...@@ -942,112 +1020,134 @@ snd_azf3328_codec_setfmt(struct snd_azf3328 *chip, ...@@ -942,112 +1020,134 @@ snd_azf3328_codec_setfmt(struct snd_azf3328 *chip,
static inline void static inline void
snd_azf3328_codec_setfmt_lowpower(struct snd_azf3328 *chip, snd_azf3328_codec_setfmt_lowpower(struct snd_azf3328 *chip,
unsigned reg enum snd_azf3328_codec_type codec_type
) )
{ {
/* choose lowest frequency for low power consumption. /* choose lowest frequency for low power consumption.
* While this will cause louder noise due to rather coarse frequency, * While this will cause louder noise due to rather coarse frequency,
* it should never matter since output should always * it should never matter since output should always
* get disabled properly when idle anyway. */ * get disabled properly when idle anyway. */
snd_azf3328_codec_setfmt(chip, reg, AZF_FREQ_4000, 8, 1); snd_azf3328_codec_setfmt(chip, codec_type, AZF_FREQ_4000, 8, 1);
} }
static void static void
snd_azf3328_codec_reg_6AH_update(struct snd_azf3328 *chip, snd_azf3328_ctrl_reg_6AH_update(struct snd_azf3328 *chip,
unsigned bitmask, unsigned bitmask,
int enable bool enable
) )
{ {
if (enable) bool do_mask = !enable;
chip->shadow_reg_codec_6AH &= ~bitmask; if (do_mask)
chip->shadow_reg_ctrl_6AH |= bitmask;
else else
chip->shadow_reg_codec_6AH |= bitmask; chip->shadow_reg_ctrl_6AH &= ~bitmask;
snd_azf3328_dbgplay("6AH_update mask 0x%04x enable %d: val 0x%04x\n", snd_azf3328_dbgcodec("6AH_update mask 0x%04x do_mask %d: val 0x%04x\n",
bitmask, enable, chip->shadow_reg_codec_6AH); bitmask, do_mask, chip->shadow_reg_ctrl_6AH);
snd_azf3328_codec_outw(chip, IDX_IO_6AH, chip->shadow_reg_codec_6AH); snd_azf3328_ctrl_outw(chip, IDX_IO_6AH, chip->shadow_reg_ctrl_6AH);
} }
static inline void static inline void
snd_azf3328_codec_enable(struct snd_azf3328 *chip, int enable) snd_azf3328_ctrl_enable_codecs(struct snd_azf3328 *chip, bool enable)
{ {
snd_azf3328_dbgplay("codec_enable %d\n", enable); snd_azf3328_dbgcodec("codec_enable %d\n", enable);
/* no idea what exactly is being done here, but I strongly assume it's /* no idea what exactly is being done here, but I strongly assume it's
* PM related */ * PM related */
snd_azf3328_codec_reg_6AH_update( snd_azf3328_ctrl_reg_6AH_update(
chip, IO_6A_PAUSE_PLAYBACK_BIT8, enable chip, IO_6A_PAUSE_PLAYBACK_BIT8, enable
); );
} }
static void static void
snd_azf3328_codec_activity(struct snd_azf3328 *chip, snd_azf3328_ctrl_codec_activity(struct snd_azf3328 *chip,
enum snd_azf3328_stream_index stream_type, enum snd_azf3328_codec_type codec_type,
int enable bool enable
) )
{ {
int need_change = (chip->audio_stream[stream_type].running != enable); struct snd_azf3328_codec_data *codec = &chip->codecs[codec_type];
bool need_change = (codec->running != enable);
snd_azf3328_dbgplay( snd_azf3328_dbgcodec(
"codec_activity: type %d, enable %d, need_change %d\n", "codec_activity: %s codec, enable %d, need_change %d\n",
stream_type, enable, need_change codec->name, enable, need_change
); );
if (need_change) { if (need_change) {
enum snd_azf3328_stream_index other = static const struct {
(stream_type == AZF_PLAYBACK) ? enum snd_azf3328_codec_type other1;
AZF_CAPTURE : AZF_PLAYBACK; enum snd_azf3328_codec_type other2;
/* small check to prevent shutting down the other party } peer_codecs[3] =
* in case it's active */ { { AZF_CODEC_CAPTURE, AZF_CODEC_I2S_OUT },
if ((enable) || !(chip->audio_stream[other].running)) { AZF_CODEC_PLAYBACK, AZF_CODEC_I2S_OUT },
snd_azf3328_codec_enable(chip, enable); { AZF_CODEC_PLAYBACK, AZF_CODEC_CAPTURE } };
bool call_function;
if (enable)
/* if enable codec, call enable_codecs func
to enable codec supply... */
call_function = 1;
else {
/* ...otherwise call enable_codecs func
(which globally shuts down operation of codecs)
only in case the other codecs are currently
not active either! */
call_function =
((!chip->codecs[peer_codecs[codec_type].other1]
.running)
&& (!chip->codecs[peer_codecs[codec_type].other2]
.running));
}
if (call_function)
snd_azf3328_ctrl_enable_codecs(chip, enable);
/* ...and adjust clock, too /* ...and adjust clock, too
* (reduce noise and power consumption) */ * (reduce noise and power consumption) */
if (!enable) if (!enable)
snd_azf3328_codec_setfmt_lowpower( snd_azf3328_codec_setfmt_lowpower(
chip, chip,
chip->audio_stream[stream_type].portbase codec_type
+ IDX_IO_PLAY_SOUNDFORMAT
); );
codec->running = enable;
} }
chip->audio_stream[stream_type].running = enable;
} }
static void static void
snd_azf3328_setdmaa(struct snd_azf3328 *chip, snd_azf3328_codec_setdmaa(struct snd_azf3328 *chip,
long unsigned int addr, enum snd_azf3328_codec_type codec_type,
unsigned int count, unsigned long addr,
unsigned int size, unsigned int count,
enum snd_azf3328_stream_index stream_type unsigned int size
) )
{ {
const struct snd_azf3328_codec_data *codec = &chip->codecs[codec_type];
snd_azf3328_dbgcallenter(); snd_azf3328_dbgcallenter();
if (!chip->audio_stream[stream_type].running) { if (!codec->running) {
/* AZF3328 uses a two buffer pointer DMA playback approach */ /* AZF3328 uses a two buffer pointer DMA transfer approach */
unsigned long flags, portbase, addr_area2; unsigned long flags, addr_area2;
/* width 32bit (prevent overflow): */ /* width 32bit (prevent overflow): */
unsigned long count_areas, count_tmp; u32 count_areas, lengths;
portbase = chip->audio_stream[stream_type].portbase;
count_areas = size/2; count_areas = size/2;
addr_area2 = addr+count_areas; addr_area2 = addr+count_areas;
count_areas--; /* max. index */ count_areas--; /* max. index */
snd_azf3328_dbgplay("set DMA: buf1 %08lx[%lu], buf2 %08lx[%lu]\n", addr, count_areas, addr_area2, count_areas); snd_azf3328_dbgcodec("setdma: buffers %08lx[%u] / %08lx[%u]\n",
addr, count_areas, addr_area2, count_areas);
/* build combined I/O buffer length word */ /* build combined I/O buffer length word */
count_tmp = count_areas; lengths = (count_areas << 16) | (count_areas);
count_areas |= (count_tmp << 16);
spin_lock_irqsave(&chip->reg_lock, flags); spin_lock_irqsave(&chip->reg_lock, flags);
outl(addr, portbase + IDX_IO_PLAY_DMA_START_1); snd_azf3328_codec_outl(codec, IDX_IO_CODEC_DMA_START_1, addr);
outl(addr_area2, portbase + IDX_IO_PLAY_DMA_START_2); snd_azf3328_codec_outl(codec, IDX_IO_CODEC_DMA_START_2,
outl(count_areas, portbase + IDX_IO_PLAY_DMA_LEN_1); addr_area2);
snd_azf3328_codec_outl(codec, IDX_IO_CODEC_DMA_LENGTHS,
lengths);
spin_unlock_irqrestore(&chip->reg_lock, flags); spin_unlock_irqrestore(&chip->reg_lock, flags);
} }
snd_azf3328_dbgcallleave(); snd_azf3328_dbgcallleave();
} }
static int static int
snd_azf3328_playback_prepare(struct snd_pcm_substream *substream) snd_azf3328_codec_prepare(struct snd_pcm_substream *substream)
{ {
#if 0 #if 0
struct snd_azf3328 *chip = snd_pcm_substream_chip(substream); struct snd_azf3328 *chip = snd_pcm_substream_chip(substream);
...@@ -1058,157 +1158,161 @@ snd_azf3328_playback_prepare(struct snd_pcm_substream *substream) ...@@ -1058,157 +1158,161 @@ snd_azf3328_playback_prepare(struct snd_pcm_substream *substream)
snd_azf3328_dbgcallenter(); snd_azf3328_dbgcallenter();
#if 0 #if 0
snd_azf3328_codec_setfmt(chip, IDX_IO_PLAY_SOUNDFORMAT, snd_azf3328_codec_setfmt(chip, AZF_CODEC_...,
runtime->rate, runtime->rate,
snd_pcm_format_width(runtime->format), snd_pcm_format_width(runtime->format),
runtime->channels); runtime->channels);
snd_azf3328_setdmaa(chip, runtime->dma_addr, count, size, AZF_PLAYBACK); snd_azf3328_codec_setdmaa(chip, AZF_CODEC_...,
runtime->dma_addr, count, size);
#endif #endif
snd_azf3328_dbgcallleave(); snd_azf3328_dbgcallleave();
return 0; return 0;
} }
static int static int
snd_azf3328_capture_prepare(struct snd_pcm_substream *substream) snd_azf3328_codec_trigger(enum snd_azf3328_codec_type codec_type,
{ struct snd_pcm_substream *substream, int cmd)
#if 0
struct snd_azf3328 *chip = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
unsigned int size = snd_pcm_lib_buffer_bytes(substream);
unsigned int count = snd_pcm_lib_period_bytes(substream);
#endif
snd_azf3328_dbgcallenter();
#if 0
snd_azf3328_codec_setfmt(chip, IDX_IO_REC_SOUNDFORMAT,
runtime->rate,
snd_pcm_format_width(runtime->format),
runtime->channels);
snd_azf3328_setdmaa(chip, runtime->dma_addr, count, size, AZF_CAPTURE);
#endif
snd_azf3328_dbgcallleave();
return 0;
}
static int
snd_azf3328_playback_trigger(struct snd_pcm_substream *substream, int cmd)
{ {
struct snd_azf3328 *chip = snd_pcm_substream_chip(substream); 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_pcm_runtime *runtime = substream->runtime;
int result = 0; int result = 0;
unsigned int status1; u16 flags1;
int previously_muted; bool previously_muted = 0;
bool is_playback_codec = (AZF_CODEC_PLAYBACK == codec_type);
snd_azf3328_dbgcalls("snd_azf3328_playback_trigger cmd %d\n", cmd); snd_azf3328_dbgcalls("snd_azf3328_codec_trigger cmd %d\n", cmd);
switch (cmd) { switch (cmd) {
case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_START:
snd_azf3328_dbgplay("START PLAYBACK\n"); snd_azf3328_dbgcodec("START %s\n", codec->name);
/* mute WaveOut (avoid clicking during setup) */ if (is_playback_codec) {
previously_muted = /* mute WaveOut (avoid clicking during setup) */
snd_azf3328_mixer_set_mute(chip, IDX_MIXER_WAVEOUT, 1); previously_muted =
snd_azf3328_mixer_set_mute(
chip, IDX_MIXER_WAVEOUT, 1
);
}
snd_azf3328_codec_setfmt(chip, IDX_IO_PLAY_SOUNDFORMAT, snd_azf3328_codec_setfmt(chip, codec_type,
runtime->rate, runtime->rate,
snd_pcm_format_width(runtime->format), snd_pcm_format_width(runtime->format),
runtime->channels); runtime->channels);
spin_lock(&chip->reg_lock); spin_lock(&chip->reg_lock);
/* first, remember current value: */ /* first, remember current value: */
status1 = snd_azf3328_codec_inw(chip, IDX_IO_PLAY_FLAGS); flags1 = snd_azf3328_codec_inw(codec, IDX_IO_CODEC_DMA_FLAGS);
/* stop playback */ /* stop transfer */
status1 &= ~DMA_RESUME; flags1 &= ~DMA_RESUME;
snd_azf3328_codec_outw(chip, IDX_IO_PLAY_FLAGS, status1); snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, flags1);
/* FIXME: clear interrupts or what??? */ /* FIXME: clear interrupts or what??? */
snd_azf3328_codec_outw(chip, IDX_IO_PLAY_IRQTYPE, 0xffff); snd_azf3328_codec_outw(codec, IDX_IO_CODEC_IRQTYPE, 0xffff);
spin_unlock(&chip->reg_lock); spin_unlock(&chip->reg_lock);
snd_azf3328_setdmaa(chip, runtime->dma_addr, snd_azf3328_codec_setdmaa(chip, codec_type, runtime->dma_addr,
snd_pcm_lib_period_bytes(substream), snd_pcm_lib_period_bytes(substream),
snd_pcm_lib_buffer_bytes(substream), snd_pcm_lib_buffer_bytes(substream)
AZF_PLAYBACK); );
spin_lock(&chip->reg_lock); spin_lock(&chip->reg_lock);
#ifdef WIN9X #ifdef WIN9X
/* FIXME: enable playback/recording??? */ /* FIXME: enable playback/recording??? */
status1 |= DMA_PLAY_SOMETHING1 | DMA_PLAY_SOMETHING2; flags1 |= DMA_RUN_SOMETHING1 | DMA_RUN_SOMETHING2;
snd_azf3328_codec_outw(chip, IDX_IO_PLAY_FLAGS, status1); snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, flags1);
/* start playback again */ /* start transfer again */
/* FIXME: what is this value (0x0010)??? */ /* FIXME: what is this value (0x0010)??? */
status1 |= DMA_RESUME | DMA_EPILOGUE_SOMETHING; flags1 |= DMA_RESUME | DMA_EPILOGUE_SOMETHING;
snd_azf3328_codec_outw(chip, IDX_IO_PLAY_FLAGS, status1); snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, flags1);
#else /* NT4 */ #else /* NT4 */
snd_azf3328_codec_outw(chip, IDX_IO_PLAY_FLAGS, snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS,
0x0000); 0x0000);
snd_azf3328_codec_outw(chip, IDX_IO_PLAY_FLAGS, snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS,
DMA_PLAY_SOMETHING1); DMA_RUN_SOMETHING1);
snd_azf3328_codec_outw(chip, IDX_IO_PLAY_FLAGS, snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS,
DMA_PLAY_SOMETHING1 | DMA_RUN_SOMETHING1 |
DMA_PLAY_SOMETHING2); DMA_RUN_SOMETHING2);
snd_azf3328_codec_outw(chip, IDX_IO_PLAY_FLAGS, snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS,
DMA_RESUME | DMA_RESUME |
SOMETHING_ALMOST_ALWAYS_SET | SOMETHING_ALMOST_ALWAYS_SET |
DMA_EPILOGUE_SOMETHING | DMA_EPILOGUE_SOMETHING |
DMA_SOMETHING_ELSE); DMA_SOMETHING_ELSE);
#endif #endif
spin_unlock(&chip->reg_lock); spin_unlock(&chip->reg_lock);
snd_azf3328_codec_activity(chip, AZF_PLAYBACK, 1); snd_azf3328_ctrl_codec_activity(chip, codec_type, 1);
/* now unmute WaveOut */ if (is_playback_codec) {
if (!previously_muted) /* now unmute WaveOut */
snd_azf3328_mixer_set_mute(chip, IDX_MIXER_WAVEOUT, 0); if (!previously_muted)
snd_azf3328_mixer_set_mute(
chip, IDX_MIXER_WAVEOUT, 0
);
}
snd_azf3328_dbgplay("STARTED PLAYBACK\n"); snd_azf3328_dbgcodec("STARTED %s\n", codec->name);
break; break;
case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_RESUME:
snd_azf3328_dbgplay("RESUME PLAYBACK\n"); snd_azf3328_dbgcodec("RESUME %s\n", codec->name);
/* resume playback if we were active */ /* resume codec if we were active */
spin_lock(&chip->reg_lock); spin_lock(&chip->reg_lock);
if (chip->audio_stream[AZF_PLAYBACK].running) if (codec->running)
snd_azf3328_codec_outw(chip, IDX_IO_PLAY_FLAGS, snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS,
snd_azf3328_codec_inw(chip, IDX_IO_PLAY_FLAGS) | DMA_RESUME); snd_azf3328_codec_inw(
codec, IDX_IO_CODEC_DMA_FLAGS
) | DMA_RESUME
);
spin_unlock(&chip->reg_lock); spin_unlock(&chip->reg_lock);
break; break;
case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_STOP:
snd_azf3328_dbgplay("STOP PLAYBACK\n"); snd_azf3328_dbgcodec("STOP %s\n", codec->name);
/* mute WaveOut (avoid clicking during setup) */ if (is_playback_codec) {
previously_muted = /* mute WaveOut (avoid clicking during setup) */
snd_azf3328_mixer_set_mute(chip, IDX_MIXER_WAVEOUT, 1); previously_muted =
snd_azf3328_mixer_set_mute(
chip, IDX_MIXER_WAVEOUT, 1
);
}
spin_lock(&chip->reg_lock); spin_lock(&chip->reg_lock);
/* first, remember current value: */ /* first, remember current value: */
status1 = snd_azf3328_codec_inw(chip, IDX_IO_PLAY_FLAGS); flags1 = snd_azf3328_codec_inw(codec, IDX_IO_CODEC_DMA_FLAGS);
/* stop playback */ /* stop transfer */
status1 &= ~DMA_RESUME; flags1 &= ~DMA_RESUME;
snd_azf3328_codec_outw(chip, IDX_IO_PLAY_FLAGS, status1); snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, flags1);
/* hmm, is this really required? we're resetting the same bit /* hmm, is this really required? we're resetting the same bit
* immediately thereafter... */ * immediately thereafter... */
status1 |= DMA_PLAY_SOMETHING1; flags1 |= DMA_RUN_SOMETHING1;
snd_azf3328_codec_outw(chip, IDX_IO_PLAY_FLAGS, status1); snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, flags1);
status1 &= ~DMA_PLAY_SOMETHING1; flags1 &= ~DMA_RUN_SOMETHING1;
snd_azf3328_codec_outw(chip, IDX_IO_PLAY_FLAGS, status1); snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, flags1);
spin_unlock(&chip->reg_lock); spin_unlock(&chip->reg_lock);
snd_azf3328_codec_activity(chip, AZF_PLAYBACK, 0); snd_azf3328_ctrl_codec_activity(chip, codec_type, 0);
/* now unmute WaveOut */ if (is_playback_codec) {
if (!previously_muted) /* now unmute WaveOut */
snd_azf3328_mixer_set_mute(chip, IDX_MIXER_WAVEOUT, 0); if (!previously_muted)
snd_azf3328_mixer_set_mute(
chip, IDX_MIXER_WAVEOUT, 0
);
}
snd_azf3328_dbgplay("STOPPED PLAYBACK\n"); snd_azf3328_dbgcodec("STOPPED %s\n", codec->name);
break; break;
case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_SUSPEND:
snd_azf3328_dbgplay("SUSPEND PLAYBACK\n"); snd_azf3328_dbgcodec("SUSPEND %s\n", codec->name);
/* make sure playback is stopped */ /* make sure codec is stopped */
snd_azf3328_codec_outw(chip, IDX_IO_PLAY_FLAGS, snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS,
snd_azf3328_codec_inw(chip, IDX_IO_PLAY_FLAGS) & ~DMA_RESUME); snd_azf3328_codec_inw(
codec, IDX_IO_CODEC_DMA_FLAGS
) & ~DMA_RESUME
);
break; break;
case SNDRV_PCM_TRIGGER_PAUSE_PUSH: case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
snd_printk(KERN_ERR "FIXME: SNDRV_PCM_TRIGGER_PAUSE_PUSH NIY!\n"); snd_printk(KERN_ERR "FIXME: SNDRV_PCM_TRIGGER_PAUSE_PUSH NIY!\n");
...@@ -1217,7 +1321,7 @@ snd_azf3328_playback_trigger(struct snd_pcm_substream *substream, int cmd) ...@@ -1217,7 +1321,7 @@ snd_azf3328_playback_trigger(struct snd_pcm_substream *substream, int cmd)
snd_printk(KERN_ERR "FIXME: SNDRV_PCM_TRIGGER_PAUSE_RELEASE NIY!\n"); snd_printk(KERN_ERR "FIXME: SNDRV_PCM_TRIGGER_PAUSE_RELEASE NIY!\n");
break; break;
default: default:
printk(KERN_ERR "FIXME: unknown trigger mode!\n"); snd_printk(KERN_ERR "FIXME: unknown trigger mode!\n");
return -EINVAL; return -EINVAL;
} }
...@@ -1225,172 +1329,74 @@ snd_azf3328_playback_trigger(struct snd_pcm_substream *substream, int cmd) ...@@ -1225,172 +1329,74 @@ snd_azf3328_playback_trigger(struct snd_pcm_substream *substream, int cmd)
return result; return result;
} }
/* this is just analogous to playback; I'm not quite sure whether recording
* should actually be triggered like that */
static int static int
snd_azf3328_capture_trigger(struct snd_pcm_substream *substream, int cmd) snd_azf3328_codec_playback_trigger(struct snd_pcm_substream *substream, int cmd)
{ {
struct snd_azf3328 *chip = snd_pcm_substream_chip(substream); return snd_azf3328_codec_trigger(AZF_CODEC_PLAYBACK, substream, cmd);
struct snd_pcm_runtime *runtime = substream->runtime; }
int result = 0;
unsigned int status1;
snd_azf3328_dbgcalls("snd_azf3328_capture_trigger cmd %d\n", cmd);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
snd_azf3328_dbgplay("START CAPTURE\n");
snd_azf3328_codec_setfmt(chip, IDX_IO_REC_SOUNDFORMAT,
runtime->rate,
snd_pcm_format_width(runtime->format),
runtime->channels);
spin_lock(&chip->reg_lock);
/* first, remember current value: */
status1 = snd_azf3328_codec_inw(chip, IDX_IO_REC_FLAGS);
/* stop recording */
status1 &= ~DMA_RESUME;
snd_azf3328_codec_outw(chip, IDX_IO_REC_FLAGS, status1);
/* FIXME: clear interrupts or what??? */
snd_azf3328_codec_outw(chip, IDX_IO_REC_IRQTYPE, 0xffff);
spin_unlock(&chip->reg_lock);
snd_azf3328_setdmaa(chip, runtime->dma_addr,
snd_pcm_lib_period_bytes(substream),
snd_pcm_lib_buffer_bytes(substream),
AZF_CAPTURE);
spin_lock(&chip->reg_lock);
#ifdef WIN9X
/* FIXME: enable playback/recording??? */
status1 |= DMA_PLAY_SOMETHING1 | DMA_PLAY_SOMETHING2;
snd_azf3328_codec_outw(chip, IDX_IO_REC_FLAGS, status1);
/* start capture again */
/* FIXME: what is this value (0x0010)??? */
status1 |= DMA_RESUME | DMA_EPILOGUE_SOMETHING;
snd_azf3328_codec_outw(chip, IDX_IO_REC_FLAGS, status1);
#else
snd_azf3328_codec_outw(chip, IDX_IO_REC_FLAGS,
0x0000);
snd_azf3328_codec_outw(chip, IDX_IO_REC_FLAGS,
DMA_PLAY_SOMETHING1);
snd_azf3328_codec_outw(chip, IDX_IO_REC_FLAGS,
DMA_PLAY_SOMETHING1 |
DMA_PLAY_SOMETHING2);
snd_azf3328_codec_outw(chip, IDX_IO_REC_FLAGS,
DMA_RESUME |
SOMETHING_ALMOST_ALWAYS_SET |
DMA_EPILOGUE_SOMETHING |
DMA_SOMETHING_ELSE);
#endif
spin_unlock(&chip->reg_lock);
snd_azf3328_codec_activity(chip, AZF_CAPTURE, 1);
snd_azf3328_dbgplay("STARTED CAPTURE\n");
break;
case SNDRV_PCM_TRIGGER_RESUME:
snd_azf3328_dbgplay("RESUME CAPTURE\n");
/* resume recording if we were active */
spin_lock(&chip->reg_lock);
if (chip->audio_stream[AZF_CAPTURE].running)
snd_azf3328_codec_outw(chip, IDX_IO_REC_FLAGS,
snd_azf3328_codec_inw(chip, IDX_IO_REC_FLAGS) | DMA_RESUME);
spin_unlock(&chip->reg_lock);
break;
case SNDRV_PCM_TRIGGER_STOP:
snd_azf3328_dbgplay("STOP CAPTURE\n");
spin_lock(&chip->reg_lock);
/* first, remember current value: */
status1 = snd_azf3328_codec_inw(chip, IDX_IO_REC_FLAGS);
/* stop recording */
status1 &= ~DMA_RESUME;
snd_azf3328_codec_outw(chip, IDX_IO_REC_FLAGS, status1);
status1 |= DMA_PLAY_SOMETHING1;
snd_azf3328_codec_outw(chip, IDX_IO_REC_FLAGS, status1);
status1 &= ~DMA_PLAY_SOMETHING1;
snd_azf3328_codec_outw(chip, IDX_IO_REC_FLAGS, status1);
spin_unlock(&chip->reg_lock);
snd_azf3328_codec_activity(chip, AZF_CAPTURE, 0);
snd_azf3328_dbgplay("STOPPED CAPTURE\n"); static int
break; snd_azf3328_codec_capture_trigger(struct snd_pcm_substream *substream, int cmd)
case SNDRV_PCM_TRIGGER_SUSPEND: {
snd_azf3328_dbgplay("SUSPEND CAPTURE\n"); return snd_azf3328_codec_trigger(AZF_CODEC_CAPTURE, substream, cmd);
/* make sure recording is stopped */ }
snd_azf3328_codec_outw(chip, IDX_IO_REC_FLAGS,
snd_azf3328_codec_inw(chip, IDX_IO_REC_FLAGS) & ~DMA_RESUME);
break;
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
snd_printk(KERN_ERR "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");
break;
default:
printk(KERN_ERR "FIXME: unknown trigger mode!\n");
return -EINVAL;
}
snd_azf3328_dbgcallleave(); static int
return result; 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 static snd_pcm_uframes_t
snd_azf3328_playback_pointer(struct snd_pcm_substream *substream) snd_azf3328_codec_pointer(struct snd_pcm_substream *substream,
enum snd_azf3328_codec_type codec_type
)
{ {
struct snd_azf3328 *chip = snd_pcm_substream_chip(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; unsigned long bufptr, result;
snd_pcm_uframes_t frmres; snd_pcm_uframes_t frmres;
#ifdef QUERY_HARDWARE #ifdef QUERY_HARDWARE
bufptr = snd_azf3328_codec_inl(chip, IDX_IO_PLAY_DMA_START_1); bufptr = snd_azf3328_codec_inl(codec, IDX_IO_CODEC_DMA_START_1);
#else #else
bufptr = substream->runtime->dma_addr; bufptr = substream->runtime->dma_addr;
#endif #endif
result = snd_azf3328_codec_inl(chip, IDX_IO_PLAY_DMA_CURRPOS); result = snd_azf3328_codec_inl(codec, IDX_IO_CODEC_DMA_CURRPOS);
/* calculate offset */ /* calculate offset */
result -= bufptr; result -= bufptr;
frmres = bytes_to_frames( substream->runtime, result); frmres = bytes_to_frames( substream->runtime, result);
snd_azf3328_dbgplay("PLAY @ 0x%8lx, frames %8ld\n", result, frmres); snd_azf3328_dbgcodec("%s @ 0x%8lx, frames %8ld\n",
codec->name, result, frmres);
return frmres; return frmres;
} }
static snd_pcm_uframes_t static snd_pcm_uframes_t
snd_azf3328_capture_pointer(struct snd_pcm_substream *substream) snd_azf3328_codec_playback_pointer(struct snd_pcm_substream *substream)
{ {
struct snd_azf3328 *chip = snd_pcm_substream_chip(substream); return snd_azf3328_codec_pointer(substream, AZF_CODEC_PLAYBACK);
unsigned long bufptr, result; }
snd_pcm_uframes_t frmres;
#ifdef QUERY_HARDWARE static snd_pcm_uframes_t
bufptr = snd_azf3328_codec_inl(chip, IDX_IO_REC_DMA_START_1); snd_azf3328_codec_capture_pointer(struct snd_pcm_substream *substream)
#else {
bufptr = substream->runtime->dma_addr; return snd_azf3328_codec_pointer(substream, AZF_CODEC_CAPTURE);
#endif }
result = snd_azf3328_codec_inl(chip, IDX_IO_REC_DMA_CURRPOS);
/* calculate offset */ static snd_pcm_uframes_t
result -= bufptr; snd_azf3328_codec_i2s_out_pointer(struct snd_pcm_substream *substream)
frmres = bytes_to_frames( substream->runtime, result); {
snd_azf3328_dbgplay("REC @ 0x%8lx, frames %8ld\n", result, frmres); return snd_azf3328_codec_pointer(substream, AZF_CODEC_I2S_OUT);
return frmres;
} }
/******************************************************************/ /******************************************************************/
#ifdef SUPPORT_GAMEPORT #ifdef SUPPORT_GAMEPORT
static inline void static inline void
snd_azf3328_gameport_irq_enable(struct snd_azf3328 *chip, int enable) snd_azf3328_gameport_irq_enable(struct snd_azf3328 *chip,
bool enable
)
{ {
snd_azf3328_io_reg_setb( snd_azf3328_io_reg_setb(
chip->game_io+IDX_GAME_HWCONFIG, chip->game_io+IDX_GAME_HWCONFIG,
...@@ -1400,7 +1406,9 @@ snd_azf3328_gameport_irq_enable(struct snd_azf3328 *chip, int enable) ...@@ -1400,7 +1406,9 @@ snd_azf3328_gameport_irq_enable(struct snd_azf3328 *chip, int enable)
} }
static inline void static inline void
snd_azf3328_gameport_legacy_address_enable(struct snd_azf3328 *chip, int enable) snd_azf3328_gameport_legacy_address_enable(struct snd_azf3328 *chip,
bool enable
)
{ {
snd_azf3328_io_reg_setb( snd_azf3328_io_reg_setb(
chip->game_io+IDX_GAME_HWCONFIG, chip->game_io+IDX_GAME_HWCONFIG,
...@@ -1409,10 +1417,27 @@ snd_azf3328_gameport_legacy_address_enable(struct snd_azf3328 *chip, int enable) ...@@ -1409,10 +1417,27 @@ snd_azf3328_gameport_legacy_address_enable(struct snd_azf3328 *chip, int enable)
); );
} }
static void
snd_azf3328_gameport_set_counter_frequency(struct snd_azf3328 *chip,
unsigned int freq_cfg
)
{
snd_azf3328_io_reg_setb(
chip->game_io+IDX_GAME_HWCONFIG,
0x02,
(freq_cfg & 1) != 0
);
snd_azf3328_io_reg_setb(
chip->game_io+IDX_GAME_HWCONFIG,
0x04,
(freq_cfg & 2) != 0
);
}
static inline void static inline void
snd_azf3328_gameport_axis_circuit_enable(struct snd_azf3328 *chip, int enable) snd_azf3328_gameport_axis_circuit_enable(struct snd_azf3328 *chip, bool enable)
{ {
snd_azf3328_codec_reg_6AH_update( snd_azf3328_ctrl_reg_6AH_update(
chip, IO_6A_SOMETHING2_GAMEPORT, enable chip, IO_6A_SOMETHING2_GAMEPORT, enable
); );
} }
...@@ -1447,6 +1472,8 @@ snd_azf3328_gameport_open(struct gameport *gameport, int mode) ...@@ -1447,6 +1472,8 @@ snd_azf3328_gameport_open(struct gameport *gameport, int mode)
break; break;
} }
snd_azf3328_gameport_set_counter_frequency(chip,
GAME_HWCFG_ADC_COUNTER_FREQ_STD);
snd_azf3328_gameport_axis_circuit_enable(chip, (res == 0)); snd_azf3328_gameport_axis_circuit_enable(chip, (res == 0));
return res; return res;
...@@ -1458,6 +1485,8 @@ snd_azf3328_gameport_close(struct gameport *gameport) ...@@ -1458,6 +1485,8 @@ snd_azf3328_gameport_close(struct gameport *gameport)
struct snd_azf3328 *chip = gameport_get_port_data(gameport); struct snd_azf3328 *chip = gameport_get_port_data(gameport);
snd_azf3328_dbggame("gameport_close\n"); snd_azf3328_dbggame("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); snd_azf3328_gameport_axis_circuit_enable(chip, 0);
} }
...@@ -1491,7 +1520,7 @@ snd_azf3328_gameport_cooked_read(struct gameport *gameport, ...@@ -1491,7 +1520,7 @@ snd_azf3328_gameport_cooked_read(struct gameport *gameport,
val = snd_azf3328_game_inb(chip, IDX_GAME_AXES_CONFIG); val = snd_azf3328_game_inb(chip, IDX_GAME_AXES_CONFIG);
if (val & GAME_AXES_SAMPLING_READY) { if (val & GAME_AXES_SAMPLING_READY) {
for (i = 0; i < 4; ++i) { for (i = 0; i < ARRAY_SIZE(chip->axes); ++i) {
/* configure the axis to read */ /* configure the axis to read */
val = (i << 4) | 0x0f; val = (i << 4) | 0x0f;
snd_azf3328_game_outb(chip, IDX_GAME_AXES_CONFIG, val); snd_azf3328_game_outb(chip, IDX_GAME_AXES_CONFIG, val);
...@@ -1514,7 +1543,7 @@ snd_azf3328_gameport_cooked_read(struct gameport *gameport, ...@@ -1514,7 +1543,7 @@ snd_azf3328_gameport_cooked_read(struct gameport *gameport,
snd_azf3328_game_outw(chip, IDX_GAME_AXIS_VALUE, 0xffff); snd_azf3328_game_outw(chip, IDX_GAME_AXIS_VALUE, 0xffff);
spin_unlock_irqrestore(&chip->reg_lock, flags); spin_unlock_irqrestore(&chip->reg_lock, flags);
for (i = 0; i < 4; i++) { for (i = 0; i < ARRAY_SIZE(chip->axes); i++) {
axes[i] = chip->axes[i]; axes[i] = chip->axes[i];
if (axes[i] == 0xffff) if (axes[i] == 0xffff)
axes[i] = -1; axes[i] = -1;
...@@ -1552,6 +1581,8 @@ snd_azf3328_gameport(struct snd_azf3328 *chip, int dev) ...@@ -1552,6 +1581,8 @@ snd_azf3328_gameport(struct snd_azf3328 *chip, int dev)
/* DISABLE legacy address: we don't need it! */ /* DISABLE legacy address: we don't need it! */
snd_azf3328_gameport_legacy_address_enable(chip, 0); snd_azf3328_gameport_legacy_address_enable(chip, 0);
snd_azf3328_gameport_set_counter_frequency(chip,
GAME_HWCFG_ADC_COUNTER_FREQ_1_200);
snd_azf3328_gameport_axis_circuit_enable(chip, 0); snd_azf3328_gameport_axis_circuit_enable(chip, 0);
gameport_register_port(chip->gameport); gameport_register_port(chip->gameport);
...@@ -1585,40 +1616,77 @@ snd_azf3328_gameport_interrupt(struct snd_azf3328 *chip) ...@@ -1585,40 +1616,77 @@ snd_azf3328_gameport_interrupt(struct snd_azf3328 *chip)
static inline void static inline void
snd_azf3328_irq_log_unknown_type(u8 which) snd_azf3328_irq_log_unknown_type(u8 which)
{ {
snd_azf3328_dbgplay( snd_azf3328_dbgcodec(
"azt3328: unknown IRQ type (%x) occurred, please report!\n", "azt3328: unknown IRQ type (%x) occurred, please report!\n",
which which
); );
} }
static inline void
snd_azf3328_codec_interrupt(struct snd_azf3328 *chip, u8 status)
{
u8 which;
enum snd_azf3328_codec_type codec_type;
const struct snd_azf3328_codec_data *codec;
for (codec_type = AZF_CODEC_PLAYBACK;
codec_type <= AZF_CODEC_I2S_OUT;
++codec_type) {
/* 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);
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);
if ((chip->pcm[codec_type]) && (codec->substream)) {
snd_pcm_period_elapsed(codec->substream);
snd_azf3328_dbgcodec("%s period done (#%x), @ %x\n",
codec->name,
which,
snd_azf3328_codec_inl(
codec, IDX_IO_CODEC_DMA_CURRPOS
)
);
} else
printk(KERN_WARNING "azt3328: irq handler problem!\n");
if (which & IRQ_SOMETHING)
snd_azf3328_irq_log_unknown_type(which);
}
}
static irqreturn_t static irqreturn_t
snd_azf3328_interrupt(int irq, void *dev_id) snd_azf3328_interrupt(int irq, void *dev_id)
{ {
struct snd_azf3328 *chip = dev_id; struct snd_azf3328 *chip = dev_id;
u8 status, which; u8 status;
#if DEBUG_PLAY_REC #if DEBUG_CODEC
static unsigned long irq_count; static unsigned long irq_count;
#endif #endif
status = snd_azf3328_codec_inb(chip, IDX_IO_IRQSTATUS); status = snd_azf3328_ctrl_inb(chip, IDX_IO_IRQSTATUS);
/* fast path out, to ease interrupt sharing */ /* fast path out, to ease interrupt sharing */
if (!(status & if (!(status &
(IRQ_PLAYBACK|IRQ_RECORDING|IRQ_GAMEPORT|IRQ_MPU401|IRQ_TIMER) (IRQ_PLAYBACK|IRQ_RECORDING|IRQ_I2S_OUT
|IRQ_GAMEPORT|IRQ_MPU401|IRQ_TIMER)
)) ))
return IRQ_NONE; /* must be interrupt for another device */ return IRQ_NONE; /* must be interrupt for another device */
snd_azf3328_dbgplay( snd_azf3328_dbgcodec(
"irq_count %ld! IDX_IO_PLAY_FLAGS %04x, " "irq_count %ld! IDX_IO_IRQSTATUS %04x\n",
"IDX_IO_PLAY_IRQTYPE %04x, IDX_IO_IRQSTATUS %04x\n",
irq_count++ /* debug-only */, irq_count++ /* debug-only */,
snd_azf3328_codec_inw(chip, IDX_IO_PLAY_FLAGS),
snd_azf3328_codec_inw(chip, IDX_IO_PLAY_IRQTYPE),
status status
); );
if (status & IRQ_TIMER) { if (status & IRQ_TIMER) {
/* snd_azf3328_dbgplay("timer %ld\n", /* snd_azf3328_dbgcodec("timer %ld\n",
snd_azf3328_codec_inl(chip, IDX_IO_TIMER_VALUE) snd_azf3328_codec_inl(chip, IDX_IO_TIMER_VALUE)
& TIMER_VALUE_MASK & TIMER_VALUE_MASK
); */ ); */
...@@ -1626,71 +1694,36 @@ snd_azf3328_interrupt(int irq, void *dev_id) ...@@ -1626,71 +1694,36 @@ snd_azf3328_interrupt(int irq, void *dev_id)
snd_timer_interrupt(chip->timer, chip->timer->sticks); snd_timer_interrupt(chip->timer, chip->timer->sticks);
/* ACK timer */ /* ACK timer */
spin_lock(&chip->reg_lock); spin_lock(&chip->reg_lock);
snd_azf3328_codec_outb(chip, IDX_IO_TIMER_VALUE + 3, 0x07); snd_azf3328_ctrl_outb(chip, IDX_IO_TIMER_VALUE + 3, 0x07);
spin_unlock(&chip->reg_lock); spin_unlock(&chip->reg_lock);
snd_azf3328_dbgplay("azt3328: timer IRQ\n"); snd_azf3328_dbgcodec("azt3328: timer IRQ\n");
} }
if (status & IRQ_PLAYBACK) {
spin_lock(&chip->reg_lock);
which = snd_azf3328_codec_inb(chip, IDX_IO_PLAY_IRQTYPE);
/* ack all IRQ types immediately */
snd_azf3328_codec_outb(chip, IDX_IO_PLAY_IRQTYPE, which);
spin_unlock(&chip->reg_lock);
if (chip->pcm && chip->audio_stream[AZF_PLAYBACK].substream) { if (status & (IRQ_PLAYBACK|IRQ_RECORDING|IRQ_I2S_OUT))
snd_pcm_period_elapsed( snd_azf3328_codec_interrupt(chip, status);
chip->audio_stream[AZF_PLAYBACK].substream
);
snd_azf3328_dbgplay("PLAY period done (#%x), @ %x\n",
which,
snd_azf3328_codec_inl(
chip, IDX_IO_PLAY_DMA_CURRPOS
)
);
} else
printk(KERN_WARNING "azt3328: irq handler problem!\n");
if (which & IRQ_PLAY_SOMETHING)
snd_azf3328_irq_log_unknown_type(which);
}
if (status & IRQ_RECORDING) {
spin_lock(&chip->reg_lock);
which = snd_azf3328_codec_inb(chip, IDX_IO_REC_IRQTYPE);
/* ack all IRQ types immediately */
snd_azf3328_codec_outb(chip, IDX_IO_REC_IRQTYPE, which);
spin_unlock(&chip->reg_lock);
if (chip->pcm && chip->audio_stream[AZF_CAPTURE].substream) {
snd_pcm_period_elapsed(
chip->audio_stream[AZF_CAPTURE].substream
);
snd_azf3328_dbgplay("REC period done (#%x), @ %x\n",
which,
snd_azf3328_codec_inl(
chip, IDX_IO_REC_DMA_CURRPOS
)
);
} else
printk(KERN_WARNING "azt3328: irq handler problem!\n");
if (which & IRQ_REC_SOMETHING)
snd_azf3328_irq_log_unknown_type(which);
}
if (status & IRQ_GAMEPORT) if (status & IRQ_GAMEPORT)
snd_azf3328_gameport_interrupt(chip); snd_azf3328_gameport_interrupt(chip);
/* MPU401 has less critical IRQ requirements /* MPU401 has less critical IRQ requirements
* than timer and playback/recording, right? */ * than timer and playback/recording, right? */
if (status & IRQ_MPU401) { if (status & IRQ_MPU401) {
snd_mpu401_uart_interrupt(irq, chip->rmidi->private_data); snd_mpu401_uart_interrupt(irq, chip->rmidi->private_data);
/* hmm, do we have to ack the IRQ here somehow? /* hmm, do we have to ack the IRQ here somehow?
* If so, then I don't know how... */ * If so, then I don't know how yet... */
snd_azf3328_dbgplay("azt3328: MPU401 IRQ\n"); snd_azf3328_dbgcodec("azt3328: MPU401 IRQ\n");
} }
return IRQ_HANDLED; return IRQ_HANDLED;
} }
/*****************************************************************/ /*****************************************************************/
static const struct snd_pcm_hardware snd_azf3328_playback = /* as long as we think we have identical snd_pcm_hardware parameters
for playback, capture and i2s out, we can use the same physical struct
since the struct is simply being copied into a member.
*/
static const struct snd_pcm_hardware snd_azf3328_hardware =
{ {
/* FIXME!! Correct? */ /* FIXME!! Correct? */
.info = SNDRV_PCM_INFO_MMAP | .info = SNDRV_PCM_INFO_MMAP |
...@@ -1718,31 +1751,6 @@ static const struct snd_pcm_hardware snd_azf3328_playback = ...@@ -1718,31 +1751,6 @@ static const struct snd_pcm_hardware snd_azf3328_playback =
.fifo_size = 0, .fifo_size = 0,
}; };
static const struct snd_pcm_hardware snd_azf3328_capture =
{
/* FIXME */
.info = SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_MMAP_VALID,
.formats = SNDRV_PCM_FMTBIT_S8 |
SNDRV_PCM_FMTBIT_U8 |
SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_U16_LE,
.rates = SNDRV_PCM_RATE_5512 |
SNDRV_PCM_RATE_8000_48000 |
SNDRV_PCM_RATE_KNOT,
.rate_min = AZF_FREQ_4000,
.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,
.fifo_size = 0,
};
static unsigned int snd_azf3328_fixed_rates[] = { static unsigned int snd_azf3328_fixed_rates[] = {
AZF_FREQ_4000, AZF_FREQ_4000,
...@@ -1770,55 +1778,72 @@ static struct snd_pcm_hw_constraint_list snd_azf3328_hw_constraints_rates = { ...@@ -1770,55 +1778,72 @@ static struct snd_pcm_hw_constraint_list snd_azf3328_hw_constraints_rates = {
/*****************************************************************/ /*****************************************************************/
static int static int
snd_azf3328_playback_open(struct snd_pcm_substream *substream) snd_azf3328_pcm_open(struct snd_pcm_substream *substream,
enum snd_azf3328_codec_type codec_type
)
{ {
struct snd_azf3328 *chip = snd_pcm_substream_chip(substream); struct snd_azf3328 *chip = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime; struct snd_pcm_runtime *runtime = substream->runtime;
snd_azf3328_dbgcallenter(); snd_azf3328_dbgcallenter();
chip->audio_stream[AZF_PLAYBACK].substream = substream; chip->codecs[codec_type].substream = substream;
runtime->hw = snd_azf3328_playback;
/* 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_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
&snd_azf3328_hw_constraints_rates); &snd_azf3328_hw_constraints_rates);
snd_azf3328_dbgcallleave(); snd_azf3328_dbgcallleave();
return 0; return 0;
} }
static int
snd_azf3328_playback_open(struct snd_pcm_substream *substream)
{
return snd_azf3328_pcm_open(substream, AZF_CODEC_PLAYBACK);
}
static int static int
snd_azf3328_capture_open(struct snd_pcm_substream *substream) snd_azf3328_capture_open(struct snd_pcm_substream *substream)
{ {
struct snd_azf3328 *chip = snd_pcm_substream_chip(substream); return snd_azf3328_pcm_open(substream, AZF_CODEC_CAPTURE);
struct snd_pcm_runtime *runtime = substream->runtime; }
snd_azf3328_dbgcallenter(); static int
chip->audio_stream[AZF_CAPTURE].substream = substream; snd_azf3328_i2s_out_open(struct snd_pcm_substream *substream)
runtime->hw = snd_azf3328_capture; {
snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, return snd_azf3328_pcm_open(substream, AZF_CODEC_I2S_OUT);
&snd_azf3328_hw_constraints_rates);
snd_azf3328_dbgcallleave();
return 0;
} }
static int static int
snd_azf3328_playback_close(struct snd_pcm_substream *substream) snd_azf3328_pcm_close(struct snd_pcm_substream *substream,
enum snd_azf3328_codec_type codec_type
)
{ {
struct snd_azf3328 *chip = snd_pcm_substream_chip(substream); struct snd_azf3328 *chip = snd_pcm_substream_chip(substream);
snd_azf3328_dbgcallenter(); snd_azf3328_dbgcallenter();
chip->audio_stream[AZF_PLAYBACK].substream = NULL; chip->codecs[codec_type].substream = NULL;
snd_azf3328_dbgcallleave(); snd_azf3328_dbgcallleave();
return 0; return 0;
} }
static int
snd_azf3328_playback_close(struct snd_pcm_substream *substream)
{
return snd_azf3328_pcm_close(substream, AZF_CODEC_PLAYBACK);
}
static int static int
snd_azf3328_capture_close(struct snd_pcm_substream *substream) snd_azf3328_capture_close(struct snd_pcm_substream *substream)
{ {
struct snd_azf3328 *chip = snd_pcm_substream_chip(substream); return snd_azf3328_pcm_close(substream, AZF_CODEC_CAPTURE);
}
snd_azf3328_dbgcallenter(); static int
chip->audio_stream[AZF_CAPTURE].substream = NULL; snd_azf3328_i2s_out_close(struct snd_pcm_substream *substream)
snd_azf3328_dbgcallleave(); {
return 0; return snd_azf3328_pcm_close(substream, AZF_CODEC_I2S_OUT);
} }
/******************************************************************/ /******************************************************************/
...@@ -1829,9 +1854,9 @@ static struct snd_pcm_ops snd_azf3328_playback_ops = { ...@@ -1829,9 +1854,9 @@ static struct snd_pcm_ops snd_azf3328_playback_ops = {
.ioctl = snd_pcm_lib_ioctl, .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_azf3328_hw_params, .hw_params = snd_azf3328_hw_params,
.hw_free = snd_azf3328_hw_free, .hw_free = snd_azf3328_hw_free,
.prepare = snd_azf3328_playback_prepare, .prepare = snd_azf3328_codec_prepare,
.trigger = snd_azf3328_playback_trigger, .trigger = snd_azf3328_codec_playback_trigger,
.pointer = snd_azf3328_playback_pointer .pointer = snd_azf3328_codec_playback_pointer
}; };
static struct snd_pcm_ops snd_azf3328_capture_ops = { static struct snd_pcm_ops snd_azf3328_capture_ops = {
...@@ -1840,30 +1865,67 @@ static struct snd_pcm_ops snd_azf3328_capture_ops = { ...@@ -1840,30 +1865,67 @@ static struct snd_pcm_ops snd_azf3328_capture_ops = {
.ioctl = snd_pcm_lib_ioctl, .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_azf3328_hw_params, .hw_params = snd_azf3328_hw_params,
.hw_free = snd_azf3328_hw_free, .hw_free = snd_azf3328_hw_free,
.prepare = snd_azf3328_capture_prepare, .prepare = snd_azf3328_codec_prepare,
.trigger = snd_azf3328_capture_trigger, .trigger = snd_azf3328_codec_capture_trigger,
.pointer = snd_azf3328_capture_pointer .pointer = snd_azf3328_codec_capture_pointer
};
static struct snd_pcm_ops snd_azf3328_i2s_out_ops = {
.open = snd_azf3328_i2s_out_open,
.close = snd_azf3328_i2s_out_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
}; };
static int __devinit static int __devinit
snd_azf3328_pcm(struct snd_azf3328 *chip, int device) snd_azf3328_pcm(struct snd_azf3328 *chip)
{ {
enum { AZF_PCMDEV_STD, AZF_PCMDEV_I2S_OUT, NUM_AZF_PCMDEVS }; /* pcm devices */
struct snd_pcm *pcm; struct snd_pcm *pcm;
int err; int err;
snd_azf3328_dbgcallenter(); snd_azf3328_dbgcallenter();
if ((err = snd_pcm_new(chip->card, "AZF3328 DSP", device, 1, 1, &pcm)) < 0)
err = snd_pcm_new(chip->card, "AZF3328 DSP", AZF_PCMDEV_STD,
1, 1, &pcm);
if (err < 0)
return err; return err;
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_azf3328_playback_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_azf3328_capture_ops); &snd_azf3328_playback_ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
&snd_azf3328_capture_ops);
pcm->private_data = chip; pcm->private_data = chip;
pcm->info_flags = 0; pcm->info_flags = 0;
strcpy(pcm->name, chip->card->shortname); strcpy(pcm->name, chip->card->shortname);
chip->pcm = pcm; /* same pcm object for playback/capture (see snd_pcm_new() above) */
chip->pcm[AZF_CODEC_PLAYBACK] = pcm;
chip->pcm[AZF_CODEC_CAPTURE] = pcm;
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
snd_dma_pci_data(chip->pci), 64*1024, 64*1024); snd_dma_pci_data(chip->pci),
64*1024, 64*1024);
err = snd_pcm_new(chip->card, "AZF3328 I2S OUT", AZF_PCMDEV_I2S_OUT,
1, 0, &pcm);
if (err < 0)
return err;
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
&snd_azf3328_i2s_out_ops);
pcm->private_data = chip;
pcm->info_flags = 0;
strcpy(pcm->name, chip->card->shortname);
chip->pcm[AZF_CODEC_I2S_OUT] = pcm;
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
snd_dma_pci_data(chip->pci),
64*1024, 64*1024);
snd_azf3328_dbgcallleave(); snd_azf3328_dbgcallleave();
return 0; return 0;
...@@ -1902,7 +1964,7 @@ snd_azf3328_timer_start(struct snd_timer *timer) ...@@ -1902,7 +1964,7 @@ snd_azf3328_timer_start(struct snd_timer *timer)
snd_azf3328_dbgtimer("setting timer countdown value %d, add COUNTDOWN|IRQ\n", delay); snd_azf3328_dbgtimer("setting timer countdown value %d, add COUNTDOWN|IRQ\n", delay);
delay |= TIMER_COUNTDOWN_ENABLE | TIMER_IRQ_ENABLE; delay |= TIMER_COUNTDOWN_ENABLE | TIMER_IRQ_ENABLE;
spin_lock_irqsave(&chip->reg_lock, flags); spin_lock_irqsave(&chip->reg_lock, flags);
snd_azf3328_codec_outl(chip, IDX_IO_TIMER_VALUE, delay); snd_azf3328_ctrl_outl(chip, IDX_IO_TIMER_VALUE, delay);
spin_unlock_irqrestore(&chip->reg_lock, flags); spin_unlock_irqrestore(&chip->reg_lock, flags);
snd_azf3328_dbgcallleave(); snd_azf3328_dbgcallleave();
return 0; return 0;
...@@ -1919,7 +1981,7 @@ snd_azf3328_timer_stop(struct snd_timer *timer) ...@@ -1919,7 +1981,7 @@ snd_azf3328_timer_stop(struct snd_timer *timer)
spin_lock_irqsave(&chip->reg_lock, flags); spin_lock_irqsave(&chip->reg_lock, flags);
/* disable timer countdown and interrupt */ /* disable timer countdown and interrupt */
/* FIXME: should we write TIMER_IRQ_ACK here? */ /* FIXME: should we write TIMER_IRQ_ACK here? */
snd_azf3328_codec_outb(chip, IDX_IO_TIMER_VALUE + 3, 0); snd_azf3328_ctrl_outb(chip, IDX_IO_TIMER_VALUE + 3, 0);
spin_unlock_irqrestore(&chip->reg_lock, flags); spin_unlock_irqrestore(&chip->reg_lock, flags);
snd_azf3328_dbgcallleave(); snd_azf3328_dbgcallleave();
return 0; return 0;
...@@ -2035,7 +2097,7 @@ snd_azf3328_test_bit(unsigned unsigned reg, int bit) ...@@ -2035,7 +2097,7 @@ snd_azf3328_test_bit(unsigned unsigned reg, int bit)
outb(val, reg); outb(val, reg);
printk(KERN_ERR "reg %04x bit %d: %02x %02x %02x\n", printk(KERN_DEBUG "reg %04x bit %d: %02x %02x %02x\n",
reg, bit, val, valoff, valon reg, bit, val, valoff, valon
); );
} }
...@@ -2048,9 +2110,9 @@ snd_azf3328_debug_show_ports(const struct snd_azf3328 *chip) ...@@ -2048,9 +2110,9 @@ snd_azf3328_debug_show_ports(const struct snd_azf3328 *chip)
u16 tmp; u16 tmp;
snd_azf3328_dbgmisc( snd_azf3328_dbgmisc(
"codec_io 0x%lx, game_io 0x%lx, mpu_io 0x%lx, " "ctrl_io 0x%lx, game_io 0x%lx, mpu_io 0x%lx, "
"opl3_io 0x%lx, mixer_io 0x%lx, irq %d\n", "opl3_io 0x%lx, mixer_io 0x%lx, irq %d\n",
chip->codec_io, chip->game_io, chip->mpu_io, 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
); );
...@@ -2083,9 +2145,9 @@ snd_azf3328_debug_show_ports(const struct snd_azf3328 *chip) ...@@ -2083,9 +2145,9 @@ snd_azf3328_debug_show_ports(const struct snd_azf3328 *chip)
inb(0x38c + tmp) inb(0x38c + tmp)
); );
for (tmp = 0; tmp < AZF_IO_SIZE_CODEC; tmp += 2) for (tmp = 0; tmp < AZF_IO_SIZE_CTRL; tmp += 2)
snd_azf3328_dbgmisc("codec 0x%02x: 0x%04x\n", snd_azf3328_dbgmisc("ctrl 0x%02x: 0x%04x\n",
tmp, snd_azf3328_codec_inw(chip, tmp) tmp, snd_azf3328_ctrl_inw(chip, tmp)
); );
for (tmp = 0; tmp < AZF_IO_SIZE_MIXER; tmp += 2) for (tmp = 0; tmp < AZF_IO_SIZE_MIXER; tmp += 2)
...@@ -2106,7 +2168,8 @@ snd_azf3328_create(struct snd_card *card, ...@@ -2106,7 +2168,8 @@ snd_azf3328_create(struct snd_card *card,
static struct snd_device_ops ops = { static struct snd_device_ops ops = {
.dev_free = snd_azf3328_dev_free, .dev_free = snd_azf3328_dev_free,
}; };
u16 tmp; u8 dma_init;
enum snd_azf3328_codec_type codec_type;
*rchip = NULL; *rchip = NULL;
...@@ -2138,14 +2201,21 @@ snd_azf3328_create(struct snd_card *card, ...@@ -2138,14 +2201,21 @@ snd_azf3328_create(struct snd_card *card,
if (err < 0) if (err < 0)
goto out_err; goto out_err;
chip->codec_io = pci_resource_start(pci, 0); chip->ctrl_io = pci_resource_start(pci, 0);
chip->game_io = pci_resource_start(pci, 1); chip->game_io = pci_resource_start(pci, 1);
chip->mpu_io = pci_resource_start(pci, 2); chip->mpu_io = pci_resource_start(pci, 2);
chip->opl3_io = pci_resource_start(pci, 3); chip->opl3_io = pci_resource_start(pci, 3);
chip->mixer_io = pci_resource_start(pci, 4); chip->mixer_io = pci_resource_start(pci, 4);
chip->audio_stream[AZF_PLAYBACK].portbase = chip->codec_io + 0x00; chip->codecs[AZF_CODEC_PLAYBACK].io_base =
chip->audio_stream[AZF_CAPTURE].portbase = chip->codec_io + 0x20; 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";
if (request_irq(pci->irq, snd_azf3328_interrupt, if (request_irq(pci->irq, snd_azf3328_interrupt,
IRQF_SHARED, card->shortname, chip)) { IRQF_SHARED, card->shortname, chip)) {
...@@ -2168,20 +2238,25 @@ snd_azf3328_create(struct snd_card *card, ...@@ -2168,20 +2238,25 @@ snd_azf3328_create(struct snd_card *card,
if (err < 0) if (err < 0)
goto out_err; goto out_err;
/* shutdown codecs to save power */ /* standard codec init stuff */
/* have snd_azf3328_codec_activity() act properly */ /* default DMA init value */
chip->audio_stream[AZF_PLAYBACK].running = 1; dma_init = DMA_RUN_SOMETHING2|DMA_EPILOGUE_SOMETHING|DMA_SOMETHING_ELSE;
snd_azf3328_codec_activity(chip, AZF_PLAYBACK, 0);
/* standard chip init stuff */ for (codec_type = AZF_CODEC_PLAYBACK;
/* default IRQ init value */ codec_type <= AZF_CODEC_I2S_OUT; ++codec_type) {
tmp = DMA_PLAY_SOMETHING2|DMA_EPILOGUE_SOMETHING|DMA_SOMETHING_ELSE; struct snd_azf3328_codec_data *codec =
&chip->codecs[codec_type];
spin_lock_irq(&chip->reg_lock); /* shutdown codecs to save power */
snd_azf3328_codec_outb(chip, IDX_IO_PLAY_FLAGS, tmp); /* have ...ctrl_codec_activity() act properly */
snd_azf3328_codec_outb(chip, IDX_IO_REC_FLAGS, tmp); codec->running = 1;
snd_azf3328_codec_outb(chip, IDX_IO_SOMETHING_FLAGS, tmp); snd_azf3328_ctrl_codec_activity(chip, codec_type, 0);
spin_unlock_irq(&chip->reg_lock);
spin_lock_irq(&chip->reg_lock);
snd_azf3328_codec_outb(codec, IDX_IO_CODEC_DMA_FLAGS,
dma_init);
spin_unlock_irq(&chip->reg_lock);
}
snd_card_set_dev(card, &pci->dev); snd_card_set_dev(card, &pci->dev);
...@@ -2229,8 +2304,11 @@ snd_azf3328_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) ...@@ -2229,8 +2304,11 @@ snd_azf3328_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
card->private_data = chip; card->private_data = chip;
/* chose to use MPU401_HW_AZT2320 ID instead of MPU401_HW_MPU401,
since our hardware ought to be similar, thus use same ID. */
err = snd_mpu401_uart_new( err = snd_mpu401_uart_new(
card, 0, MPU401_HW_MPU401, chip->mpu_io, MPU401_INFO_INTEGRATED, card, 0,
MPU401_HW_AZT2320, chip->mpu_io, MPU401_INFO_INTEGRATED,
pci->irq, 0, &chip->rmidi pci->irq, 0, &chip->rmidi
); );
if (err < 0) { if (err < 0) {
...@@ -2244,7 +2322,7 @@ snd_azf3328_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) ...@@ -2244,7 +2322,7 @@ snd_azf3328_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
if (err < 0) if (err < 0)
goto out_err; goto out_err;
err = snd_azf3328_pcm(chip, 0); err = snd_azf3328_pcm(chip);
if (err < 0) if (err < 0)
goto out_err; goto out_err;
...@@ -2266,14 +2344,14 @@ snd_azf3328_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) ...@@ -2266,14 +2344,14 @@ snd_azf3328_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
opl3->private_data = chip; opl3->private_data = chip;
sprintf(card->longname, "%s at 0x%lx, irq %i", sprintf(card->longname, "%s at 0x%lx, irq %i",
card->shortname, chip->codec_io, chip->irq); card->shortname, chip->ctrl_io, chip->irq);
err = snd_card_register(card); err = snd_card_register(card);
if (err < 0) if (err < 0)
goto out_err; goto out_err;
#ifdef MODULE #ifdef MODULE
printk( printk(KERN_INFO
"azt3328: Sound driver for Aztech AZF3328-based soundcards such as PCI168.\n" "azt3328: Sound driver for Aztech AZF3328-based soundcards such as PCI168.\n"
"azt3328: Hardware was completely undocumented, unfortunately.\n" "azt3328: Hardware was completely undocumented, unfortunately.\n"
"azt3328: Feel free to contact andi AT lisas.de for bug reports etc.!\n" "azt3328: Feel free to contact andi AT lisas.de for bug reports etc.!\n"
...@@ -2308,36 +2386,52 @@ snd_azf3328_remove(struct pci_dev *pci) ...@@ -2308,36 +2386,52 @@ snd_azf3328_remove(struct pci_dev *pci)
} }
#ifdef CONFIG_PM #ifdef CONFIG_PM
static inline void
snd_azf3328_suspend_regs(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",
io_addr, *saved_regs);
++saved_regs;
io_addr += sizeof(*saved_regs);
}
}
static int static int
snd_azf3328_suspend(struct pci_dev *pci, pm_message_t state) snd_azf3328_suspend(struct pci_dev *pci, pm_message_t state)
{ {
struct snd_card *card = pci_get_drvdata(pci); struct snd_card *card = pci_get_drvdata(pci);
struct snd_azf3328 *chip = card->private_data; struct snd_azf3328 *chip = card->private_data;
unsigned reg; u16 *saved_regs_ctrl_u16;
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
snd_pcm_suspend_all(chip->pcm); snd_pcm_suspend_all(chip->pcm[AZF_CODEC_PLAYBACK]);
snd_pcm_suspend_all(chip->pcm[AZF_CODEC_I2S_OUT]);
for (reg = 0; reg < AZF_IO_SIZE_MIXER_PM / 2; ++reg) snd_azf3328_suspend_regs(chip->mixer_io,
chip->saved_regs_mixer[reg] = inw(chip->mixer_io + reg * 2); ARRAY_SIZE(chip->saved_regs_mixer), chip->saved_regs_mixer);
/* make sure to disable master volume etc. to prevent looping sound */ /* 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_PLAY_MASTER, 1);
snd_azf3328_mixer_set_mute(chip, IDX_MIXER_WAVEOUT, 1); snd_azf3328_mixer_set_mute(chip, IDX_MIXER_WAVEOUT, 1);
for (reg = 0; reg < AZF_IO_SIZE_CODEC_PM / 2; ++reg) snd_azf3328_suspend_regs(chip->ctrl_io,
chip->saved_regs_codec[reg] = inw(chip->codec_io + reg * 2); ARRAY_SIZE(chip->saved_regs_ctrl), chip->saved_regs_ctrl);
/* manually store the one currently relevant write-only reg, too */ /* manually store the one currently relevant write-only reg, too */
chip->saved_regs_codec[IDX_IO_6AH / 2] = chip->shadow_reg_codec_6AH; saved_regs_ctrl_u16 = (u16 *)chip->saved_regs_ctrl;
saved_regs_ctrl_u16[IDX_IO_6AH / 2] = chip->shadow_reg_ctrl_6AH;
for (reg = 0; reg < AZF_IO_SIZE_GAME_PM / 2; ++reg) snd_azf3328_suspend_regs(chip->game_io,
chip->saved_regs_game[reg] = inw(chip->game_io + reg * 2); ARRAY_SIZE(chip->saved_regs_game), chip->saved_regs_game);
for (reg = 0; reg < AZF_IO_SIZE_MPU_PM / 2; ++reg) snd_azf3328_suspend_regs(chip->mpu_io,
chip->saved_regs_mpu[reg] = inw(chip->mpu_io + reg * 2); ARRAY_SIZE(chip->saved_regs_mpu), chip->saved_regs_mpu);
for (reg = 0; reg < AZF_IO_SIZE_OPL3_PM / 2; ++reg) snd_azf3328_suspend_regs(chip->opl3_io,
chip->saved_regs_opl3[reg] = inw(chip->opl3_io + reg * 2); ARRAY_SIZE(chip->saved_regs_opl3), chip->saved_regs_opl3);
pci_disable_device(pci); pci_disable_device(pci);
pci_save_state(pci); pci_save_state(pci);
...@@ -2345,12 +2439,28 @@ snd_azf3328_suspend(struct pci_dev *pci, pm_message_t state) ...@@ -2345,12 +2439,28 @@ snd_azf3328_suspend(struct pci_dev *pci, pm_message_t state)
return 0; 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 static int
snd_azf3328_resume(struct pci_dev *pci) snd_azf3328_resume(struct pci_dev *pci)
{ {
struct snd_card *card = pci_get_drvdata(pci); struct snd_card *card = pci_get_drvdata(pci);
struct snd_azf3328 *chip = card->private_data; const struct snd_azf3328 *chip = card->private_data;
unsigned reg;
pci_set_power_state(pci, PCI_D0); pci_set_power_state(pci, PCI_D0);
pci_restore_state(pci); pci_restore_state(pci);
...@@ -2362,16 +2472,24 @@ snd_azf3328_resume(struct pci_dev *pci) ...@@ -2362,16 +2472,24 @@ snd_azf3328_resume(struct pci_dev *pci)
} }
pci_set_master(pci); pci_set_master(pci);
for (reg = 0; reg < AZF_IO_SIZE_GAME_PM / 2; ++reg) snd_azf3328_resume_regs(chip->saved_regs_game, chip->game_io,
outw(chip->saved_regs_game[reg], chip->game_io + reg * 2); ARRAY_SIZE(chip->saved_regs_game));
for (reg = 0; reg < AZF_IO_SIZE_MPU_PM / 2; ++reg) snd_azf3328_resume_regs(chip->saved_regs_mpu, chip->mpu_io,
outw(chip->saved_regs_mpu[reg], chip->mpu_io + reg * 2); ARRAY_SIZE(chip->saved_regs_mpu));
for (reg = 0; reg < AZF_IO_SIZE_OPL3_PM / 2; ++reg) snd_azf3328_resume_regs(chip->saved_regs_opl3, chip->opl3_io,
outw(chip->saved_regs_opl3[reg], chip->opl3_io + reg * 2); ARRAY_SIZE(chip->saved_regs_opl3));
for (reg = 0; reg < AZF_IO_SIZE_MIXER_PM / 2; ++reg)
outw(chip->saved_regs_mixer[reg], chip->mixer_io + reg * 2); snd_azf3328_resume_regs(chip->saved_regs_mixer, chip->mixer_io,
for (reg = 0; reg < AZF_IO_SIZE_CODEC_PM / 2; ++reg) ARRAY_SIZE(chip->saved_regs_mixer));
outw(chip->saved_regs_codec[reg], chip->codec_io + reg * 2);
/* 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,
ARRAY_SIZE(chip->saved_regs_ctrl));
snd_power_change_state(card, SNDRV_CTL_POWER_D0); snd_power_change_state(card, SNDRV_CTL_POWER_D0);
return 0; return 0;
......
...@@ -6,50 +6,59 @@ ...@@ -6,50 +6,59 @@
/*** main I/O area port indices ***/ /*** main I/O area port indices ***/
/* (only 0x70 of 0x80 bytes saved/restored by Windows driver) */ /* (only 0x70 of 0x80 bytes saved/restored by Windows driver) */
#define AZF_IO_SIZE_CODEC 0x80 #define AZF_IO_SIZE_CTRL 0x80
#define AZF_IO_SIZE_CODEC_PM 0x70 #define AZF_IO_SIZE_CTRL_PM 0x70
/* the driver initialisation suggests a layout of 4 main areas: /* the driver initialisation suggests a layout of 4 areas
* from 0x00 (playback), from 0x20 (recording) and from 0x40 (maybe MPU401??). * within the main card control I/O:
* from 0x00 (playback codec), from 0x20 (recording codec)
* and from 0x40 (most certainly I2S out codec).
* And another area from 0x60 to 0x6f (DirectX timer, IRQ management, * And another area from 0x60 to 0x6f (DirectX timer, IRQ management,
* power management etc.???). */ * power management etc.???). */
/** playback area **/ #define AZF_IO_OFFS_CODEC_PLAYBACK 0x00
#define IDX_IO_PLAY_FLAGS 0x00 /* PU:0x0000 */ #define AZF_IO_OFFS_CODEC_CAPTURE 0x20
#define AZF_IO_OFFS_CODEC_I2S_OUT 0x40
#define IDX_IO_CODEC_DMA_FLAGS 0x00 /* PU:0x0000 */
/* able to reactivate output after output muting due to 8/16bit /* able to reactivate output after output muting due to 8/16bit
* output change, just like 0x0002. * output change, just like 0x0002.
* 0x0001 is the only bit that's able to start the DMA counter */ * 0x0001 is the only bit that's able to start the DMA counter */
#define DMA_RESUME 0x0001 /* paused if cleared ? */ #define DMA_RESUME 0x0001 /* paused if cleared? */
/* 0x0002 *temporarily* set during DMA stopping. hmm /* 0x0002 *temporarily* set during DMA stopping. hmm
* both 0x0002 and 0x0004 set in playback setup. */ * both 0x0002 and 0x0004 set in playback setup. */
/* able to reactivate output after output muting due to 8/16bit /* able to reactivate output after output muting due to 8/16bit
* output change, just like 0x0001. */ * output change, just like 0x0001. */
#define DMA_PLAY_SOMETHING1 0x0002 /* \ alternated (toggled) */ #define DMA_RUN_SOMETHING1 0x0002 /* \ alternated (toggled) */
/* 0x0004: NOT able to reactivate output */ /* 0x0004: NOT able to reactivate output */
#define DMA_PLAY_SOMETHING2 0x0004 /* / bits */ #define DMA_RUN_SOMETHING2 0x0004 /* / bits */
#define SOMETHING_ALMOST_ALWAYS_SET 0x0008 /* ???; can be modified */ #define SOMETHING_ALMOST_ALWAYS_SET 0x0008 /* ???; can be modified */
#define DMA_EPILOGUE_SOMETHING 0x0010 #define DMA_EPILOGUE_SOMETHING 0x0010
#define DMA_SOMETHING_ELSE 0x0020 /* ??? */ #define DMA_SOMETHING_ELSE 0x0020 /* ??? */
#define SOMETHING_UNMODIFIABLE 0xffc0 /* unused ? not modifiable */ #define SOMETHING_UNMODIFIABLE 0xffc0 /* unused? not modifiable */
#define IDX_IO_PLAY_IRQTYPE 0x02 /* PU:0x0001 */ #define IDX_IO_CODEC_IRQTYPE 0x02 /* PU:0x0001 */
/* write back to flags in case flags are set, in order to ACK IRQ in handler /* write back to flags in case flags are set, in order to ACK IRQ in handler
* (bit 1 of port 0x64 indicates interrupt for one of these three types) * (bit 1 of port 0x64 indicates interrupt for one of these three types)
* sometimes in this case it just writes 0xffff to globally ACK all IRQs * sometimes in this case it just writes 0xffff to globally ACK all IRQs
* settings written are not reflected when reading back, though. * settings written are not reflected when reading back, though.
* seems to be IRQ, too (frequently used: port |= 0x07 !), but who knows ? */ * seems to be IRQ, too (frequently used: port |= 0x07 !), but who knows? */
#define IRQ_PLAY_SOMETHING 0x0001 /* something & ACK */ #define IRQ_SOMETHING 0x0001 /* something & ACK */
#define IRQ_FINISHED_PLAYBUF_1 0x0002 /* 1st dmabuf finished & ACK */ #define IRQ_FINISHED_DMABUF_1 0x0002 /* 1st dmabuf finished & ACK */
#define IRQ_FINISHED_PLAYBUF_2 0x0004 /* 2nd dmabuf finished & ACK */ #define IRQ_FINISHED_DMABUF_2 0x0004 /* 2nd dmabuf finished & ACK */
#define IRQMASK_SOME_STATUS_1 0x0008 /* \ related bits */ #define IRQMASK_SOME_STATUS_1 0x0008 /* \ related bits */
#define IRQMASK_SOME_STATUS_2 0x0010 /* / (checked together in loop) */ #define IRQMASK_SOME_STATUS_2 0x0010 /* / (checked together in loop) */
#define IRQMASK_UNMODIFIABLE 0xffe0 /* unused ? not modifiable */ #define IRQMASK_UNMODIFIABLE 0xffe0 /* unused? not modifiable */
#define IDX_IO_PLAY_DMA_START_1 0x04 /* start address of 1st DMA play area, PU:0x00000000 */ /* start address of 1st DMA transfer area, PU:0x00000000 */
#define IDX_IO_PLAY_DMA_START_2 0x08 /* start address of 2nd DMA play area, PU:0x00000000 */ #define IDX_IO_CODEC_DMA_START_1 0x04
#define IDX_IO_PLAY_DMA_LEN_1 0x0c /* length of 1st DMA play area, PU:0x0000 */ /* start address of 2nd DMA transfer area, PU:0x00000000 */
#define IDX_IO_PLAY_DMA_LEN_2 0x0e /* length of 2nd DMA play area, PU:0x0000 */ #define IDX_IO_CODEC_DMA_START_2 0x08
#define IDX_IO_PLAY_DMA_CURRPOS 0x10 /* current DMA position, PU:0x00000000 */ /* both lengths of DMA transfer areas, PU:0x00000000
#define IDX_IO_PLAY_DMA_CURROFS 0x14 /* offset within current DMA play area, PU:0x0000 */ length1: offset 0x0c, length2: offset 0x0e */
#define IDX_IO_PLAY_SOUNDFORMAT 0x16 /* PU:0x0010 */ #define IDX_IO_CODEC_DMA_LENGTHS 0x0c
#define IDX_IO_CODEC_DMA_CURRPOS 0x10 /* current DMA position, PU:0x00000000 */
/* offset within current DMA transfer area, PU:0x0000 */
#define IDX_IO_CODEC_DMA_CURROFS 0x14
#define IDX_IO_CODEC_SOUNDFORMAT 0x16 /* PU:0x0010 */
/* all unspecified bits can't be modified */ /* all unspecified bits can't be modified */
#define SOUNDFORMAT_FREQUENCY_MASK 0x000f #define SOUNDFORMAT_FREQUENCY_MASK 0x000f
#define SOUNDFORMAT_XTAL1 0x00 #define SOUNDFORMAT_XTAL1 0x00
...@@ -76,6 +85,7 @@ ...@@ -76,6 +85,7 @@
#define SOUNDFORMAT_FLAG_16BIT 0x0010 #define SOUNDFORMAT_FLAG_16BIT 0x0010
#define SOUNDFORMAT_FLAG_2CHANNELS 0x0020 #define SOUNDFORMAT_FLAG_2CHANNELS 0x0020
/* define frequency helpers, for maximum value safety */ /* define frequency helpers, for maximum value safety */
enum azf_freq_t { enum azf_freq_t {
#define AZF_FREQ(rate) AZF_FREQ_##rate = rate #define AZF_FREQ(rate) AZF_FREQ_##rate = rate
...@@ -96,29 +106,6 @@ enum azf_freq_t { ...@@ -96,29 +106,6 @@ enum azf_freq_t {
#undef AZF_FREQ #undef AZF_FREQ
}; };
/** recording area (see also: playback bit flag definitions) **/
#define IDX_IO_REC_FLAGS 0x20 /* ??, PU:0x0000 */
#define IDX_IO_REC_IRQTYPE 0x22 /* ??, PU:0x0000 */
#define IRQ_REC_SOMETHING 0x0001 /* something & ACK */
#define IRQ_FINISHED_RECBUF_1 0x0002 /* 1st dmabuf finished & ACK */
#define IRQ_FINISHED_RECBUF_2 0x0004 /* 2nd dmabuf finished & ACK */
/* hmm, maybe these are just the corresponding *recording* flags ?
* but OTOH they are most likely at port 0x22 instead */
#define IRQMASK_SOME_STATUS_1 0x0008 /* \ related bits */
#define IRQMASK_SOME_STATUS_2 0x0010 /* / (checked together in loop) */
#define IDX_IO_REC_DMA_START_1 0x24 /* PU:0x00000000 */
#define IDX_IO_REC_DMA_START_2 0x28 /* PU:0x00000000 */
#define IDX_IO_REC_DMA_LEN_1 0x2c /* PU:0x0000 */
#define IDX_IO_REC_DMA_LEN_2 0x2e /* PU:0x0000 */
#define IDX_IO_REC_DMA_CURRPOS 0x30 /* PU:0x00000000 */
#define IDX_IO_REC_DMA_CURROFS 0x34 /* PU:0x00000000 */
#define IDX_IO_REC_SOUNDFORMAT 0x36 /* PU:0x0000 */
/** hmm, what is this I/O area for? MPU401?? or external DAC via I2S?? (after playback, recording, ???, timer) **/
#define IDX_IO_SOMETHING_FLAGS 0x40 /* gets set to 0x34 just like port 0x0 and 0x20 on card init, PU:0x0000 */
/* general */
#define IDX_IO_42H 0x42 /* PU:0x0001 */
/** DirectX timer, main interrupt area (FIXME: and something else?) **/ /** DirectX timer, main interrupt area (FIXME: and something else?) **/
#define IDX_IO_TIMER_VALUE 0x60 /* found this timer area by pure luck :-) */ #define IDX_IO_TIMER_VALUE 0x60 /* found this timer area by pure luck :-) */
/* timer countdown value; triggers IRQ when timer is finished */ /* timer countdown value; triggers IRQ when timer is finished */
...@@ -133,17 +120,19 @@ enum azf_freq_t { ...@@ -133,17 +120,19 @@ enum azf_freq_t {
#define IDX_IO_IRQSTATUS 0x64 #define IDX_IO_IRQSTATUS 0x64
/* some IRQ bit in here might also be used to signal a power-management timer /* some IRQ bit in here might also be used to signal a power-management timer
* timeout, to request shutdown of the chip (e.g. AD1815JS has such a thing). * timeout, to request shutdown of the chip (e.g. AD1815JS has such a thing).
* Some OPL3 hardware (e.g. in LM4560) has some special timer hardware which * OPL3 hardware contains several timers which confusingly in most cases
* can trigger an OPL3 timer IRQ, so maybe there's such a thing as well... */ * are NOT routed to an IRQ, but some designs (e.g. LM4560) DO support that,
* so I wouldn't be surprised at all to discover that AZF3328
* supports that thing as well... */
#define IRQ_PLAYBACK 0x0001 #define IRQ_PLAYBACK 0x0001
#define IRQ_RECORDING 0x0002 #define IRQ_RECORDING 0x0002
#define IRQ_UNKNOWN1 0x0004 /* most probably I2S port */ #define IRQ_I2S_OUT 0x0004 /* this IS I2S, right!? (untested) */
#define IRQ_GAMEPORT 0x0008 /* Interrupt of Digital(ly) Enhanced Game Port */ #define IRQ_GAMEPORT 0x0008 /* Interrupt of Digital(ly) Enhanced Game Port */
#define IRQ_MPU401 0x0010 #define IRQ_MPU401 0x0010
#define IRQ_TIMER 0x0020 /* DirectX timer */ #define IRQ_TIMER 0x0020 /* DirectX timer */
#define IRQ_UNKNOWN2 0x0040 /* probably unused, or possibly I2S port? */ #define IRQ_UNKNOWN2 0x0040 /* probably unused, or possibly OPL3 timer? */
#define IRQ_UNKNOWN3 0x0080 /* probably unused, or possibly I2S port? */ #define IRQ_UNKNOWN3 0x0080 /* probably unused, or possibly OPL3 timer? */
#define IDX_IO_66H 0x66 /* writing 0xffff returns 0x0000 */ #define IDX_IO_66H 0x66 /* writing 0xffff returns 0x0000 */
/* this is set to e.g. 0x3ff or 0x300, and writable; /* this is set to e.g. 0x3ff or 0x300, and writable;
* maybe some buffer limit, but I couldn't find out more, PU:0x00ff: */ * maybe some buffer limit, but I couldn't find out more, PU:0x00ff: */
...@@ -206,7 +195,7 @@ enum azf_freq_t { ...@@ -206,7 +195,7 @@ enum azf_freq_t {
/*** Gameport area port indices ***/ /*** Gameport area port indices ***/
/* (only 0x06 of 0x08 bytes saved/restored by Windows driver) */ /* (only 0x06 of 0x08 bytes saved/restored by Windows driver) */
#define AZF_IO_SIZE_GAME 0x08 #define AZF_IO_SIZE_GAME 0x08
#define AZF_IO_SIZE_GAME_PM 0x06 #define AZF_IO_SIZE_GAME_PM 0x06
enum { enum {
AZF_GAME_LEGACY_IO_PORT = 0x200 AZF_GAME_LEGACY_IO_PORT = 0x200
...@@ -272,6 +261,12 @@ enum { ...@@ -272,6 +261,12 @@ enum {
* 11 --> 1/200: */ * 11 --> 1/200: */
#define GAME_HWCFG_ADC_COUNTER_FREQ_MASK 0x06 #define GAME_HWCFG_ADC_COUNTER_FREQ_MASK 0x06
/* FIXME: these values might be reversed... */
#define GAME_HWCFG_ADC_COUNTER_FREQ_STD 0
#define GAME_HWCFG_ADC_COUNTER_FREQ_1_2 1
#define GAME_HWCFG_ADC_COUNTER_FREQ_1_20 2
#define GAME_HWCFG_ADC_COUNTER_FREQ_1_200 3
/* enable gameport legacy I/O address (0x200) /* enable gameport legacy I/O address (0x200)
* I was unable to locate any configurability for a different address: */ * I was unable to locate any configurability for a different address: */
#define GAME_HWCFG_LEGACY_ADDRESS_ENABLE 0x08 #define GAME_HWCFG_LEGACY_ADDRESS_ENABLE 0x08
...@@ -281,6 +276,7 @@ enum { ...@@ -281,6 +276,7 @@ enum {
#define AZF_IO_SIZE_MPU_PM 0x04 #define AZF_IO_SIZE_MPU_PM 0x04
/*** OPL3 synth ***/ /*** OPL3 synth ***/
/* (only 0x06 of 0x08 bytes saved/restored by Windows driver) */
#define AZF_IO_SIZE_OPL3 0x08 #define AZF_IO_SIZE_OPL3 0x08
#define AZF_IO_SIZE_OPL3_PM 0x06 #define AZF_IO_SIZE_OPL3_PM 0x06
/* hmm, given that a standard OPL3 has 4 registers only, /* hmm, given that a standard OPL3 has 4 registers only,
...@@ -340,4 +336,7 @@ enum { ...@@ -340,4 +336,7 @@ enum {
#define SET_CHAN_LEFT 1 #define SET_CHAN_LEFT 1
#define SET_CHAN_RIGHT 2 #define SET_CHAN_RIGHT 2
/* helper macro to align I/O port ranges to 32bit I/O width */
#define AZF_ALIGN(x) (((x) + 3) & (~3))
#endif /* __SOUND_AZT3328_H */ #endif /* __SOUND_AZT3328_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment