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
config SND_AZT3328
tristate "Aztech AZF3328 / PCI168 (EXPERIMENTAL)"
depends on EXPERIMENTAL
tristate "Aztech AZF3328 / PCI168"
select SND_OPL3_LIB
select SND_MPU401_UART
select SND_PCM
select SND_RAWMIDI
help
Say Y here to include support for Aztech AZF3328 (PCI168)
soundcards.
......
/*
* 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.
* Driver developed on PCI168 AP(W) version (PCI rev. 10, subsystem ID 1801),
......@@ -10,6 +10,13 @@
* PCI168 A/AP, sub ID 8000
* 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
* 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
......@@ -71,10 +78,11 @@
* - built-in General DirectX timer having a 20 bits counter
* with 1us resolution (see below!)
* - 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 hardware volume control
* - 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?)
* At least the Trident 4D Wave DX has one bit somewhere
* to enable writes to PCI subsystem VID registers, that should be it.
......@@ -82,6 +90,7 @@
* some custom data starting at 0x80. What kind of config settings
* are located in our extended PCI space anyway??
* - 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,
* since it additionally supports the card's 1MHz DirectX timer - just try
......@@ -146,10 +155,15 @@
* to read the Digital Enhanced Game Port. Not sure whether it is fixable.
*
* 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.
* - 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
* haven't figured out yet and which thus might not even exist...
* as long as they're unused, to improve audio quality and save power).
* 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
* some improvement, too...
* - figure out what all unknown port bits are responsible for
......@@ -185,25 +199,46 @@ MODULE_SUPPORTED_DEVICE("{{Aztech,AZF3328}}");
#define SUPPORT_GAMEPORT 1
#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_CALLS 0
#define DEBUG_MIXER 0
#define DEBUG_PLAY_REC 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_ERR format, ##args)
#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_ERR "--> %s\n", __func__)
#define snd_azf3328_dbgcallleave() printk(KERN_ERR "<-- %s\n", __func__)
#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()
......@@ -216,10 +251,10 @@ MODULE_SUPPORTED_DEVICE("{{Aztech,AZF3328}}");
#define snd_azf3328_dbgmixer(format, args...)
#endif
#if DEBUG_PLAY_REC
#define snd_azf3328_dbgplay(format, args...) printk(KERN_DEBUG format, ##args)
#if DEBUG_CODEC
#define snd_azf3328_dbgcodec(format, args...) printk(KERN_DEBUG format, ##args)
#else
#define snd_azf3328_dbgplay(format, args...)
#define snd_azf3328_dbgcodec(format, args...)
#endif
#if DEBUG_MISC
......@@ -234,6 +269,12 @@ MODULE_SUPPORTED_DEVICE("{{Aztech,AZF3328}}");
#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.");
......@@ -250,22 +291,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_audio_stream {
struct snd_azf3328_codec_data {
unsigned long io_base;
struct snd_pcm_substream *substream;
int enabled;
int running;
unsigned long portbase;
bool running;
const char *name;
};
enum snd_azf3328_stream_index {
AZF_PLAYBACK = 0,
AZF_CAPTURE = 1,
enum snd_azf3328_codec_type {
AZF_CODEC_PLAYBACK = 0,
AZF_CODEC_CAPTURE = 1,
AZF_CODEC_I2S_OUT = 2,
};
struct snd_azf3328 {
/* 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 mpu_io; /* usually 0xb800, size 4 */
unsigned long opl3_io; /* usually 0xbc00, size 8 */
......@@ -275,15 +317,17 @@ struct snd_azf3328 {
struct snd_timer *timer;
struct snd_pcm *pcm;
struct snd_azf3328_audio_stream audio_stream[2];
struct snd_pcm *pcm[3];
/* playback, recording and I2S out codecs */
struct snd_azf3328_codec_data codecs[3];
struct snd_card *card;
struct snd_rawmidi *rmidi;
#ifdef SUPPORT_GAMEPORT
struct gameport *gameport;
int axes[4];
u16 axes[4];
#endif
struct pci_dev *pci;
......@@ -293,16 +337,16 @@ struct snd_azf3328 {
* If we need to add more registers here, then we might try to fold this
* into some transparent combined shadow register handling with
* CONFIG_PM register storage below, but that's slightly difficult. */
u16 shadow_reg_codec_6AH;
u16 shadow_reg_ctrl_6AH;
#ifdef CONFIG_PM
/* register value containers for power management
* Note: not always full I/O range preserved (just like Win driver!) */
u16 saved_regs_codec[AZF_IO_SIZE_CODEC_PM / 2];
u16 saved_regs_game [AZF_IO_SIZE_GAME_PM / 2];
u16 saved_regs_mpu [AZF_IO_SIZE_MPU_PM / 2];
u16 saved_regs_opl3 [AZF_IO_SIZE_OPL3_PM / 2];
u16 saved_regs_mixer[AZF_IO_SIZE_MIXER_PM / 2];
* Note: not always full I/O range preserved (similar to Win driver!) */
u32 saved_regs_ctrl[AZF_ALIGN(AZF_IO_SIZE_CTRL_PM) / 4];
u32 saved_regs_game[AZF_ALIGN(AZF_IO_SIZE_GAME_PM) / 4];
u32 saved_regs_mpu[AZF_ALIGN(AZF_IO_SIZE_MPU_PM) / 4];
u32 saved_regs_opl3[AZF_ALIGN(AZF_IO_SIZE_OPL3_PM) / 4];
u32 saved_regs_mixer[AZF_ALIGN(AZF_IO_SIZE_MIXER_PM) / 4];
#endif
};
......@@ -316,7 +360,7 @@ MODULE_DEVICE_TABLE(pci, snd_azf3328_ids);
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;
......@@ -331,39 +375,72 @@ snd_azf3328_io_reg_setb(unsigned reg, u8 mask, int do_set)
}
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
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
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
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
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
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
......@@ -404,13 +481,13 @@ snd_azf3328_mixer_inw(const struct snd_azf3328 *chip, unsigned reg)
#define AZF_MUTE_BIT 0x80
static int
static bool
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;
int updated;
bool updated;
/* the mute bit is on the *second* (i.e. right) register of a
* left/right channel setting */
......@@ -569,7 +646,7 @@ snd_azf3328_get_mixer(struct snd_kcontrol *kcontrol,
{
struct snd_azf3328 *chip = snd_kcontrol_chip(kcontrol);
struct azf3328_mixer_reg reg;
unsigned int oreg, val;
u16 oreg, val;
snd_azf3328_dbgcallenter();
snd_azf3328_mixer_reg_decode(&reg, kcontrol->private_value);
......@@ -600,7 +677,7 @@ snd_azf3328_put_mixer(struct snd_kcontrol *kcontrol,
{
struct snd_azf3328 *chip = snd_kcontrol_chip(kcontrol);
struct azf3328_mixer_reg reg;
unsigned int oreg, nreg, val;
u16 oreg, nreg, val;
snd_azf3328_dbgcallenter();
snd_azf3328_mixer_reg_decode(&reg, kcontrol->private_value);
......@@ -709,7 +786,7 @@ snd_azf3328_put_mixer_enum(struct snd_kcontrol *kcontrol,
{
struct snd_azf3328 *chip = snd_kcontrol_chip(kcontrol);
struct azf3328_mixer_reg reg;
unsigned int oreg, nreg, val;
u16 oreg, nreg, val;
snd_azf3328_mixer_reg_decode(&reg, kcontrol->private_value);
oreg = snd_azf3328_mixer_inw(chip, reg.reg);
......@@ -867,14 +944,15 @@ snd_azf3328_hw_free(struct snd_pcm_substream *substream)
static void
snd_azf3328_codec_setfmt(struct snd_azf3328 *chip,
unsigned reg,
enum snd_azf3328_codec_type codec_type,
enum azf_freq_t bitrate,
unsigned int format_width,
unsigned int channels
)
{
u16 val = 0xff00;
unsigned long flags;
const struct snd_azf3328_codec_data *codec = &chip->codecs[codec_type];
u16 val = 0xff00;
snd_azf3328_dbgcallenter();
switch (bitrate) {
......@@ -917,7 +995,7 @@ snd_azf3328_codec_setfmt(struct snd_azf3328 *chip,
spin_lock_irqsave(&chip->reg_lock, flags);
/* 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
* 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,
* (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 (reg == IDX_IO_PLAY_SOUNDFORMAT) /* only do it for playback */
snd_azf3328_codec_outw(chip, IDX_IO_PLAY_FLAGS,
snd_azf3328_codec_inw(chip, IDX_IO_PLAY_FLAGS) |
DMA_PLAY_SOMETHING1 |
DMA_PLAY_SOMETHING2 |
if (codec_type == AZF_CODEC_PLAYBACK) /* only do it for playback */
snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS,
snd_azf3328_codec_inw(codec, IDX_IO_CODEC_DMA_FLAGS) |
DMA_RUN_SOMETHING1 |
DMA_RUN_SOMETHING2 |
SOMETHING_ALMOST_ALWAYS_SET |
DMA_EPILOGUE_SOMETHING |
DMA_SOMETHING_ELSE
......@@ -942,112 +1020,134 @@ snd_azf3328_codec_setfmt(struct snd_azf3328 *chip,
static inline void
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.
* 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, reg, AZF_FREQ_4000, 8, 1);
snd_azf3328_codec_setfmt(chip, codec_type, AZF_FREQ_4000, 8, 1);
}
static void
snd_azf3328_codec_reg_6AH_update(struct snd_azf3328 *chip,
snd_azf3328_ctrl_reg_6AH_update(struct snd_azf3328 *chip,
unsigned bitmask,
int enable
bool enable
)
{
if (enable)
chip->shadow_reg_codec_6AH &= ~bitmask;
bool do_mask = !enable;
if (do_mask)
chip->shadow_reg_ctrl_6AH |= bitmask;
else
chip->shadow_reg_codec_6AH |= bitmask;
snd_azf3328_dbgplay("6AH_update mask 0x%04x enable %d: val 0x%04x\n",
bitmask, enable, chip->shadow_reg_codec_6AH);
snd_azf3328_codec_outw(chip, IDX_IO_6AH, chip->shadow_reg_codec_6AH);
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);
snd_azf3328_ctrl_outw(chip, IDX_IO_6AH, chip->shadow_reg_ctrl_6AH);
}
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
* PM related */
snd_azf3328_codec_reg_6AH_update(
snd_azf3328_ctrl_reg_6AH_update(
chip, IO_6A_PAUSE_PLAYBACK_BIT8, enable
);
}
static void
snd_azf3328_codec_activity(struct snd_azf3328 *chip,
enum snd_azf3328_stream_index stream_type,
int enable
snd_azf3328_ctrl_codec_activity(struct snd_azf3328 *chip,
enum snd_azf3328_codec_type codec_type,
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(
"codec_activity: type %d, enable %d, need_change %d\n",
stream_type, enable, need_change
snd_azf3328_dbgcodec(
"codec_activity: %s codec, enable %d, need_change %d\n",
codec->name, enable, need_change
);
if (need_change) {
enum snd_azf3328_stream_index other =
(stream_type == AZF_PLAYBACK) ?
AZF_CAPTURE : AZF_PLAYBACK;
/* small check to prevent shutting down the other party
* in case it's active */
if ((enable) || !(chip->audio_stream[other].running))
snd_azf3328_codec_enable(chip, enable);
static const struct {
enum snd_azf3328_codec_type other1;
enum snd_azf3328_codec_type other2;
} peer_codecs[3] =
{ { AZF_CODEC_CAPTURE, AZF_CODEC_I2S_OUT },
{ AZF_CODEC_PLAYBACK, AZF_CODEC_I2S_OUT },
{ 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
* (reduce noise and power consumption) */
if (!enable)
snd_azf3328_codec_setfmt_lowpower(
chip,
chip->audio_stream[stream_type].portbase
+ IDX_IO_PLAY_SOUNDFORMAT
codec_type
);
codec->running = enable;
}
chip->audio_stream[stream_type].running = enable;
}
static void
snd_azf3328_setdmaa(struct snd_azf3328 *chip,
long unsigned int addr,
snd_azf3328_codec_setdmaa(struct snd_azf3328 *chip,
enum snd_azf3328_codec_type codec_type,
unsigned long addr,
unsigned int count,
unsigned int size,
enum snd_azf3328_stream_index stream_type
unsigned int size
)
{
const struct snd_azf3328_codec_data *codec = &chip->codecs[codec_type];
snd_azf3328_dbgcallenter();
if (!chip->audio_stream[stream_type].running) {
/* AZF3328 uses a two buffer pointer DMA playback approach */
if (!codec->running) {
/* AZF3328 uses a two buffer pointer DMA transfer approach */
unsigned long flags, portbase, addr_area2;
unsigned long flags, addr_area2;
/* 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;
addr_area2 = addr+count_areas;
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 */
count_tmp = count_areas;
count_areas |= (count_tmp << 16);
lengths = (count_areas << 16) | (count_areas);
spin_lock_irqsave(&chip->reg_lock, flags);
outl(addr, portbase + IDX_IO_PLAY_DMA_START_1);
outl(addr_area2, portbase + IDX_IO_PLAY_DMA_START_2);
outl(count_areas, portbase + IDX_IO_PLAY_DMA_LEN_1);
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);
}
snd_azf3328_dbgcallleave();
}
static int
snd_azf3328_playback_prepare(struct snd_pcm_substream *substream)
snd_azf3328_codec_prepare(struct snd_pcm_substream *substream)
{
#if 0
struct snd_azf3328 *chip = snd_pcm_substream_chip(substream);
......@@ -1058,157 +1158,161 @@ snd_azf3328_playback_prepare(struct snd_pcm_substream *substream)
snd_azf3328_dbgcallenter();
#if 0
snd_azf3328_codec_setfmt(chip, IDX_IO_PLAY_SOUNDFORMAT,
snd_azf3328_codec_setfmt(chip, AZF_CODEC_...,
runtime->rate,
snd_pcm_format_width(runtime->format),
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
snd_azf3328_dbgcallleave();
return 0;
}
static int
snd_azf3328_capture_prepare(struct snd_pcm_substream *substream)
{
#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)
snd_azf3328_codec_trigger(enum snd_azf3328_codec_type codec_type,
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;
int result = 0;
unsigned int status1;
int previously_muted;
u16 flags1;
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) {
case SNDRV_PCM_TRIGGER_START:
snd_azf3328_dbgplay("START PLAYBACK\n");
snd_azf3328_dbgcodec("START %s\n", codec->name);
if (is_playback_codec) {
/* mute WaveOut (avoid clicking during setup) */
previously_muted =
snd_azf3328_mixer_set_mute(chip, IDX_MIXER_WAVEOUT, 1);
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,
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_PLAY_FLAGS);
flags1 = snd_azf3328_codec_inw(codec, IDX_IO_CODEC_DMA_FLAGS);
/* stop playback */
status1 &= ~DMA_RESUME;
snd_azf3328_codec_outw(chip, IDX_IO_PLAY_FLAGS, status1);
/* stop transfer */
flags1 &= ~DMA_RESUME;
snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, flags1);
/* 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);
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_buffer_bytes(substream),
AZF_PLAYBACK);
snd_pcm_lib_buffer_bytes(substream)
);
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_PLAY_FLAGS, status1);
flags1 |= DMA_RUN_SOMETHING1 | DMA_RUN_SOMETHING2;
snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, flags1);
/* start playback again */
/* start transfer again */
/* FIXME: what is this value (0x0010)??? */
status1 |= DMA_RESUME | DMA_EPILOGUE_SOMETHING;
snd_azf3328_codec_outw(chip, IDX_IO_PLAY_FLAGS, status1);
flags1 |= DMA_RESUME | DMA_EPILOGUE_SOMETHING;
snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, flags1);
#else /* NT4 */
snd_azf3328_codec_outw(chip, IDX_IO_PLAY_FLAGS,
snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS,
0x0000);
snd_azf3328_codec_outw(chip, IDX_IO_PLAY_FLAGS,
DMA_PLAY_SOMETHING1);
snd_azf3328_codec_outw(chip, IDX_IO_PLAY_FLAGS,
DMA_PLAY_SOMETHING1 |
DMA_PLAY_SOMETHING2);
snd_azf3328_codec_outw(chip, IDX_IO_PLAY_FLAGS,
snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS,
DMA_RUN_SOMETHING1);
snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS,
DMA_RUN_SOMETHING1 |
DMA_RUN_SOMETHING2);
snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_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_PLAYBACK, 1);
snd_azf3328_ctrl_codec_activity(chip, codec_type, 1);
if (is_playback_codec) {
/* now unmute WaveOut */
if (!previously_muted)
snd_azf3328_mixer_set_mute(chip, IDX_MIXER_WAVEOUT, 0);
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;
case SNDRV_PCM_TRIGGER_RESUME:
snd_azf3328_dbgplay("RESUME PLAYBACK\n");
/* resume playback if we were active */
snd_azf3328_dbgcodec("RESUME %s\n", codec->name);
/* resume codec if we were active */
spin_lock(&chip->reg_lock);
if (chip->audio_stream[AZF_PLAYBACK].running)
snd_azf3328_codec_outw(chip, IDX_IO_PLAY_FLAGS,
snd_azf3328_codec_inw(chip, IDX_IO_PLAY_FLAGS) | DMA_RESUME);
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);
break;
case SNDRV_PCM_TRIGGER_STOP:
snd_azf3328_dbgplay("STOP PLAYBACK\n");
snd_azf3328_dbgcodec("STOP %s\n", codec->name);
if (is_playback_codec) {
/* mute WaveOut (avoid clicking during setup) */
previously_muted =
snd_azf3328_mixer_set_mute(chip, IDX_MIXER_WAVEOUT, 1);
snd_azf3328_mixer_set_mute(
chip, IDX_MIXER_WAVEOUT, 1
);
}
spin_lock(&chip->reg_lock);
/* 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 */
status1 &= ~DMA_RESUME;
snd_azf3328_codec_outw(chip, IDX_IO_PLAY_FLAGS, status1);
/* stop transfer */
flags1 &= ~DMA_RESUME;
snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, flags1);
/* hmm, is this really required? we're resetting the same bit
* immediately thereafter... */
status1 |= DMA_PLAY_SOMETHING1;
snd_azf3328_codec_outw(chip, IDX_IO_PLAY_FLAGS, status1);
flags1 |= DMA_RUN_SOMETHING1;
snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, flags1);
status1 &= ~DMA_PLAY_SOMETHING1;
snd_azf3328_codec_outw(chip, IDX_IO_PLAY_FLAGS, status1);
flags1 &= ~DMA_RUN_SOMETHING1;
snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, flags1);
spin_unlock(&chip->reg_lock);
snd_azf3328_codec_activity(chip, AZF_PLAYBACK, 0);
snd_azf3328_ctrl_codec_activity(chip, codec_type, 0);
if (is_playback_codec) {
/* now unmute WaveOut */
if (!previously_muted)
snd_azf3328_mixer_set_mute(chip, IDX_MIXER_WAVEOUT, 0);
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;
case SNDRV_PCM_TRIGGER_SUSPEND:
snd_azf3328_dbgplay("SUSPEND PLAYBACK\n");
/* make sure playback is stopped */
snd_azf3328_codec_outw(chip, IDX_IO_PLAY_FLAGS,
snd_azf3328_codec_inw(chip, IDX_IO_PLAY_FLAGS) & ~DMA_RESUME);
snd_azf3328_dbgcodec("SUSPEND %s\n", codec->name);
/* make sure codec is stopped */
snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS,
snd_azf3328_codec_inw(
codec, IDX_IO_CODEC_DMA_FLAGS
) & ~DMA_RESUME
);
break;
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
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)
snd_printk(KERN_ERR "FIXME: SNDRV_PCM_TRIGGER_PAUSE_RELEASE NIY!\n");
break;
default:
printk(KERN_ERR "FIXME: unknown trigger mode!\n");
snd_printk(KERN_ERR "FIXME: unknown trigger mode!\n");
return -EINVAL;
}
......@@ -1225,172 +1329,74 @@ snd_azf3328_playback_trigger(struct snd_pcm_substream *substream, int cmd)
return result;
}
/* this is just analogous to playback; I'm not quite sure whether recording
* should actually be triggered like that */
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);
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);
return snd_azf3328_codec_trigger(AZF_CODEC_PLAYBACK, substream, cmd);
}
snd_azf3328_dbgplay("STOPPED CAPTURE\n");
break;
case SNDRV_PCM_TRIGGER_SUSPEND:
snd_azf3328_dbgplay("SUSPEND CAPTURE\n");
/* 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;
}
static int
snd_azf3328_codec_capture_trigger(struct snd_pcm_substream *substream, int cmd)
{
return snd_azf3328_codec_trigger(AZF_CODEC_CAPTURE, substream, cmd);
}
snd_azf3328_dbgcallleave();
return result;
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_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;
snd_pcm_uframes_t frmres;
#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
bufptr = substream->runtime->dma_addr;
#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 */
result -= bufptr;
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;
}
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);
unsigned long bufptr, result;
snd_pcm_uframes_t frmres;
return snd_azf3328_codec_pointer(substream, AZF_CODEC_PLAYBACK);
}
#ifdef QUERY_HARDWARE
bufptr = snd_azf3328_codec_inl(chip, IDX_IO_REC_DMA_START_1);
#else
bufptr = substream->runtime->dma_addr;
#endif
result = snd_azf3328_codec_inl(chip, IDX_IO_REC_DMA_CURRPOS);
static snd_pcm_uframes_t
snd_azf3328_codec_capture_pointer(struct snd_pcm_substream *substream)
{
return snd_azf3328_codec_pointer(substream, AZF_CODEC_CAPTURE);
}
/* calculate offset */
result -= bufptr;
frmres = bytes_to_frames( substream->runtime, result);
snd_azf3328_dbgplay("REC @ 0x%8lx, frames %8ld\n", result, frmres);
return frmres;
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
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(
chip->game_io+IDX_GAME_HWCONFIG,
......@@ -1400,7 +1406,9 @@ snd_azf3328_gameport_irq_enable(struct snd_azf3328 *chip, int enable)
}
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(
chip->game_io+IDX_GAME_HWCONFIG,
......@@ -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
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
);
}
......@@ -1447,6 +1472,8 @@ snd_azf3328_gameport_open(struct gameport *gameport, int mode)
break;
}
snd_azf3328_gameport_set_counter_frequency(chip,
GAME_HWCFG_ADC_COUNTER_FREQ_STD);
snd_azf3328_gameport_axis_circuit_enable(chip, (res == 0));
return res;
......@@ -1458,6 +1485,8 @@ snd_azf3328_gameport_close(struct gameport *gameport)
struct snd_azf3328 *chip = gameport_get_port_data(gameport);
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);
}
......@@ -1491,7 +1520,7 @@ snd_azf3328_gameport_cooked_read(struct gameport *gameport,
val = snd_azf3328_game_inb(chip, IDX_GAME_AXES_CONFIG);
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 */
val = (i << 4) | 0x0f;
snd_azf3328_game_outb(chip, IDX_GAME_AXES_CONFIG, val);
......@@ -1514,7 +1543,7 @@ snd_azf3328_gameport_cooked_read(struct gameport *gameport,
snd_azf3328_game_outw(chip, IDX_GAME_AXIS_VALUE, 0xffff);
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];
if (axes[i] == 0xffff)
axes[i] = -1;
......@@ -1552,6 +1581,8 @@ snd_azf3328_gameport(struct snd_azf3328 *chip, int dev)
/* DISABLE legacy address: we don't need it! */
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);
gameport_register_port(chip->gameport);
......@@ -1585,40 +1616,77 @@ snd_azf3328_gameport_interrupt(struct snd_azf3328 *chip)
static inline void
snd_azf3328_irq_log_unknown_type(u8 which)
{
snd_azf3328_dbgplay(
snd_azf3328_dbgcodec(
"azt3328: unknown IRQ type (%x) occurred, please report!\n",
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
snd_azf3328_interrupt(int irq, void *dev_id)
{
struct snd_azf3328 *chip = dev_id;
u8 status, which;
#if DEBUG_PLAY_REC
u8 status;
#if DEBUG_CODEC
static unsigned long irq_count;
#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 */
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 */
snd_azf3328_dbgplay(
"irq_count %ld! IDX_IO_PLAY_FLAGS %04x, "
"IDX_IO_PLAY_IRQTYPE %04x, IDX_IO_IRQSTATUS %04x\n",
snd_azf3328_dbgcodec(
"irq_count %ld! IDX_IO_IRQSTATUS %04x\n",
irq_count++ /* debug-only */,
snd_azf3328_codec_inw(chip, IDX_IO_PLAY_FLAGS),
snd_azf3328_codec_inw(chip, IDX_IO_PLAY_IRQTYPE),
status
);
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)
& TIMER_VALUE_MASK
); */
......@@ -1626,71 +1694,36 @@ snd_azf3328_interrupt(int irq, void *dev_id)
snd_timer_interrupt(chip->timer, chip->timer->sticks);
/* ACK timer */
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);
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) {
snd_pcm_period_elapsed(
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 (status & (IRQ_PLAYBACK|IRQ_RECORDING|IRQ_I2S_OUT))
snd_azf3328_codec_interrupt(chip, status);
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)
snd_azf3328_gameport_interrupt(chip);
/* MPU401 has less critical IRQ requirements
* than timer and playback/recording, right? */
if (status & IRQ_MPU401) {
snd_mpu401_uart_interrupt(irq, chip->rmidi->private_data);
/* hmm, do we have to ack the IRQ here somehow?
* If so, then I don't know how... */
snd_azf3328_dbgplay("azt3328: MPU401 IRQ\n");
* If so, then I don't know how yet... */
snd_azf3328_dbgcodec("azt3328: MPU401 IRQ\n");
}
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? */
.info = SNDRV_PCM_INFO_MMAP |
......@@ -1718,31 +1751,6 @@ static const struct snd_pcm_hardware snd_azf3328_playback =
.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[] = {
AZF_FREQ_4000,
......@@ -1770,55 +1778,72 @@ static struct snd_pcm_hw_constraint_list snd_azf3328_hw_constraints_rates = {
/*****************************************************************/
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_pcm_runtime *runtime = substream->runtime;
snd_azf3328_dbgcallenter();
chip->audio_stream[AZF_PLAYBACK].substream = substream;
runtime->hw = snd_azf3328_playback;
chip->codecs[codec_type].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();
return 0;
}
static int
snd_azf3328_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)
{
struct snd_azf3328 *chip = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
return snd_azf3328_pcm_open(substream, AZF_CODEC_CAPTURE);
}
snd_azf3328_dbgcallenter();
chip->audio_stream[AZF_CAPTURE].substream = substream;
runtime->hw = snd_azf3328_capture;
snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
&snd_azf3328_hw_constraints_rates);
snd_azf3328_dbgcallleave();
return 0;
static int
snd_azf3328_i2s_out_open(struct snd_pcm_substream *substream)
{
return snd_azf3328_pcm_open(substream, AZF_CODEC_I2S_OUT);
}
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);
snd_azf3328_dbgcallenter();
chip->audio_stream[AZF_PLAYBACK].substream = NULL;
chip->codecs[codec_type].substream = NULL;
snd_azf3328_dbgcallleave();
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)
{
struct snd_azf3328 *chip = snd_pcm_substream_chip(substream);
return snd_azf3328_pcm_close(substream, AZF_CODEC_CAPTURE);
}
snd_azf3328_dbgcallenter();
chip->audio_stream[AZF_CAPTURE].substream = NULL;
snd_azf3328_dbgcallleave();
return 0;
static int
snd_azf3328_i2s_out_close(struct snd_pcm_substream *substream)
{
return snd_azf3328_pcm_close(substream, AZF_CODEC_I2S_OUT);
}
/******************************************************************/
......@@ -1829,9 +1854,9 @@ static struct snd_pcm_ops snd_azf3328_playback_ops = {
.ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_azf3328_hw_params,
.hw_free = snd_azf3328_hw_free,
.prepare = snd_azf3328_playback_prepare,
.trigger = snd_azf3328_playback_trigger,
.pointer = snd_azf3328_playback_pointer
.prepare = snd_azf3328_codec_prepare,
.trigger = snd_azf3328_codec_playback_trigger,
.pointer = snd_azf3328_codec_playback_pointer
};
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,
.hw_params = snd_azf3328_hw_params,
.hw_free = snd_azf3328_hw_free,
.prepare = snd_azf3328_capture_prepare,
.trigger = snd_azf3328_capture_trigger,
.pointer = snd_azf3328_capture_pointer
.prepare = snd_azf3328_codec_prepare,
.trigger = snd_azf3328_codec_capture_trigger,
.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
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;
int err;
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;
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
&snd_azf3328_playback_ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
&snd_azf3328_capture_ops);
pcm->private_data = chip;
pcm->info_flags = 0;
strcpy(pcm->name, chip->card->shortname);
/* 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_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_playback_ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_azf3328_capture_ops);
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 = pcm;
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_dma_pci_data(chip->pci),
64*1024, 64*1024);
snd_azf3328_dbgcallleave();
return 0;
......@@ -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);
delay |= TIMER_COUNTDOWN_ENABLE | TIMER_IRQ_ENABLE;
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);
snd_azf3328_dbgcallleave();
return 0;
......@@ -1919,7 +1981,7 @@ snd_azf3328_timer_stop(struct snd_timer *timer)
spin_lock_irqsave(&chip->reg_lock, flags);
/* disable timer countdown and interrupt */
/* 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);
snd_azf3328_dbgcallleave();
return 0;
......@@ -2035,7 +2097,7 @@ snd_azf3328_test_bit(unsigned unsigned reg, int bit)
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
);
}
......@@ -2048,9 +2110,9 @@ snd_azf3328_debug_show_ports(const struct snd_azf3328 *chip)
u16 tmp;
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",
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
);
......@@ -2083,9 +2145,9 @@ snd_azf3328_debug_show_ports(const struct snd_azf3328 *chip)
inb(0x38c + tmp)
);
for (tmp = 0; tmp < AZF_IO_SIZE_CODEC; tmp += 2)
snd_azf3328_dbgmisc("codec 0x%02x: 0x%04x\n",
tmp, snd_azf3328_codec_inw(chip, 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)
);
for (tmp = 0; tmp < AZF_IO_SIZE_MIXER; tmp += 2)
......@@ -2106,7 +2168,8 @@ snd_azf3328_create(struct snd_card *card,
static struct snd_device_ops ops = {
.dev_free = snd_azf3328_dev_free,
};
u16 tmp;
u8 dma_init;
enum snd_azf3328_codec_type codec_type;
*rchip = NULL;
......@@ -2138,14 +2201,21 @@ snd_azf3328_create(struct snd_card *card,
if (err < 0)
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->mpu_io = pci_resource_start(pci, 2);
chip->opl3_io = pci_resource_start(pci, 3);
chip->mixer_io = pci_resource_start(pci, 4);
chip->audio_stream[AZF_PLAYBACK].portbase = chip->codec_io + 0x00;
chip->audio_stream[AZF_CAPTURE].portbase = chip->codec_io + 0x20;
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";
if (request_irq(pci->irq, snd_azf3328_interrupt,
IRQF_SHARED, card->shortname, chip)) {
......@@ -2168,20 +2238,25 @@ snd_azf3328_create(struct snd_card *card,
if (err < 0)
goto out_err;
/* shutdown codecs to save power */
/* have snd_azf3328_codec_activity() act properly */
chip->audio_stream[AZF_PLAYBACK].running = 1;
snd_azf3328_codec_activity(chip, AZF_PLAYBACK, 0);
/* standard codec init stuff */
/* default DMA init value */
dma_init = DMA_RUN_SOMETHING2|DMA_EPILOGUE_SOMETHING|DMA_SOMETHING_ELSE;
/* standard chip init stuff */
/* default IRQ init value */
tmp = DMA_PLAY_SOMETHING2|DMA_EPILOGUE_SOMETHING|DMA_SOMETHING_ELSE;
for (codec_type = AZF_CODEC_PLAYBACK;
codec_type <= AZF_CODEC_I2S_OUT; ++codec_type) {
struct snd_azf3328_codec_data *codec =
&chip->codecs[codec_type];
/* shutdown codecs to save power */
/* have ...ctrl_codec_activity() act properly */
codec->running = 1;
snd_azf3328_ctrl_codec_activity(chip, codec_type, 0);
spin_lock_irq(&chip->reg_lock);
snd_azf3328_codec_outb(chip, IDX_IO_PLAY_FLAGS, tmp);
snd_azf3328_codec_outb(chip, IDX_IO_REC_FLAGS, tmp);
snd_azf3328_codec_outb(chip, IDX_IO_SOMETHING_FLAGS, tmp);
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);
......@@ -2229,8 +2304,11 @@ snd_azf3328_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
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(
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
);
if (err < 0) {
......@@ -2244,7 +2322,7 @@ snd_azf3328_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
if (err < 0)
goto out_err;
err = snd_azf3328_pcm(chip, 0);
err = snd_azf3328_pcm(chip);
if (err < 0)
goto out_err;
......@@ -2266,14 +2344,14 @@ snd_azf3328_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
opl3->private_data = chip;
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);
if (err < 0)
goto out_err;
#ifdef MODULE
printk(
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"
......@@ -2308,36 +2386,52 @@ snd_azf3328_remove(struct pci_dev *pci)
}
#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
snd_azf3328_suspend(struct pci_dev *pci, pm_message_t state)
{
struct snd_card *card = pci_get_drvdata(pci);
struct snd_azf3328 *chip = card->private_data;
unsigned reg;
u16 *saved_regs_ctrl_u16;
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)
chip->saved_regs_mixer[reg] = inw(chip->mixer_io + reg * 2);
snd_azf3328_suspend_regs(chip->mixer_io,
ARRAY_SIZE(chip->saved_regs_mixer), chip->saved_regs_mixer);
/* make sure to disable master volume etc. to prevent looping sound */
snd_azf3328_mixer_set_mute(chip, IDX_MIXER_PLAY_MASTER, 1);
snd_azf3328_mixer_set_mute(chip, IDX_MIXER_WAVEOUT, 1);
for (reg = 0; reg < AZF_IO_SIZE_CODEC_PM / 2; ++reg)
chip->saved_regs_codec[reg] = inw(chip->codec_io + reg * 2);
snd_azf3328_suspend_regs(chip->ctrl_io,
ARRAY_SIZE(chip->saved_regs_ctrl), chip->saved_regs_ctrl);
/* 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)
chip->saved_regs_game[reg] = inw(chip->game_io + reg * 2);
for (reg = 0; reg < AZF_IO_SIZE_MPU_PM / 2; ++reg)
chip->saved_regs_mpu[reg] = inw(chip->mpu_io + reg * 2);
for (reg = 0; reg < AZF_IO_SIZE_OPL3_PM / 2; ++reg)
chip->saved_regs_opl3[reg] = inw(chip->opl3_io + reg * 2);
snd_azf3328_suspend_regs(chip->game_io,
ARRAY_SIZE(chip->saved_regs_game), chip->saved_regs_game);
snd_azf3328_suspend_regs(chip->mpu_io,
ARRAY_SIZE(chip->saved_regs_mpu), chip->saved_regs_mpu);
snd_azf3328_suspend_regs(chip->opl3_io,
ARRAY_SIZE(chip->saved_regs_opl3), chip->saved_regs_opl3);
pci_disable_device(pci);
pci_save_state(pci);
......@@ -2345,12 +2439,28 @@ snd_azf3328_suspend(struct pci_dev *pci, pm_message_t state)
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)
{
struct snd_card *card = pci_get_drvdata(pci);
struct snd_azf3328 *chip = card->private_data;
unsigned reg;
const struct snd_azf3328 *chip = card->private_data;
pci_set_power_state(pci, PCI_D0);
pci_restore_state(pci);
......@@ -2362,16 +2472,24 @@ snd_azf3328_resume(struct pci_dev *pci)
}
pci_set_master(pci);
for (reg = 0; reg < AZF_IO_SIZE_GAME_PM / 2; ++reg)
outw(chip->saved_regs_game[reg], chip->game_io + reg * 2);
for (reg = 0; reg < AZF_IO_SIZE_MPU_PM / 2; ++reg)
outw(chip->saved_regs_mpu[reg], chip->mpu_io + reg * 2);
for (reg = 0; reg < AZF_IO_SIZE_OPL3_PM / 2; ++reg)
outw(chip->saved_regs_opl3[reg], chip->opl3_io + reg * 2);
for (reg = 0; reg < AZF_IO_SIZE_MIXER_PM / 2; ++reg)
outw(chip->saved_regs_mixer[reg], chip->mixer_io + reg * 2);
for (reg = 0; reg < AZF_IO_SIZE_CODEC_PM / 2; ++reg)
outw(chip->saved_regs_codec[reg], chip->codec_io + reg * 2);
snd_azf3328_resume_regs(chip->saved_regs_game, chip->game_io,
ARRAY_SIZE(chip->saved_regs_game));
snd_azf3328_resume_regs(chip->saved_regs_mpu, chip->mpu_io,
ARRAY_SIZE(chip->saved_regs_mpu));
snd_azf3328_resume_regs(chip->saved_regs_opl3, chip->opl3_io,
ARRAY_SIZE(chip->saved_regs_opl3));
snd_azf3328_resume_regs(chip->saved_regs_mixer, chip->mixer_io,
ARRAY_SIZE(chip->saved_regs_mixer));
/* unfortunately with 32bit transfers, IDX_MIXER_PLAY_MASTER (0x02)
and IDX_MIXER_RESET (offset 0x00) get touched at the same time,
resulting in a mixer reset condition persisting until _after_
master vol was restored. Thus master vol needs an extra restore. */
outw(((u16 *)chip->saved_regs_mixer)[1], chip->mixer_io + 2);
snd_azf3328_resume_regs(chip->saved_regs_ctrl, chip->ctrl_io,
ARRAY_SIZE(chip->saved_regs_ctrl));
snd_power_change_state(card, SNDRV_CTL_POWER_D0);
return 0;
......
......@@ -6,50 +6,59 @@
/*** main I/O area port indices ***/
/* (only 0x70 of 0x80 bytes saved/restored by Windows driver) */
#define AZF_IO_SIZE_CODEC 0x80
#define AZF_IO_SIZE_CODEC_PM 0x70
#define AZF_IO_SIZE_CTRL 0x80
#define AZF_IO_SIZE_CTRL_PM 0x70
/* the driver initialisation suggests a layout of 4 main areas:
* from 0x00 (playback), from 0x20 (recording) and from 0x40 (maybe MPU401??).
/* the driver initialisation suggests a layout of 4 areas
* 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,
* power management etc.???). */
/** playback area **/
#define IDX_IO_PLAY_FLAGS 0x00 /* PU:0x0000 */
#define AZF_IO_OFFS_CODEC_PLAYBACK 0x00
#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
* output change, just like 0x0002.
* 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
* both 0x0002 and 0x0004 set in playback setup. */
/* able to reactivate output after output muting due to 8/16bit
* 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 */
#define DMA_PLAY_SOMETHING2 0x0004 /* / bits */
#define DMA_RUN_SOMETHING2 0x0004 /* / bits */
#define SOMETHING_ALMOST_ALWAYS_SET 0x0008 /* ???; can be modified */
#define DMA_EPILOGUE_SOMETHING 0x0010
#define DMA_SOMETHING_ELSE 0x0020 /* ??? */
#define SOMETHING_UNMODIFIABLE 0xffc0 /* unused ? not modifiable */
#define IDX_IO_PLAY_IRQTYPE 0x02 /* PU:0x0001 */
#define SOMETHING_UNMODIFIABLE 0xffc0 /* unused? not modifiable */
#define IDX_IO_CODEC_IRQTYPE 0x02 /* PU:0x0001 */
/* 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)
* sometimes in this case it just writes 0xffff to globally ACK all IRQs
* settings written are not reflected when reading back, though.
* seems to be IRQ, too (frequently used: port |= 0x07 !), but who knows ? */
#define IRQ_PLAY_SOMETHING 0x0001 /* something & ACK */
#define IRQ_FINISHED_PLAYBUF_1 0x0002 /* 1st dmabuf finished & ACK */
#define IRQ_FINISHED_PLAYBUF_2 0x0004 /* 2nd dmabuf finished & ACK */
* seems to be IRQ, too (frequently used: port |= 0x07 !), but who knows? */
#define IRQ_SOMETHING 0x0001 /* something & ACK */
#define IRQ_FINISHED_DMABUF_1 0x0002 /* 1st 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_2 0x0010 /* / (checked together in loop) */
#define IRQMASK_UNMODIFIABLE 0xffe0 /* unused ? not modifiable */
#define IDX_IO_PLAY_DMA_START_1 0x04 /* start address of 1st DMA play area, PU:0x00000000 */
#define IDX_IO_PLAY_DMA_START_2 0x08 /* start address of 2nd DMA play area, PU:0x00000000 */
#define IDX_IO_PLAY_DMA_LEN_1 0x0c /* length of 1st DMA play area, PU:0x0000 */
#define IDX_IO_PLAY_DMA_LEN_2 0x0e /* length of 2nd DMA play area, PU:0x0000 */
#define IDX_IO_PLAY_DMA_CURRPOS 0x10 /* current DMA position, PU:0x00000000 */
#define IDX_IO_PLAY_DMA_CURROFS 0x14 /* offset within current DMA play area, PU:0x0000 */
#define IDX_IO_PLAY_SOUNDFORMAT 0x16 /* PU:0x0010 */
#define IRQMASK_UNMODIFIABLE 0xffe0 /* unused? not modifiable */
/* start address of 1st DMA transfer area, PU:0x00000000 */
#define IDX_IO_CODEC_DMA_START_1 0x04
/* start address of 2nd DMA transfer area, PU:0x00000000 */
#define IDX_IO_CODEC_DMA_START_2 0x08
/* both lengths of DMA transfer areas, PU:0x00000000
length1: offset 0x0c, length2: offset 0x0e */
#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 */
#define SOUNDFORMAT_FREQUENCY_MASK 0x000f
#define SOUNDFORMAT_XTAL1 0x00
......@@ -76,6 +85,7 @@
#define SOUNDFORMAT_FLAG_16BIT 0x0010
#define SOUNDFORMAT_FLAG_2CHANNELS 0x0020
/* define frequency helpers, for maximum value safety */
enum azf_freq_t {
#define AZF_FREQ(rate) AZF_FREQ_##rate = rate
......@@ -96,29 +106,6 @@ enum azf_freq_t {
#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?) **/
#define IDX_IO_TIMER_VALUE 0x60 /* found this timer area by pure luck :-) */
/* timer countdown value; triggers IRQ when timer is finished */
......@@ -133,17 +120,19 @@ enum azf_freq_t {
#define IDX_IO_IRQSTATUS 0x64
/* 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).
* Some OPL3 hardware (e.g. in LM4560) has some special timer hardware which
* can trigger an OPL3 timer IRQ, so maybe there's such a thing as well... */
* OPL3 hardware contains several timers which confusingly in most cases
* 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_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_MPU401 0x0010
#define IRQ_TIMER 0x0020 /* DirectX timer */
#define IRQ_UNKNOWN2 0x0040 /* probably unused, or possibly I2S port? */
#define IRQ_UNKNOWN3 0x0080 /* probably unused, or possibly I2S port? */
#define IRQ_UNKNOWN2 0x0040 /* probably unused, or possibly OPL3 timer? */
#define IRQ_UNKNOWN3 0x0080 /* probably unused, or possibly OPL3 timer? */
#define IDX_IO_66H 0x66 /* writing 0xffff returns 0x0000 */
/* this is set to e.g. 0x3ff or 0x300, and writable;
* maybe some buffer limit, but I couldn't find out more, PU:0x00ff: */
......@@ -272,6 +261,12 @@ enum {
* 11 --> 1/200: */
#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)
* I was unable to locate any configurability for a different address: */
#define GAME_HWCFG_LEGACY_ADDRESS_ENABLE 0x08
......@@ -281,6 +276,7 @@ enum {
#define AZF_IO_SIZE_MPU_PM 0x04
/*** OPL3 synth ***/
/* (only 0x06 of 0x08 bytes saved/restored by Windows driver) */
#define AZF_IO_SIZE_OPL3 0x08
#define AZF_IO_SIZE_OPL3_PM 0x06
/* hmm, given that a standard OPL3 has 4 registers only,
......@@ -340,4 +336,7 @@ enum {
#define SET_CHAN_LEFT 1
#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 */
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