blob: bb84bb68a776a84055fe1ceb2230d29a30463bc9 [file] [log] [blame]
Andreas Mohrb5dc20c2011-02-19 00:49:32 +01001/* azt3328.c - driver for Aztech AZF3328 based soundcards (e.g. PCI168).
2 * Copyright (C) 2002, 2005 - 2011 by Andreas Mohr <andi AT lisas.de>
Linus Torvalds1da177e2005-04-16 15:20:36 -07003 *
4 * Framework borrowed from Bart Hartgers's als4000.c.
5 * Driver developed on PCI168 AP(W) version (PCI rev. 10, subsystem ID 1801),
6 * found in a Fujitsu-Siemens PC ("Cordant", aluminum case).
7 * Other versions are:
8 * PCI168 A(W), sub ID 1800
9 * PCI168 A/AP, sub ID 8000
10 * Please give me feedback in case you try my driver with one of these!!
11 *
Andreas Mohrdfbf9512009-07-05 13:55:46 +020012 * Keywords: Windows XP Vista 168nt4-125.zip 168win95-125.zip PCI 168 download
13 * (XP/Vista do not support this card at all but every Linux distribution
14 * has very good support out of the box;
15 * just to make sure that the right people hit this and get to know that,
16 * despite the high level of Internet ignorance - as usual :-P -
Andreas Mohr78df6172009-07-12 22:17:54 +020017 * about very good support for this card - on Linux!)
Andreas Mohrdfbf9512009-07-05 13:55:46 +020018 *
Linus Torvalds1da177e2005-04-16 15:20:36 -070019 * GPL LICENSE
20 * This program is free software; you can redistribute it and/or modify
21 * it under the terms of the GNU General Public License as published by
22 * the Free Software Foundation; either version 2 of the License, or
23 * (at your option) any later version.
24 *
25 * This program is distributed in the hope that it will be useful,
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 * GNU General Public License for more details.
29
30 * You should have received a copy of the GNU General Public License
31 * along with this program; if not, write to the Free Software
32 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
33 *
34 * NOTES
35 * Since Aztech does not provide any chipset documentation,
36 * even on repeated request to various addresses,
37 * and the answer that was finally given was negative
38 * (and I was stupid enough to manage to get hold of a PCI168 soundcard
39 * in the first place >:-P}),
40 * I was forced to base this driver on reverse engineering
41 * (3 weeks' worth of evenings filled with driver work).
Andreas Mohre2f87262006-05-17 11:04:19 +020042 * (and no, I did NOT go the easy way: to pick up a SB PCI128 for 9 Euros)
Linus Torvalds1da177e2005-04-16 15:20:36 -070043 *
Andreas Mohr02330fb2008-05-16 12:18:29 +020044 * It is quite likely that the AZF3328 chip is the PCI cousin of the
45 * AZF3318 ("azt1020 pnp", "MM Pro 16") ISA chip, given very similar specs.
Linus Torvalds1da177e2005-04-16 15:20:36 -070046 *
Andreas Mohr02330fb2008-05-16 12:18:29 +020047 * The AZF3328 chip (note: AZF3328, *not* AZT3328, that's just the driver name
48 * for compatibility reasons) from Azfin (joint-venture of Aztech and Fincitec,
49 * Fincitec acquired by National Semiconductor in 2002, together with the
50 * Fincitec-related company ARSmikro) has the following features:
51 *
52 * - compatibility & compliance:
53 * - Microsoft PC 97 ("PC 97 Hardware Design Guide",
54 * http://www.microsoft.com/whdc/archive/pcguides.mspx)
55 * - Microsoft PC 98 Baseline Audio
56 * - MPU401 UART
57 * - Sound Blaster Emulation (DOS Box)
Linus Torvalds1da177e2005-04-16 15:20:36 -070058 * - builtin AC97 conformant codec (SNR over 80dB)
Andreas Mohr13769e32006-05-17 11:03:16 +020059 * Note that "conformant" != "compliant"!! this chip's mixer register layout
60 * *differs* from the standard AC97 layout:
61 * they chose to not implement the headphone register (which is not a
62 * problem since it's merely optional), yet when doing this, they committed
63 * the grave sin of letting other registers follow immediately instead of
64 * keeping a headphone dummy register, thereby shifting the mixer register
65 * addresses illegally. So far unfortunately it looks like the very flexible
66 * ALSA AC97 support is still not enough to easily compensate for such a
67 * grave layout violation despite all tweaks and quirks mechanisms it offers.
Andreas Mohrb5dc20c2011-02-19 00:49:32 +010068 * Well, not quite: now ac97 layer is much improved (bus-specific ops!),
69 * thus I was able to implement support - it's actually working quite well.
70 * An interesting item might be Aztech AMR 2800-W, since it's an AC97
71 * modem card which might reveal the Aztech-specific codec ID which
72 * we might want to pretend, too. Dito PCI168's brother, PCI368,
73 * where the advertising datasheet says it's AC97-based and has a
74 * Digital Enhanced Game Port.
Andreas Mohr02330fb2008-05-16 12:18:29 +020075 * - builtin genuine OPL3 - verified to work fine, 20080506
Linus Torvalds1da177e2005-04-16 15:20:36 -070076 * - full duplex 16bit playback/record at independent sampling rate
Andreas Mohr02330fb2008-05-16 12:18:29 +020077 * - MPU401 (+ legacy address support, claimed by one official spec sheet)
78 * FIXME: how to enable legacy addr??
Linus Torvalds1da177e2005-04-16 15:20:36 -070079 * - game port (legacy address support)
Andreas Mohre24a1212007-03-26 12:49:45 +020080 * - builtin DirectInput support, helps reduce CPU overhead (interrupt-driven
Andreas Mohr02330fb2008-05-16 12:18:29 +020081 * features supported). - See common term "Digital Enhanced Game Port"...
82 * (probably DirectInput 3.0 spec - confirm)
83 * - builtin 3D enhancement (said to be YAMAHA Ymersion)
Linus Torvalds1da177e2005-04-16 15:20:36 -070084 * - built-in General DirectX timer having a 20 bits counter
Andreas Mohrd91c64c2005-10-25 11:17:45 +020085 * with 1us resolution (see below!)
Andreas Mohr02330fb2008-05-16 12:18:29 +020086 * - I2S serial output port for external DAC
Andreas Mohrdfbf9512009-07-05 13:55:46 +020087 * [FIXME: 3.3V or 5V level? maximum rate is 66.2kHz right?]
Linus Torvalds1da177e2005-04-16 15:20:36 -070088 * - supports 33MHz PCI spec 2.1, PCI power management 1.0, compliant with ACPI
89 * - supports hardware volume control
90 * - single chip low cost solution (128 pin QFP)
Andreas Mohrdfbf9512009-07-05 13:55:46 +020091 * - supports programmable Sub-vendor and Sub-system ID [24C02 SEEPROM chip]
Linus Torvalds1da177e2005-04-16 15:20:36 -070092 * required for Microsoft's logo compliance (FIXME: where?)
Andreas Mohr02330fb2008-05-16 12:18:29 +020093 * At least the Trident 4D Wave DX has one bit somewhere
94 * to enable writes to PCI subsystem VID registers, that should be it.
95 * This might easily be in extended PCI reg space, since PCI168 also has
96 * some custom data starting at 0x80. What kind of config settings
97 * are located in our extended PCI space anyway??
Linus Torvalds1da177e2005-04-16 15:20:36 -070098 * - PCI168 AP(W) card: power amplifier with 4 Watts/channel at 4 Ohms
Andreas Mohrdfbf9512009-07-05 13:55:46 +020099 * [TDA1517P chip]
Linus Torvalds1da177e2005-04-16 15:20:36 -0700100 *
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200101 * Note that this driver now is actually *better* than the Windows driver,
102 * since it additionally supports the card's 1MHz DirectX timer - just try
103 * the following snd-seq module parameters etc.:
104 * - options snd-seq seq_default_timer_class=2 seq_default_timer_sclass=0
105 * seq_default_timer_card=0 seq_client_load=1 seq_default_timer_device=0
106 * seq_default_timer_subdevice=0 seq_default_timer_resolution=1000000
107 * - "timidity -iAv -B2,8 -Os -EFreverb=0"
108 * - "pmidi -p 128:0 jazz.mid"
109 *
Andreas Mohr02330fb2008-05-16 12:18:29 +0200110 * OPL3 hardware playback testing, try something like:
111 * cat /proc/asound/hwdep
112 * and
113 * aconnect -o
114 * Then use
115 * sbiload -Dhw:x,y --opl3 /usr/share/sounds/opl3/std.o3 ......./drums.o3
116 * where x,y is the xx-yy number as given in hwdep.
117 * Then try
118 * pmidi -p a:b jazz.mid
119 * where a:b is the client number plus 0 usually, as given by aconnect above.
120 * Oh, and make sure to unmute the FM mixer control (doh!)
121 * NOTE: power use during OPL3 playback is _VERY_ high (70W --> 90W!)
122 * despite no CPU activity, possibly due to hindering ACPI idling somehow.
123 * Shouldn't be a problem of the AZF3328 chip itself, I'd hope.
124 * Higher PCM / FM mixer levels seem to conflict (causes crackling),
125 * at least sometimes. Maybe even use with hardware sequencer timer above :)
126 * adplay/adplug-utils might soon offer hardware-based OPL3 playback, too.
127 *
Linus Torvalds1da177e2005-04-16 15:20:36 -0700128 * Certain PCI versions of this card are susceptible to DMA traffic underruns
129 * in some systems (resulting in sound crackling/clicking/popping),
130 * probably because they don't have a DMA FIFO buffer or so.
131 * Overview (PCI ID/PCI subID/PCI rev.):
132 * - no DMA crackling on SiS735: 0x50DC/0x1801/16
133 * - unknown performance: 0x50DC/0x1801/10
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200134 * (well, it's not bad on an Athlon 1800 with now very optimized IRQ handler)
135 *
Linus Torvalds1da177e2005-04-16 15:20:36 -0700136 * Crackling happens with VIA chipsets or, in my case, an SiS735, which is
137 * supposed to be very fast and supposed to get rid of crackling much
138 * better than a VIA, yet ironically I still get crackling, like many other
139 * people with the same chipset.
140 * Possible remedies:
Andreas Mohr02330fb2008-05-16 12:18:29 +0200141 * - use speaker (amplifier) output instead of headphone output
142 * (in case crackling is due to overloaded output clipping)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700143 * - plug card into a different PCI slot, preferrably one that isn't shared
144 * too much (this helps a lot, but not completely!)
145 * - get rid of PCI VGA card, use AGP instead
146 * - upgrade or downgrade BIOS
147 * - fiddle with PCI latency settings (setpci -v -s BUSID latency_timer=XX)
148 * Not too helpful.
149 * - Disable ACPI/power management/"Auto Detect RAM/PCI Clk" in BIOS
Andreas Mohr02330fb2008-05-16 12:18:29 +0200150 *
Linus Torvalds1da177e2005-04-16 15:20:36 -0700151 * BUGS
Andreas Mohr02330fb2008-05-16 12:18:29 +0200152 * - full-duplex might *still* be problematic, however a recent test was fine
Andreas Mohre24a1212007-03-26 12:49:45 +0200153 * - (non-bug) "Bass/Treble or 3D settings don't work" - they do get evaluated
154 * if you set PCM output switch to "pre 3D" instead of "post 3D".
155 * If this can't be set, then get a mixer application that Isn't Stupid (tm)
156 * (e.g. kmix, gamix) - unfortunately several are!!
Andreas Mohr02330fb2008-05-16 12:18:29 +0200157 * - locking is not entirely clean, especially the audio stream activity
158 * ints --> may be racy
159 * - an _unconnected_ secondary joystick at the gameport will be reported
160 * to be "active" (floating values, not precisely -1) due to the way we need
161 * to read the Digital Enhanced Game Port. Not sure whether it is fixable.
162 *
Linus Torvalds1da177e2005-04-16 15:20:36 -0700163 * TODO
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200164 * - use PCI_VDEVICE
165 * - verify driver status on x86_64
166 * - test multi-card driver operation
167 * - (ab)use 1MHz DirectX timer as kernel clocksource
Linus Torvalds1da177e2005-04-16 15:20:36 -0700168 * - test MPU401 MIDI playback etc.
Andreas Mohr02330fb2008-05-16 12:18:29 +0200169 * - add more power micro-management (disable various units of the card
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200170 * as long as they're unused, to improve audio quality and save power).
171 * However this requires more I/O ports which I haven't figured out yet
172 * and which thus might not even exist...
Andreas Mohrca54bde2006-05-17 11:02:24 +0200173 * The standard suspend/resume functionality could probably make use of
174 * some improvement, too...
Linus Torvalds1da177e2005-04-16 15:20:36 -0700175 * - figure out what all unknown port bits are responsible for
Andreas Mohr13769e32006-05-17 11:03:16 +0200176 * - figure out some cleverly evil scheme to possibly make ALSA AC97 code
177 * fully accept our quite incompatible ""AC97"" mixer and thus save some
178 * code (but I'm not too optimistic that doing this is possible at all)
Andreas Mohr02330fb2008-05-16 12:18:29 +0200179 * - use MMIO (memory-mapped I/O)? Slightly faster access, e.g. for gameport.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700180 */
181
Linus Torvalds1da177e2005-04-16 15:20:36 -0700182#include <asm/io.h>
183#include <linux/init.h>
Andreas Mohr689c6912010-12-27 21:17:35 +0100184#include <linux/bug.h> /* WARN_ONCE */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700185#include <linux/pci.h>
186#include <linux/delay.h>
187#include <linux/slab.h>
188#include <linux/gameport.h>
189#include <linux/moduleparam.h>
Matthias Gehre910638a2006-03-28 01:56:48 -0800190#include <linux/dma-mapping.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -0700191#include <sound/core.h>
192#include <sound/control.h>
193#include <sound/pcm.h>
194#include <sound/rawmidi.h>
195#include <sound/mpu401.h>
196#include <sound/opl3.h>
197#include <sound/initval.h>
Andreas Mohrb5dc20c2011-02-19 00:49:32 +0100198/*
199 * Config switch, to use ALSA's AC97 layer instead of old custom mixer crap.
200 * If the AC97 compatibility parts we needed to implement locally turn out
201 * to work nicely, then remove the old implementation eventually.
202 */
203#define AZF_USE_AC97_LAYER 1
204
205#ifdef AZF_USE_AC97_LAYER
206#include <sound/ac97_codec.h>
207#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700208#include "azt3328.h"
209
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200210MODULE_AUTHOR("Andreas Mohr <andi AT lisas.de>");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700211MODULE_DESCRIPTION("Aztech AZF3328 (PCI168)");
212MODULE_LICENSE("GPL");
213MODULE_SUPPORTED_DEVICE("{{Aztech,AZF3328}}");
214
215#if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE))
Andreas Mohr02330fb2008-05-16 12:18:29 +0200216#define SUPPORT_GAMEPORT 1
Linus Torvalds1da177e2005-04-16 15:20:36 -0700217#endif
218
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200219/* === Debug settings ===
220 Further diagnostic functionality than the settings below
Andreas Mohradf59312010-12-27 21:16:43 +0100221 does not need to be provided, since one can easily write a POSIX shell script
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200222 to dump the card's I/O ports (those listed in lspci -v -v):
Andreas Mohradf59312010-12-27 21:16:43 +0100223 dump()
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200224 {
225 local descr=$1; local addr=$2; local count=$3
226
227 echo "${descr}: ${count} @ ${addr}:"
Andreas Mohradf59312010-12-27 21:16:43 +0100228 dd if=/dev/port skip=`printf %d ${addr}` count=${count} bs=1 \
229 2>/dev/null| hexdump -C
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200230 }
231 and then use something like
232 "dump joy200 0x200 8", "dump mpu388 0x388 4", "dump joy 0xb400 8",
233 "dump codec00 0xa800 32", "dump mixer 0xb800 64", "dump synth 0xbc00 8",
234 possibly within a "while true; do ... sleep 1; done" loop.
235 Tweaking ports could be done using
236 VALSTRING="`printf "%02x" $value`"
Andreas Mohradf59312010-12-27 21:16:43 +0100237 printf "\x""$VALSTRING"|dd of=/dev/port seek=`printf %d ${addr}` bs=1 \
238 2>/dev/null
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200239*/
240
Linus Torvalds1da177e2005-04-16 15:20:36 -0700241#define DEBUG_MISC 0
242#define DEBUG_CALLS 0
243#define DEBUG_MIXER 0
Andreas Mohr78df6172009-07-12 22:17:54 +0200244#define DEBUG_CODEC 0
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200245#define DEBUG_TIMER 0
Andreas Mohr02330fb2008-05-16 12:18:29 +0200246#define DEBUG_GAME 0
Andreas Mohr78df6172009-07-12 22:17:54 +0200247#define DEBUG_PM 0
Linus Torvalds1da177e2005-04-16 15:20:36 -0700248#define MIXER_TESTING 0
249
250#if DEBUG_MISC
Andreas Mohr78df6172009-07-12 22:17:54 +0200251#define snd_azf3328_dbgmisc(format, args...) printk(KERN_DEBUG format, ##args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700252#else
253#define snd_azf3328_dbgmisc(format, args...)
Andreas Mohr02330fb2008-05-16 12:18:29 +0200254#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700255
256#if DEBUG_CALLS
257#define snd_azf3328_dbgcalls(format, args...) printk(format, ##args)
Andreas Mohr78df6172009-07-12 22:17:54 +0200258#define snd_azf3328_dbgcallenter() printk(KERN_DEBUG "--> %s\n", __func__)
259#define snd_azf3328_dbgcallleave() printk(KERN_DEBUG "<-- %s\n", __func__)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700260#else
261#define snd_azf3328_dbgcalls(format, args...)
262#define snd_azf3328_dbgcallenter()
263#define snd_azf3328_dbgcallleave()
Andreas Mohr02330fb2008-05-16 12:18:29 +0200264#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700265
266#if DEBUG_MIXER
Takashi Iwaiee419652009-02-05 16:11:31 +0100267#define snd_azf3328_dbgmixer(format, args...) printk(KERN_DEBUG format, ##args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700268#else
269#define snd_azf3328_dbgmixer(format, args...)
Andreas Mohr02330fb2008-05-16 12:18:29 +0200270#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700271
Andreas Mohr78df6172009-07-12 22:17:54 +0200272#if DEBUG_CODEC
273#define snd_azf3328_dbgcodec(format, args...) printk(KERN_DEBUG format, ##args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700274#else
Andreas Mohr78df6172009-07-12 22:17:54 +0200275#define snd_azf3328_dbgcodec(format, args...)
Andreas Mohr02330fb2008-05-16 12:18:29 +0200276#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700277
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200278#if DEBUG_MISC
Takashi Iwaiee419652009-02-05 16:11:31 +0100279#define snd_azf3328_dbgtimer(format, args...) printk(KERN_DEBUG format, ##args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700280#else
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200281#define snd_azf3328_dbgtimer(format, args...)
Andreas Mohr02330fb2008-05-16 12:18:29 +0200282#endif
283
284#if DEBUG_GAME
Takashi Iwaiee419652009-02-05 16:11:31 +0100285#define snd_azf3328_dbggame(format, args...) printk(KERN_DEBUG format, ##args)
Andreas Mohr02330fb2008-05-16 12:18:29 +0200286#else
287#define snd_azf3328_dbggame(format, args...)
288#endif
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200289
Andreas Mohr78df6172009-07-12 22:17:54 +0200290#if DEBUG_PM
291#define snd_azf3328_dbgpm(format, args...) printk(KERN_DEBUG format, ##args)
292#else
293#define snd_azf3328_dbgpm(format, args...)
294#endif
295
Linus Torvalds1da177e2005-04-16 15:20:36 -0700296static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
297module_param_array(index, int, NULL, 0444);
298MODULE_PARM_DESC(index, "Index value for AZF3328 soundcard.");
299
300static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
301module_param_array(id, charp, NULL, 0444);
302MODULE_PARM_DESC(id, "ID string for AZF3328 soundcard.");
303
304static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
305module_param_array(enable, bool, NULL, 0444);
306MODULE_PARM_DESC(enable, "Enable AZF3328 soundcard.");
307
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200308static int seqtimer_scaling = 128;
309module_param(seqtimer_scaling, int, 0444);
310MODULE_PARM_DESC(seqtimer_scaling, "Set 1024000Hz sequencer timer scale factor (lockup danger!). Default 128.");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700311
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200312enum snd_azf3328_codec_type {
Andreas Mohradf59312010-12-27 21:16:43 +0100313 /* warning: fixed indices (also used for bitmask checks!) */
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200314 AZF_CODEC_PLAYBACK = 0,
315 AZF_CODEC_CAPTURE = 1,
316 AZF_CODEC_I2S_OUT = 2,
Andreas Mohr02330fb2008-05-16 12:18:29 +0200317};
318
Andreas Mohrda237f32010-12-27 21:17:26 +0100319struct snd_azf3328_codec_data {
320 unsigned long io_base; /* keep first! (avoid offset calc) */
321 unsigned int dma_base; /* helper to avoid an indirection in hotpath */
322 spinlock_t *lock; /* TODO: convert to our own per-codec lock member */
323 struct snd_pcm_substream *substream;
324 bool running;
325 enum snd_azf3328_codec_type type;
326 const char *name;
327};
328
Takashi Iwai95de7762005-11-17 15:02:42 +0100329struct snd_azf3328 {
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200330 /* often-used fields towards beginning, then grouped */
Andreas Mohr02330fb2008-05-16 12:18:29 +0200331
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200332 unsigned long ctrl_io; /* usually 0xb000, size 128 */
Andreas Mohr02330fb2008-05-16 12:18:29 +0200333 unsigned long game_io; /* usually 0xb400, size 8 */
334 unsigned long mpu_io; /* usually 0xb800, size 4 */
335 unsigned long opl3_io; /* usually 0xbc00, size 8 */
336 unsigned long mixer_io; /* usually 0xc000, size 64 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700337
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200338 spinlock_t reg_lock;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700339
Takashi Iwai95de7762005-11-17 15:02:42 +0100340 struct snd_timer *timer;
Andreas Mohr02330fb2008-05-16 12:18:29 +0200341
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200342 struct snd_pcm *pcm[3];
343
344 /* playback, recording and I2S out codecs */
345 struct snd_azf3328_codec_data codecs[3];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700346
Andreas Mohrb5dc20c2011-02-19 00:49:32 +0100347#ifdef AZF_USE_AC97_LAYER
348 struct snd_ac97 *ac97;
349#endif
350
Takashi Iwai95de7762005-11-17 15:02:42 +0100351 struct snd_card *card;
352 struct snd_rawmidi *rmidi;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700353
Andreas Mohr02330fb2008-05-16 12:18:29 +0200354#ifdef SUPPORT_GAMEPORT
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200355 struct gameport *gameport;
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200356 u16 axes[4];
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200357#endif
358
359 struct pci_dev *pci;
360 int irq;
Andreas Mohrca54bde2006-05-17 11:02:24 +0200361
Andreas Mohr627d3e72008-06-23 11:50:47 +0200362 /* register 0x6a is write-only, thus need to remember setting.
363 * If we need to add more registers here, then we might try to fold this
364 * into some transparent combined shadow register handling with
365 * CONFIG_PM register storage below, but that's slightly difficult. */
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200366 u16 shadow_reg_ctrl_6AH;
Andreas Mohr627d3e72008-06-23 11:50:47 +0200367
Andreas Mohrca54bde2006-05-17 11:02:24 +0200368#ifdef CONFIG_PM
369 /* register value containers for power management
Andreas Mohr78df6172009-07-12 22:17:54 +0200370 * Note: not always full I/O range preserved (similar to Win driver!) */
371 u32 saved_regs_ctrl[AZF_ALIGN(AZF_IO_SIZE_CTRL_PM) / 4];
372 u32 saved_regs_game[AZF_ALIGN(AZF_IO_SIZE_GAME_PM) / 4];
373 u32 saved_regs_mpu[AZF_ALIGN(AZF_IO_SIZE_MPU_PM) / 4];
374 u32 saved_regs_opl3[AZF_ALIGN(AZF_IO_SIZE_OPL3_PM) / 4];
375 u32 saved_regs_mixer[AZF_ALIGN(AZF_IO_SIZE_MIXER_PM) / 4];
Andreas Mohrca54bde2006-05-17 11:02:24 +0200376#endif
Takashi Iwai95de7762005-11-17 15:02:42 +0100377};
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200378
Alexey Dobriyancebe41d2010-02-06 00:21:03 +0200379static DEFINE_PCI_DEVICE_TABLE(snd_azf3328_ids) = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700380 { 0x122D, 0x50DC, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* PCI168/3328 */
381 { 0x122D, 0x80DA, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* 3328 */
382 { 0, }
383};
384
385MODULE_DEVICE_TABLE(pci, snd_azf3328_ids);
386
Andreas Mohr02330fb2008-05-16 12:18:29 +0200387
388static int
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200389snd_azf3328_io_reg_setb(unsigned reg, u8 mask, bool do_set)
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200390{
Andreas Mohradf59312010-12-27 21:16:43 +0100391 /* Well, strictly spoken, the inb/outb sequence isn't atomic
392 and would need locking. However we currently don't care
393 since it potentially complicates matters. */
Andreas Mohr02330fb2008-05-16 12:18:29 +0200394 u8 prev = inb(reg), new;
395
396 new = (do_set) ? (prev|mask) : (prev & ~mask);
397 /* we need to always write the new value no matter whether it differs
398 * or not, since some register bits don't indicate their setting */
399 outb(new, reg);
400 if (new != prev)
401 return 1;
402
403 return 0;
404}
405
Andreas Mohr02330fb2008-05-16 12:18:29 +0200406static inline void
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200407snd_azf3328_codec_outb(const struct snd_azf3328_codec_data *codec,
408 unsigned reg,
409 u8 value
410)
Andreas Mohr02330fb2008-05-16 12:18:29 +0200411{
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200412 outb(value, codec->io_base + reg);
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200413}
414
415static inline u8
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200416snd_azf3328_codec_inb(const struct snd_azf3328_codec_data *codec, unsigned reg)
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200417{
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200418 return inb(codec->io_base + reg);
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200419}
420
421static inline void
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200422snd_azf3328_codec_outw(const struct snd_azf3328_codec_data *codec,
423 unsigned reg,
424 u16 value
425)
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200426{
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200427 outw(value, codec->io_base + reg);
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200428}
429
430static inline u16
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200431snd_azf3328_codec_inw(const struct snd_azf3328_codec_data *codec, unsigned reg)
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200432{
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200433 return inw(codec->io_base + reg);
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200434}
435
436static inline void
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200437snd_azf3328_codec_outl(const struct snd_azf3328_codec_data *codec,
438 unsigned reg,
439 u32 value
440)
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200441{
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200442 outl(value, codec->io_base + reg);
Andreas Mohr02330fb2008-05-16 12:18:29 +0200443}
444
Andreas Mohr689c6912010-12-27 21:17:35 +0100445static inline void
446snd_azf3328_codec_outl_multi(const struct snd_azf3328_codec_data *codec,
447 unsigned reg, const void *buffer, int count
448)
449{
450 unsigned long addr = codec->io_base + reg;
451 if (count) {
452 const u32 *buf = buffer;
453 do {
454 outl(*buf++, addr);
455 addr += 4;
456 } while (--count);
457 }
458}
459
Andreas Mohr02330fb2008-05-16 12:18:29 +0200460static inline u32
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200461snd_azf3328_codec_inl(const struct snd_azf3328_codec_data *codec, unsigned reg)
Andreas Mohr02330fb2008-05-16 12:18:29 +0200462{
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200463 return inl(codec->io_base + reg);
464}
465
466static inline void
467snd_azf3328_ctrl_outb(const struct snd_azf3328 *chip, unsigned reg, u8 value)
468{
469 outb(value, chip->ctrl_io + reg);
470}
471
472static inline u8
473snd_azf3328_ctrl_inb(const struct snd_azf3328 *chip, unsigned reg)
474{
475 return inb(chip->ctrl_io + reg);
476}
477
478static inline void
479snd_azf3328_ctrl_outw(const struct snd_azf3328 *chip, unsigned reg, u16 value)
480{
481 outw(value, chip->ctrl_io + reg);
482}
483
484static inline void
485snd_azf3328_ctrl_outl(const struct snd_azf3328 *chip, unsigned reg, u32 value)
486{
487 outl(value, chip->ctrl_io + reg);
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200488}
489
490static inline void
Andreas Mohr02330fb2008-05-16 12:18:29 +0200491snd_azf3328_game_outb(const struct snd_azf3328 *chip, unsigned reg, u8 value)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700492{
Andreas Mohr02330fb2008-05-16 12:18:29 +0200493 outb(value, chip->game_io + reg);
494}
495
496static inline void
497snd_azf3328_game_outw(const struct snd_azf3328 *chip, unsigned reg, u16 value)
498{
499 outw(value, chip->game_io + reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700500}
501
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200502static inline u8
Andreas Mohr02330fb2008-05-16 12:18:29 +0200503snd_azf3328_game_inb(const struct snd_azf3328 *chip, unsigned reg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700504{
Andreas Mohr02330fb2008-05-16 12:18:29 +0200505 return inb(chip->game_io + reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700506}
507
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200508static inline u16
Andreas Mohr02330fb2008-05-16 12:18:29 +0200509snd_azf3328_game_inw(const struct snd_azf3328 *chip, unsigned reg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700510{
Andreas Mohr02330fb2008-05-16 12:18:29 +0200511 return inw(chip->game_io + reg);
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200512}
513
Andreas Mohr02330fb2008-05-16 12:18:29 +0200514static inline void
515snd_azf3328_mixer_outw(const struct snd_azf3328 *chip, unsigned reg, u16 value)
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200516{
Andreas Mohr02330fb2008-05-16 12:18:29 +0200517 outw(value, chip->mixer_io + reg);
518}
519
520static inline u16
521snd_azf3328_mixer_inw(const struct snd_azf3328 *chip, unsigned reg)
522{
523 return inw(chip->mixer_io + reg);
524}
525
526#define AZF_MUTE_BIT 0x80
527
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200528static bool
Andreas Mohrb5dc20c2011-02-19 00:49:32 +0100529snd_azf3328_mixer_mute_control(const struct snd_azf3328 *chip,
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200530 unsigned reg, bool do_mute
Andreas Mohr02330fb2008-05-16 12:18:29 +0200531)
532{
533 unsigned long portbase = chip->mixer_io + reg + 1;
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200534 bool updated;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700535
536 /* the mute bit is on the *second* (i.e. right) register of a
537 * left/right channel setting */
Andreas Mohr02330fb2008-05-16 12:18:29 +0200538 updated = snd_azf3328_io_reg_setb(portbase, AZF_MUTE_BIT, do_mute);
539
540 /* indicate whether it was muted before */
541 return (do_mute) ? !updated : updated;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700542}
543
Andreas Mohrb5dc20c2011-02-19 00:49:32 +0100544static inline bool
545snd_azf3328_mixer_mute_control_master(const struct snd_azf3328 *chip,
546 bool do_mute
547)
548{
549 return snd_azf3328_mixer_mute_control(
550 chip,
551 IDX_MIXER_PLAY_MASTER,
552 do_mute
553 );
554}
555
556static inline bool
557snd_azf3328_mixer_mute_control_pcm(const struct snd_azf3328 *chip,
558 bool do_mute
559)
560{
561 return snd_azf3328_mixer_mute_control(
562 chip,
563 IDX_MIXER_WAVEOUT,
564 do_mute
565 );
566}
567
568static inline void
569snd_azf3328_mixer_reset(const struct snd_azf3328 *chip)
570{
571 /* reset (close) mixer:
572 * first mute master volume, then reset
573 */
574 snd_azf3328_mixer_mute_control_master(chip, 1);
575 snd_azf3328_mixer_outw(chip, IDX_MIXER_RESET, 0x0000);
576}
577
578#ifdef AZF_USE_AC97_LAYER
579
580static inline void
581snd_azf3328_mixer_ac97_map_unsupported(unsigned short reg, const char *mode)
582{
583 /* need to add some more or less clever emulation? */
584 printk(KERN_WARNING
585 "azt3328: missing %s emulation for AC97 register 0x%02x!\n",
586 mode, reg);
587}
588
589/*
590 * Need to have _special_ AC97 mixer hardware register index mapper,
591 * to compensate for the issue of a rather AC97-incompatible hardware layout.
592 */
593#define AZF_REG_MASK 0x3f
594#define AZF_AC97_REG_UNSUPPORTED 0x8000
595#define AZF_AC97_REG_REAL_IO_READ 0x4000
596#define AZF_AC97_REG_REAL_IO_WRITE 0x2000
597#define AZF_AC97_REG_REAL_IO_RW \
598 (AZF_AC97_REG_REAL_IO_READ | AZF_AC97_REG_REAL_IO_WRITE)
599#define AZF_AC97_REG_EMU_IO_READ 0x0400
600#define AZF_AC97_REG_EMU_IO_WRITE 0x0200
601#define AZF_AC97_REG_EMU_IO_RW \
602 (AZF_AC97_REG_EMU_IO_READ | AZF_AC97_REG_EMU_IO_WRITE)
603static unsigned short
604snd_azf3328_mixer_ac97_map_reg_idx(unsigned short reg)
605{
606 static const struct {
607 unsigned short azf_reg;
608 } azf_reg_mapper[] = {
609 /* Especially when taking into consideration
610 * mono/stereo-based sequence of azf vs. AC97 control series,
611 * it's quite obvious that azf simply got rid
612 * of the AC97_HEADPHONE control at its intended offset,
613 * thus shifted _all_ controls by one,
614 * and _then_ simply added it as an FMSYNTH control at the end,
615 * to make up for the offset.
616 * This means we'll have to translate indices here as
617 * needed and then do some tiny AC97 patch action
618 * (snd_ac97_rename_vol_ctl() etc.) - that's it.
619 */
620 { /* AC97_RESET */ IDX_MIXER_RESET
621 | AZF_AC97_REG_REAL_IO_WRITE
622 | AZF_AC97_REG_EMU_IO_READ },
623 { /* AC97_MASTER */ IDX_MIXER_PLAY_MASTER },
624 /* note large shift: AC97_HEADPHONE to IDX_MIXER_FMSYNTH! */
625 { /* AC97_HEADPHONE */ IDX_MIXER_FMSYNTH },
626 { /* AC97_MASTER_MONO */ IDX_MIXER_MODEMOUT },
627 { /* AC97_MASTER_TONE */ IDX_MIXER_BASSTREBLE },
628 { /* AC97_PC_BEEP */ IDX_MIXER_PCBEEP },
629 { /* AC97_PHONE */ IDX_MIXER_MODEMIN },
630 { /* AC97_MIC */ IDX_MIXER_MIC },
631 { /* AC97_LINE */ IDX_MIXER_LINEIN },
632 { /* AC97_CD */ IDX_MIXER_CDAUDIO },
633 { /* AC97_VIDEO */ IDX_MIXER_VIDEO },
634 { /* AC97_AUX */ IDX_MIXER_AUX },
635 { /* AC97_PCM */ IDX_MIXER_WAVEOUT },
636 { /* AC97_REC_SEL */ IDX_MIXER_REC_SELECT },
637 { /* AC97_REC_GAIN */ IDX_MIXER_REC_VOLUME },
638 { /* AC97_REC_GAIN_MIC */ AZF_AC97_REG_EMU_IO_RW },
639 { /* AC97_GENERAL_PURPOSE */ IDX_MIXER_ADVCTL2 },
640 { /* AC97_3D_CONTROL */ IDX_MIXER_ADVCTL1 },
641 };
642
643 unsigned short reg_azf = AZF_AC97_REG_UNSUPPORTED;
644
645 /* azf3328 supports the low-numbered and low-spec:ed range
646 of AC97 regs only */
647 if (reg <= AC97_3D_CONTROL) {
648 unsigned short reg_idx = reg / 2;
649 reg_azf = azf_reg_mapper[reg_idx].azf_reg;
650 /* a translation-only entry means it's real read/write: */
651 if (!(reg_azf & ~AZF_REG_MASK))
652 reg_azf |= AZF_AC97_REG_REAL_IO_RW;
653 } else {
654 switch (reg) {
655 case AC97_POWERDOWN:
656 reg_azf = AZF_AC97_REG_EMU_IO_RW;
657 break;
658 case AC97_EXTENDED_ID:
659 reg_azf = AZF_AC97_REG_EMU_IO_READ;
660 break;
661 case AC97_EXTENDED_STATUS:
662 /* I don't know what the h*ll AC97 layer
663 * would consult this _extended_ register for
664 * given a base-AC97-advertised card,
665 * but let's just emulate it anyway :-P
666 */
667 reg_azf = AZF_AC97_REG_EMU_IO_RW;
668 break;
669 case AC97_VENDOR_ID1:
670 case AC97_VENDOR_ID2:
671 reg_azf = AZF_AC97_REG_EMU_IO_READ;
672 break;
673 }
674 }
675 return reg_azf;
676}
677
678static const unsigned short
679azf_emulated_ac97_caps =
680 AC97_BC_DEDICATED_MIC |
681 AC97_BC_BASS_TREBLE |
682 /* Headphone is an FM Synth control here */
683 AC97_BC_HEADPHONE |
684 /* no AC97_BC_LOUDNESS! */
685 /* mask 0x7c00 is
686 vendor-specific 3D enhancement
687 vendor indicator.
688 Since there actually _is_ an
689 entry for Aztech Labs
690 (13), make damn sure
691 to indicate it. */
692 (13 << 10);
693
694static const unsigned short
695azf_emulated_ac97_powerdown =
696 /* pretend everything to be active */
697 AC97_PD_ADC_STATUS |
698 AC97_PD_DAC_STATUS |
699 AC97_PD_MIXER_STATUS |
700 AC97_PD_VREF_STATUS;
701
702/*
703 * Emulated, _inofficial_ vendor ID
704 * (there might be some devices such as the MR 2800-W
705 * which could reveal the real Aztech AC97 ID).
706 * We choose to use "AZT" prefix, and then use 1 to indicate PCI168
707 * (better don't use 0x68 since there's a PCI368 as well).
708 */
709static const unsigned int
710azf_emulated_ac97_vendor_id = 0x415a5401;
711
712static unsigned short
713snd_azf3328_mixer_ac97_read(struct snd_ac97 *ac97, unsigned short reg_ac97)
714{
715 const struct snd_azf3328 *chip = ac97->private_data;
716 unsigned short reg_azf = snd_azf3328_mixer_ac97_map_reg_idx(reg_ac97);
717 unsigned short reg_val = 0;
718 bool unsupported = 0;
719
720 snd_azf3328_dbgmixer(
721 "snd_azf3328_mixer_ac97_read reg_ac97 %u\n",
722 reg_ac97
723 );
724 if (reg_azf & AZF_AC97_REG_UNSUPPORTED)
725 unsupported = 1;
726 else {
727 if (reg_azf & AZF_AC97_REG_REAL_IO_READ)
728 reg_val = snd_azf3328_mixer_inw(chip,
729 reg_azf & AZF_REG_MASK);
730 else {
731 /*
732 * Proceed with dummy I/O read,
733 * to ensure compatible timing where this may matter.
734 * (ALSA AC97 layer usually doesn't call I/O functions
735 * due to intelligent I/O caching anyway)
736 * Choose a mixer register that's thoroughly unrelated
737 * to common audio (try to minimize distortion).
738 */
739 snd_azf3328_mixer_inw(chip, IDX_MIXER_SOMETHING30H);
740 }
741
742 if (reg_azf & AZF_AC97_REG_EMU_IO_READ) {
743 switch (reg_ac97) {
744 case AC97_RESET:
745 reg_val |= azf_emulated_ac97_caps;
746 break;
747 case AC97_POWERDOWN:
748 reg_val |= azf_emulated_ac97_powerdown;
749 break;
750 case AC97_EXTENDED_ID:
751 case AC97_EXTENDED_STATUS:
752 /* AFAICS we simply can't support anything: */
753 reg_val |= 0;
754 break;
755 case AC97_VENDOR_ID1:
756 reg_val = azf_emulated_ac97_vendor_id >> 16;
757 break;
758 case AC97_VENDOR_ID2:
759 reg_val = azf_emulated_ac97_vendor_id & 0xffff;
760 break;
761 default:
762 unsupported = 1;
763 break;
764 }
765 }
766 }
767 if (unsupported)
768 snd_azf3328_mixer_ac97_map_unsupported(reg_ac97, "read");
769
770 return reg_val;
771}
772
773static void
774snd_azf3328_mixer_ac97_write(struct snd_ac97 *ac97,
775 unsigned short reg_ac97, unsigned short val)
776{
777 const struct snd_azf3328 *chip = ac97->private_data;
778 unsigned short reg_azf = snd_azf3328_mixer_ac97_map_reg_idx(reg_ac97);
779 bool unsupported = 0;
780
781 snd_azf3328_dbgmixer(
782 "snd_azf3328_mixer_ac97_write reg_ac97 %u val %u\n",
783 reg_ac97, val
784 );
785 if (reg_azf & AZF_AC97_REG_UNSUPPORTED)
786 unsupported = 1;
787 else {
788 if (reg_azf & AZF_AC97_REG_REAL_IO_WRITE)
789 snd_azf3328_mixer_outw(
790 chip,
791 reg_azf & AZF_REG_MASK,
792 val
793 );
794 else
795 if (reg_azf & AZF_AC97_REG_EMU_IO_WRITE) {
796 switch (reg_ac97) {
797 case AC97_REC_GAIN_MIC:
798 case AC97_POWERDOWN:
799 case AC97_EXTENDED_STATUS:
800 /*
801 * Silently swallow these writes.
802 * Since for most registers our card doesn't
803 * actually support a comparable feature,
804 * this is exactly what we should do here.
805 * The AC97 layer's I/O caching probably
806 * automatically takes care of all the rest...
807 * (remembers written values etc.)
808 */
809 break;
810 default:
811 unsupported = 1;
812 break;
813 }
814 }
815 }
816 if (unsupported)
817 snd_azf3328_mixer_ac97_map_unsupported(reg_ac97, "write");
818}
819
820static int __devinit
821snd_azf3328_mixer_new(struct snd_azf3328 *chip)
822{
823 struct snd_ac97_bus *bus;
824 struct snd_ac97_template ac97;
825 static struct snd_ac97_bus_ops ops = {
826 .write = snd_azf3328_mixer_ac97_write,
827 .read = snd_azf3328_mixer_ac97_read,
828 };
829 int rc;
830
831 memset(&ac97, 0, sizeof(ac97));
832 ac97.scaps = AC97_SCAP_SKIP_MODEM
833 | AC97_SCAP_AUDIO /* we support audio! */
834 | AC97_SCAP_NO_SPDIF;
835 ac97.private_data = chip;
836 ac97.pci = chip->pci;
837
838 /*
839 * ALSA's AC97 layer has terrible init crackling issues,
840 * unfortunately, and since it makes use of AC97_RESET,
841 * there's no use trying to mute Master Playback proactively.
842 */
843
844 rc = snd_ac97_bus(chip->card, 0, &ops, NULL, &bus);
845 if (!rc)
846 rc = snd_ac97_mixer(bus, &ac97, &chip->ac97);
847 /*
848 * Make sure to complain loudly in case of AC97 init failure,
849 * since failure may happen quite often,
850 * due to this card being a very quirky AC97 "lookalike".
851 */
852 if (rc)
853 printk(KERN_ERR "azt3328: AC97 init failed, err %d!\n", rc);
854
855 /* If we return an error here, then snd_card_free() should
856 * free up any ac97 codecs that got created, as well as the bus.
857 */
858 return rc;
859}
860#else /* AZF_USE_AC97_LAYER */
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200861static void
Andreas Mohr02330fb2008-05-16 12:18:29 +0200862snd_azf3328_mixer_write_volume_gradually(const struct snd_azf3328 *chip,
863 unsigned reg,
864 unsigned char dst_vol_left,
865 unsigned char dst_vol_right,
866 int chan_sel, int delay
867)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700868{
Andreas Mohr02330fb2008-05-16 12:18:29 +0200869 unsigned long portbase = chip->mixer_io + reg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700870 unsigned char curr_vol_left = 0, curr_vol_right = 0;
Andreas Mohr02330fb2008-05-16 12:18:29 +0200871 int left_change = 0, right_change = 0;
872
Linus Torvalds1da177e2005-04-16 15:20:36 -0700873 snd_azf3328_dbgcallenter();
Andreas Mohr02330fb2008-05-16 12:18:29 +0200874
875 if (chan_sel & SET_CHAN_LEFT) {
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200876 curr_vol_left = inb(portbase + 1);
Andreas Mohr02330fb2008-05-16 12:18:29 +0200877
878 /* take care of muting flag contained in left channel */
879 if (curr_vol_left & AZF_MUTE_BIT)
880 dst_vol_left |= AZF_MUTE_BIT;
881 else
882 dst_vol_left &= ~AZF_MUTE_BIT;
883
884 left_change = (curr_vol_left > dst_vol_left) ? -1 : 1;
885 }
886
887 if (chan_sel & SET_CHAN_RIGHT) {
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200888 curr_vol_right = inb(portbase + 0);
Andreas Mohr02330fb2008-05-16 12:18:29 +0200889
890 right_change = (curr_vol_right > dst_vol_right) ? -1 : 1;
891 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700892
Andreas Mohre2f87262006-05-17 11:04:19 +0200893 do {
Andreas Mohr02330fb2008-05-16 12:18:29 +0200894 if (left_change) {
895 if (curr_vol_left != dst_vol_left) {
896 curr_vol_left += left_change;
897 outb(curr_vol_left, portbase + 1);
898 } else
899 left_change = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700900 }
Andreas Mohr02330fb2008-05-16 12:18:29 +0200901 if (right_change) {
902 if (curr_vol_right != dst_vol_right) {
903 curr_vol_right += right_change;
904
Linus Torvalds1da177e2005-04-16 15:20:36 -0700905 /* during volume change, the right channel is crackling
906 * somewhat more than the left channel, unfortunately.
907 * This seems to be a hardware issue. */
Andreas Mohr02330fb2008-05-16 12:18:29 +0200908 outb(curr_vol_right, portbase + 0);
909 } else
910 right_change = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700911 }
912 if (delay)
913 mdelay(delay);
Andreas Mohr02330fb2008-05-16 12:18:29 +0200914 } while ((left_change) || (right_change));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700915 snd_azf3328_dbgcallleave();
916}
917
918/*
919 * general mixer element
920 */
Takashi Iwai95de7762005-11-17 15:02:42 +0100921struct azf3328_mixer_reg {
Andreas Mohr02330fb2008-05-16 12:18:29 +0200922 unsigned reg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700923 unsigned int lchan_shift, rchan_shift;
924 unsigned int mask;
925 unsigned int invert: 1;
926 unsigned int stereo: 1;
927 unsigned int enum_c: 4;
Takashi Iwai95de7762005-11-17 15:02:42 +0100928};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700929
930#define COMPOSE_MIXER_REG(reg,lchan_shift,rchan_shift,mask,invert,stereo,enum_c) \
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200931 ((reg) | (lchan_shift << 8) | (rchan_shift << 12) | \
932 (mask << 16) | \
933 (invert << 24) | \
934 (stereo << 25) | \
935 (enum_c << 26))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700936
Takashi Iwai95de7762005-11-17 15:02:42 +0100937static void snd_azf3328_mixer_reg_decode(struct azf3328_mixer_reg *r, unsigned long val)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700938{
939 r->reg = val & 0xff;
940 r->lchan_shift = (val >> 8) & 0x0f;
941 r->rchan_shift = (val >> 12) & 0x0f;
942 r->mask = (val >> 16) & 0xff;
943 r->invert = (val >> 24) & 1;
944 r->stereo = (val >> 25) & 1;
945 r->enum_c = (val >> 26) & 0x0f;
946}
947
948/*
949 * mixer switches/volumes
950 */
951
952#define AZF3328_MIXER_SWITCH(xname, reg, shift, invert) \
953{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
954 .info = snd_azf3328_info_mixer, \
955 .get = snd_azf3328_get_mixer, .put = snd_azf3328_put_mixer, \
956 .private_value = COMPOSE_MIXER_REG(reg, shift, 0, 0x1, invert, 0, 0), \
957}
958
959#define AZF3328_MIXER_VOL_STEREO(xname, reg, mask, invert) \
960{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
961 .info = snd_azf3328_info_mixer, \
962 .get = snd_azf3328_get_mixer, .put = snd_azf3328_put_mixer, \
963 .private_value = COMPOSE_MIXER_REG(reg, 8, 0, mask, invert, 1, 0), \
964}
965
966#define AZF3328_MIXER_VOL_MONO(xname, reg, mask, is_right_chan) \
967{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
968 .info = snd_azf3328_info_mixer, \
969 .get = snd_azf3328_get_mixer, .put = snd_azf3328_put_mixer, \
970 .private_value = COMPOSE_MIXER_REG(reg, is_right_chan ? 0 : 8, 0, mask, 1, 0, 0), \
971}
972
973#define AZF3328_MIXER_VOL_SPECIAL(xname, reg, mask, shift, invert) \
974{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
975 .info = snd_azf3328_info_mixer, \
976 .get = snd_azf3328_get_mixer, .put = snd_azf3328_put_mixer, \
977 .private_value = COMPOSE_MIXER_REG(reg, shift, 0, mask, invert, 0, 0), \
978}
979
980#define AZF3328_MIXER_ENUM(xname, reg, enum_c, shift) \
981{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
982 .info = snd_azf3328_info_mixer_enum, \
983 .get = snd_azf3328_get_mixer_enum, .put = snd_azf3328_put_mixer_enum, \
984 .private_value = COMPOSE_MIXER_REG(reg, shift, 0, 0, 0, 0, enum_c), \
985}
986
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200987static int
Takashi Iwai95de7762005-11-17 15:02:42 +0100988snd_azf3328_info_mixer(struct snd_kcontrol *kcontrol,
989 struct snd_ctl_elem_info *uinfo)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700990{
Takashi Iwai95de7762005-11-17 15:02:42 +0100991 struct azf3328_mixer_reg reg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700992
993 snd_azf3328_dbgcallenter();
994 snd_azf3328_mixer_reg_decode(&reg, kcontrol->private_value);
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200995 uinfo->type = reg.mask == 1 ?
996 SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700997 uinfo->count = reg.stereo + 1;
998 uinfo->value.integer.min = 0;
999 uinfo->value.integer.max = reg.mask;
1000 snd_azf3328_dbgcallleave();
1001 return 0;
1002}
1003
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001004static int
Takashi Iwai95de7762005-11-17 15:02:42 +01001005snd_azf3328_get_mixer(struct snd_kcontrol *kcontrol,
1006 struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001007{
Takashi Iwai95de7762005-11-17 15:02:42 +01001008 struct snd_azf3328 *chip = snd_kcontrol_chip(kcontrol);
1009 struct azf3328_mixer_reg reg;
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001010 u16 oreg, val;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001011
1012 snd_azf3328_dbgcallenter();
1013 snd_azf3328_mixer_reg_decode(&reg, kcontrol->private_value);
1014
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001015 oreg = snd_azf3328_mixer_inw(chip, reg.reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001016 val = (oreg >> reg.lchan_shift) & reg.mask;
1017 if (reg.invert)
1018 val = reg.mask - val;
1019 ucontrol->value.integer.value[0] = val;
1020 if (reg.stereo) {
1021 val = (oreg >> reg.rchan_shift) & reg.mask;
1022 if (reg.invert)
1023 val = reg.mask - val;
1024 ucontrol->value.integer.value[1] = val;
1025 }
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001026 snd_azf3328_dbgmixer("get: %02x is %04x -> vol %02lx|%02lx "
1027 "(shift %02d|%02d, mask %02x, inv. %d, stereo %d)\n",
1028 reg.reg, oreg,
1029 ucontrol->value.integer.value[0], ucontrol->value.integer.value[1],
1030 reg.lchan_shift, reg.rchan_shift, reg.mask, reg.invert, reg.stereo);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001031 snd_azf3328_dbgcallleave();
1032 return 0;
1033}
1034
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001035static int
Takashi Iwai95de7762005-11-17 15:02:42 +01001036snd_azf3328_put_mixer(struct snd_kcontrol *kcontrol,
1037 struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001038{
Takashi Iwai95de7762005-11-17 15:02:42 +01001039 struct snd_azf3328 *chip = snd_kcontrol_chip(kcontrol);
1040 struct azf3328_mixer_reg reg;
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001041 u16 oreg, nreg, val;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001042
1043 snd_azf3328_dbgcallenter();
1044 snd_azf3328_mixer_reg_decode(&reg, kcontrol->private_value);
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001045 oreg = snd_azf3328_mixer_inw(chip, reg.reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001046 val = ucontrol->value.integer.value[0] & reg.mask;
1047 if (reg.invert)
1048 val = reg.mask - val;
1049 nreg = oreg & ~(reg.mask << reg.lchan_shift);
1050 nreg |= (val << reg.lchan_shift);
1051 if (reg.stereo) {
1052 val = ucontrol->value.integer.value[1] & reg.mask;
1053 if (reg.invert)
1054 val = reg.mask - val;
1055 nreg &= ~(reg.mask << reg.rchan_shift);
1056 nreg |= (val << reg.rchan_shift);
1057 }
1058 if (reg.mask >= 0x07) /* it's a volume control, so better take care */
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001059 snd_azf3328_mixer_write_volume_gradually(
1060 chip, reg.reg, nreg >> 8, nreg & 0xff,
1061 /* just set both channels, doesn't matter */
1062 SET_CHAN_LEFT|SET_CHAN_RIGHT,
1063 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001064 else
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001065 snd_azf3328_mixer_outw(chip, reg.reg, nreg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001066
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001067 snd_azf3328_dbgmixer("put: %02x to %02lx|%02lx, "
1068 "oreg %04x; shift %02d|%02d -> nreg %04x; after: %04x\n",
1069 reg.reg, ucontrol->value.integer.value[0], ucontrol->value.integer.value[1],
1070 oreg, reg.lchan_shift, reg.rchan_shift,
1071 nreg, snd_azf3328_mixer_inw(chip, reg.reg));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001072 snd_azf3328_dbgcallleave();
1073 return (nreg != oreg);
1074}
1075
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001076static int
Takashi Iwai95de7762005-11-17 15:02:42 +01001077snd_azf3328_info_mixer_enum(struct snd_kcontrol *kcontrol,
1078 struct snd_ctl_elem_info *uinfo)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001079{
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001080 static const char * const texts1[] = {
Andreas Mohr13769e32006-05-17 11:03:16 +02001081 "Mic1", "Mic2"
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001082 };
1083 static const char * const texts2[] = {
Andreas Mohr13769e32006-05-17 11:03:16 +02001084 "Mix", "Mic"
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001085 };
1086 static const char * const texts3[] = {
Andreas Mohr02330fb2008-05-16 12:18:29 +02001087 "Mic", "CD", "Video", "Aux",
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001088 "Line", "Mix", "Mix Mono", "Phone"
Linus Torvalds1da177e2005-04-16 15:20:36 -07001089 };
Andreas Mohr13769e32006-05-17 11:03:16 +02001090 static const char * const texts4[] = {
1091 "pre 3D", "post 3D"
1092 };
Takashi Iwai95de7762005-11-17 15:02:42 +01001093 struct azf3328_mixer_reg reg;
Andreas Mohr627d3e72008-06-23 11:50:47 +02001094 const char * const *p = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001095
1096 snd_azf3328_mixer_reg_decode(&reg, kcontrol->private_value);
1097 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
1098 uinfo->count = (reg.reg == IDX_MIXER_REC_SELECT) ? 2 : 1;
1099 uinfo->value.enumerated.items = reg.enum_c;
1100 if (uinfo->value.enumerated.item > reg.enum_c - 1U)
1101 uinfo->value.enumerated.item = reg.enum_c - 1U;
Andreas Mohre2f87262006-05-17 11:04:19 +02001102 if (reg.reg == IDX_MIXER_ADVCTL2) {
Andreas Mohr13769e32006-05-17 11:03:16 +02001103 switch(reg.lchan_shift) {
1104 case 8: /* modem out sel */
Andreas Mohr627d3e72008-06-23 11:50:47 +02001105 p = texts1;
Andreas Mohr13769e32006-05-17 11:03:16 +02001106 break;
1107 case 9: /* mono sel source */
Andreas Mohr627d3e72008-06-23 11:50:47 +02001108 p = texts2;
Andreas Mohr13769e32006-05-17 11:03:16 +02001109 break;
1110 case 15: /* PCM Out Path */
Andreas Mohr627d3e72008-06-23 11:50:47 +02001111 p = texts4;
Andreas Mohr13769e32006-05-17 11:03:16 +02001112 break;
1113 }
Andreas Mohre2f87262006-05-17 11:04:19 +02001114 } else
Andreas Mohr02330fb2008-05-16 12:18:29 +02001115 if (reg.reg == IDX_MIXER_REC_SELECT)
Andreas Mohr627d3e72008-06-23 11:50:47 +02001116 p = texts3;
Andreas Mohr02330fb2008-05-16 12:18:29 +02001117
Andreas Mohr627d3e72008-06-23 11:50:47 +02001118 strcpy(uinfo->value.enumerated.name, p[uinfo->value.enumerated.item]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001119 return 0;
1120}
1121
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001122static int
Takashi Iwai95de7762005-11-17 15:02:42 +01001123snd_azf3328_get_mixer_enum(struct snd_kcontrol *kcontrol,
1124 struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001125{
Takashi Iwai95de7762005-11-17 15:02:42 +01001126 struct snd_azf3328 *chip = snd_kcontrol_chip(kcontrol);
1127 struct azf3328_mixer_reg reg;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001128 unsigned short val;
Andreas Mohr02330fb2008-05-16 12:18:29 +02001129
Linus Torvalds1da177e2005-04-16 15:20:36 -07001130 snd_azf3328_mixer_reg_decode(&reg, kcontrol->private_value);
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001131 val = snd_azf3328_mixer_inw(chip, reg.reg);
Andreas Mohre2f87262006-05-17 11:04:19 +02001132 if (reg.reg == IDX_MIXER_REC_SELECT) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001133 ucontrol->value.enumerated.item[0] = (val >> 8) & (reg.enum_c - 1);
1134 ucontrol->value.enumerated.item[1] = (val >> 0) & (reg.enum_c - 1);
Andreas Mohre2f87262006-05-17 11:04:19 +02001135 } else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001136 ucontrol->value.enumerated.item[0] = (val >> reg.lchan_shift) & (reg.enum_c - 1);
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001137
1138 snd_azf3328_dbgmixer("get_enum: %02x is %04x -> %d|%d (shift %02d, enum_c %d)\n",
1139 reg.reg, val, ucontrol->value.enumerated.item[0], ucontrol->value.enumerated.item[1],
1140 reg.lchan_shift, reg.enum_c);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001141 return 0;
1142}
1143
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001144static int
Takashi Iwai95de7762005-11-17 15:02:42 +01001145snd_azf3328_put_mixer_enum(struct snd_kcontrol *kcontrol,
1146 struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001147{
Takashi Iwai95de7762005-11-17 15:02:42 +01001148 struct snd_azf3328 *chip = snd_kcontrol_chip(kcontrol);
1149 struct azf3328_mixer_reg reg;
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001150 u16 oreg, nreg, val;
Andreas Mohr02330fb2008-05-16 12:18:29 +02001151
Linus Torvalds1da177e2005-04-16 15:20:36 -07001152 snd_azf3328_mixer_reg_decode(&reg, kcontrol->private_value);
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001153 oreg = snd_azf3328_mixer_inw(chip, reg.reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001154 val = oreg;
Andreas Mohre2f87262006-05-17 11:04:19 +02001155 if (reg.reg == IDX_MIXER_REC_SELECT) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001156 if (ucontrol->value.enumerated.item[0] > reg.enum_c - 1U ||
1157 ucontrol->value.enumerated.item[1] > reg.enum_c - 1U)
1158 return -EINVAL;
1159 val = (ucontrol->value.enumerated.item[0] << 8) |
1160 (ucontrol->value.enumerated.item[1] << 0);
Andreas Mohre2f87262006-05-17 11:04:19 +02001161 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001162 if (ucontrol->value.enumerated.item[0] > reg.enum_c - 1U)
1163 return -EINVAL;
1164 val &= ~((reg.enum_c - 1) << reg.lchan_shift);
1165 val |= (ucontrol->value.enumerated.item[0] << reg.lchan_shift);
1166 }
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001167 snd_azf3328_mixer_outw(chip, reg.reg, val);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001168 nreg = val;
1169
1170 snd_azf3328_dbgmixer("put_enum: %02x to %04x, oreg %04x\n", reg.reg, val, oreg);
1171 return (nreg != oreg);
1172}
1173
Takashi Iwai1b60f6b2007-03-13 22:13:47 +01001174static struct snd_kcontrol_new snd_azf3328_mixer_controls[] __devinitdata = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001175 AZF3328_MIXER_SWITCH("Master Playback Switch", IDX_MIXER_PLAY_MASTER, 15, 1),
1176 AZF3328_MIXER_VOL_STEREO("Master Playback Volume", IDX_MIXER_PLAY_MASTER, 0x1f, 1),
Andreas Mohr627d3e72008-06-23 11:50:47 +02001177 AZF3328_MIXER_SWITCH("PCM Playback Switch", IDX_MIXER_WAVEOUT, 15, 1),
1178 AZF3328_MIXER_VOL_STEREO("PCM Playback Volume",
1179 IDX_MIXER_WAVEOUT, 0x1f, 1),
1180 AZF3328_MIXER_SWITCH("PCM 3D Bypass Playback Switch",
1181 IDX_MIXER_ADVCTL2, 7, 1),
Linus Torvalds1da177e2005-04-16 15:20:36 -07001182 AZF3328_MIXER_SWITCH("FM Playback Switch", IDX_MIXER_FMSYNTH, 15, 1),
1183 AZF3328_MIXER_VOL_STEREO("FM Playback Volume", IDX_MIXER_FMSYNTH, 0x1f, 1),
1184 AZF3328_MIXER_SWITCH("CD Playback Switch", IDX_MIXER_CDAUDIO, 15, 1),
1185 AZF3328_MIXER_VOL_STEREO("CD Playback Volume", IDX_MIXER_CDAUDIO, 0x1f, 1),
1186 AZF3328_MIXER_SWITCH("Capture Switch", IDX_MIXER_REC_VOLUME, 15, 1),
1187 AZF3328_MIXER_VOL_STEREO("Capture Volume", IDX_MIXER_REC_VOLUME, 0x0f, 0),
1188 AZF3328_MIXER_ENUM("Capture Source", IDX_MIXER_REC_SELECT, 8, 0),
1189 AZF3328_MIXER_SWITCH("Mic Playback Switch", IDX_MIXER_MIC, 15, 1),
1190 AZF3328_MIXER_VOL_MONO("Mic Playback Volume", IDX_MIXER_MIC, 0x1f, 1),
1191 AZF3328_MIXER_SWITCH("Mic Boost (+20dB)", IDX_MIXER_MIC, 6, 0),
1192 AZF3328_MIXER_SWITCH("Line Playback Switch", IDX_MIXER_LINEIN, 15, 1),
1193 AZF3328_MIXER_VOL_STEREO("Line Playback Volume", IDX_MIXER_LINEIN, 0x1f, 1),
Jaroslav Kyselad355c82a2009-11-03 15:47:25 +01001194 AZF3328_MIXER_SWITCH("Beep Playback Switch", IDX_MIXER_PCBEEP, 15, 1),
1195 AZF3328_MIXER_VOL_SPECIAL("Beep Playback Volume", IDX_MIXER_PCBEEP, 0x0f, 1, 1),
Linus Torvalds1da177e2005-04-16 15:20:36 -07001196 AZF3328_MIXER_SWITCH("Video Playback Switch", IDX_MIXER_VIDEO, 15, 1),
1197 AZF3328_MIXER_VOL_STEREO("Video Playback Volume", IDX_MIXER_VIDEO, 0x1f, 1),
1198 AZF3328_MIXER_SWITCH("Aux Playback Switch", IDX_MIXER_AUX, 15, 1),
1199 AZF3328_MIXER_VOL_STEREO("Aux Playback Volume", IDX_MIXER_AUX, 0x1f, 1),
1200 AZF3328_MIXER_SWITCH("Modem Playback Switch", IDX_MIXER_MODEMOUT, 15, 1),
1201 AZF3328_MIXER_VOL_MONO("Modem Playback Volume", IDX_MIXER_MODEMOUT, 0x1f, 1),
1202 AZF3328_MIXER_SWITCH("Modem Capture Switch", IDX_MIXER_MODEMIN, 15, 1),
1203 AZF3328_MIXER_VOL_MONO("Modem Capture Volume", IDX_MIXER_MODEMIN, 0x1f, 1),
Andreas Mohr13769e32006-05-17 11:03:16 +02001204 AZF3328_MIXER_ENUM("Mic Select", IDX_MIXER_ADVCTL2, 2, 8),
1205 AZF3328_MIXER_ENUM("Mono Output Select", IDX_MIXER_ADVCTL2, 2, 9),
Andreas Mohre24a1212007-03-26 12:49:45 +02001206 AZF3328_MIXER_ENUM("PCM Output Route", IDX_MIXER_ADVCTL2, 2, 15), /* PCM Out Path, place in front since it controls *both* 3D and Bass/Treble! */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001207 AZF3328_MIXER_VOL_SPECIAL("Tone Control - Treble", IDX_MIXER_BASSTREBLE, 0x07, 1, 0),
1208 AZF3328_MIXER_VOL_SPECIAL("Tone Control - Bass", IDX_MIXER_BASSTREBLE, 0x07, 9, 0),
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001209 AZF3328_MIXER_SWITCH("3D Control - Switch", IDX_MIXER_ADVCTL2, 13, 0),
Andreas Mohr13769e32006-05-17 11:03:16 +02001210 AZF3328_MIXER_VOL_SPECIAL("3D Control - Width", IDX_MIXER_ADVCTL1, 0x07, 1, 0), /* "3D Width" */
1211 AZF3328_MIXER_VOL_SPECIAL("3D Control - Depth", IDX_MIXER_ADVCTL1, 0x03, 8, 0), /* "Hifi 3D" */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001212#if MIXER_TESTING
1213 AZF3328_MIXER_SWITCH("0", IDX_MIXER_ADVCTL2, 0, 0),
1214 AZF3328_MIXER_SWITCH("1", IDX_MIXER_ADVCTL2, 1, 0),
1215 AZF3328_MIXER_SWITCH("2", IDX_MIXER_ADVCTL2, 2, 0),
1216 AZF3328_MIXER_SWITCH("3", IDX_MIXER_ADVCTL2, 3, 0),
1217 AZF3328_MIXER_SWITCH("4", IDX_MIXER_ADVCTL2, 4, 0),
1218 AZF3328_MIXER_SWITCH("5", IDX_MIXER_ADVCTL2, 5, 0),
1219 AZF3328_MIXER_SWITCH("6", IDX_MIXER_ADVCTL2, 6, 0),
1220 AZF3328_MIXER_SWITCH("7", IDX_MIXER_ADVCTL2, 7, 0),
1221 AZF3328_MIXER_SWITCH("8", IDX_MIXER_ADVCTL2, 8, 0),
1222 AZF3328_MIXER_SWITCH("9", IDX_MIXER_ADVCTL2, 9, 0),
1223 AZF3328_MIXER_SWITCH("10", IDX_MIXER_ADVCTL2, 10, 0),
1224 AZF3328_MIXER_SWITCH("11", IDX_MIXER_ADVCTL2, 11, 0),
1225 AZF3328_MIXER_SWITCH("12", IDX_MIXER_ADVCTL2, 12, 0),
1226 AZF3328_MIXER_SWITCH("13", IDX_MIXER_ADVCTL2, 13, 0),
1227 AZF3328_MIXER_SWITCH("14", IDX_MIXER_ADVCTL2, 14, 0),
1228 AZF3328_MIXER_SWITCH("15", IDX_MIXER_ADVCTL2, 15, 0),
1229#endif
1230};
1231
Takashi Iwai1b60f6b2007-03-13 22:13:47 +01001232static u16 __devinitdata snd_azf3328_init_values[][2] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001233 { IDX_MIXER_PLAY_MASTER, MIXER_MUTE_MASK|0x1f1f },
1234 { IDX_MIXER_MODEMOUT, MIXER_MUTE_MASK|0x1f1f },
1235 { IDX_MIXER_BASSTREBLE, 0x0000 },
1236 { IDX_MIXER_PCBEEP, MIXER_MUTE_MASK|0x1f1f },
1237 { IDX_MIXER_MODEMIN, MIXER_MUTE_MASK|0x1f1f },
1238 { IDX_MIXER_MIC, MIXER_MUTE_MASK|0x001f },
1239 { IDX_MIXER_LINEIN, MIXER_MUTE_MASK|0x1f1f },
1240 { IDX_MIXER_CDAUDIO, MIXER_MUTE_MASK|0x1f1f },
1241 { IDX_MIXER_VIDEO, MIXER_MUTE_MASK|0x1f1f },
1242 { IDX_MIXER_AUX, MIXER_MUTE_MASK|0x1f1f },
1243 { IDX_MIXER_WAVEOUT, MIXER_MUTE_MASK|0x1f1f },
1244 { IDX_MIXER_FMSYNTH, MIXER_MUTE_MASK|0x1f1f },
1245 { IDX_MIXER_REC_VOLUME, MIXER_MUTE_MASK|0x0707 },
1246};
1247
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001248static int __devinit
Takashi Iwai95de7762005-11-17 15:02:42 +01001249snd_azf3328_mixer_new(struct snd_azf3328 *chip)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001250{
Takashi Iwai95de7762005-11-17 15:02:42 +01001251 struct snd_card *card;
1252 const struct snd_kcontrol_new *sw;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001253 unsigned int idx;
1254 int err;
1255
1256 snd_azf3328_dbgcallenter();
Takashi Iwaida3cec32008-08-08 17:12:14 +02001257 if (snd_BUG_ON(!chip || !chip->card))
1258 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001259
1260 card = chip->card;
1261
1262 /* mixer reset */
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001263 snd_azf3328_mixer_outw(chip, IDX_MIXER_RESET, 0x0000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001264
1265 /* mute and zero volume channels */
Andreas Mohr02330fb2008-05-16 12:18:29 +02001266 for (idx = 0; idx < ARRAY_SIZE(snd_azf3328_init_values); ++idx) {
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001267 snd_azf3328_mixer_outw(chip,
1268 snd_azf3328_init_values[idx][0],
1269 snd_azf3328_init_values[idx][1]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001270 }
Andreas Mohr02330fb2008-05-16 12:18:29 +02001271
Linus Torvalds1da177e2005-04-16 15:20:36 -07001272 /* add mixer controls */
1273 sw = snd_azf3328_mixer_controls;
Andreas Mohr02330fb2008-05-16 12:18:29 +02001274 for (idx = 0; idx < ARRAY_SIZE(snd_azf3328_mixer_controls);
1275 ++idx, ++sw) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001276 if ((err = snd_ctl_add(chip->card, snd_ctl_new1(sw, chip))) < 0)
1277 return err;
1278 }
1279 snd_component_add(card, "AZF3328 mixer");
1280 strcpy(card->mixername, "AZF3328 mixer");
1281
1282 snd_azf3328_dbgcallleave();
1283 return 0;
1284}
Andreas Mohrb5dc20c2011-02-19 00:49:32 +01001285#endif /* AZF_USE_AC97_LAYER */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001286
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001287static int
Takashi Iwai95de7762005-11-17 15:02:42 +01001288snd_azf3328_hw_params(struct snd_pcm_substream *substream,
1289 struct snd_pcm_hw_params *hw_params)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001290{
1291 int res;
1292 snd_azf3328_dbgcallenter();
1293 res = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
1294 snd_azf3328_dbgcallleave();
1295 return res;
1296}
1297
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001298static int
Takashi Iwai95de7762005-11-17 15:02:42 +01001299snd_azf3328_hw_free(struct snd_pcm_substream *substream)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001300{
1301 snd_azf3328_dbgcallenter();
1302 snd_pcm_lib_free_pages(substream);
1303 snd_azf3328_dbgcallleave();
1304 return 0;
1305}
1306
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001307static void
Andreas Mohrda237f32010-12-27 21:17:26 +01001308snd_azf3328_codec_setfmt(struct snd_azf3328_codec_data *codec,
Andreas Mohr627d3e72008-06-23 11:50:47 +02001309 enum azf_freq_t bitrate,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001310 unsigned int format_width,
1311 unsigned int channels
1312)
1313{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001314 unsigned long flags;
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001315 u16 val = 0xff00;
Andreas Mohr8d9a1142010-12-27 21:16:49 +01001316 u8 freq = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001317
1318 snd_azf3328_dbgcallenter();
1319 switch (bitrate) {
Andreas Mohr8d9a1142010-12-27 21:16:49 +01001320#define AZF_FMT_XLATE(in_freq, out_bits) \
1321 do { \
1322 case AZF_FREQ_ ## in_freq: \
1323 freq = SOUNDFORMAT_FREQ_ ## out_bits; \
1324 break; \
1325 } while (0);
1326 AZF_FMT_XLATE(4000, SUSPECTED_4000)
1327 AZF_FMT_XLATE(4800, SUSPECTED_4800)
1328 /* the AZF3328 names it "5510" for some strange reason: */
1329 AZF_FMT_XLATE(5512, 5510)
1330 AZF_FMT_XLATE(6620, 6620)
1331 AZF_FMT_XLATE(8000, 8000)
1332 AZF_FMT_XLATE(9600, 9600)
1333 AZF_FMT_XLATE(11025, 11025)
1334 AZF_FMT_XLATE(13240, SUSPECTED_13240)
1335 AZF_FMT_XLATE(16000, 16000)
1336 AZF_FMT_XLATE(22050, 22050)
1337 AZF_FMT_XLATE(32000, 32000)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001338 default:
Takashi Iwai99b359b2005-10-20 18:26:44 +02001339 snd_printk(KERN_WARNING "unknown bitrate %d, assuming 44.1kHz!\n", bitrate);
Andreas Mohr02330fb2008-05-16 12:18:29 +02001340 /* fall-through */
Andreas Mohr8d9a1142010-12-27 21:16:49 +01001341 AZF_FMT_XLATE(44100, 44100)
1342 AZF_FMT_XLATE(48000, 48000)
1343 AZF_FMT_XLATE(66200, SUSPECTED_66200)
1344#undef AZF_FMT_XLATE
Linus Torvalds1da177e2005-04-16 15:20:36 -07001345 }
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001346 /* val = 0xff07; 3m27.993s (65301Hz; -> 64000Hz???) hmm, 66120, 65967, 66123 */
1347 /* val = 0xff09; 17m15.098s (13123,478Hz; -> 12000Hz???) hmm, 13237.2Hz? */
1348 /* val = 0xff0a; 47m30.599s (4764,891Hz; -> 4800Hz???) yup, 4803Hz */
1349 /* val = 0xff0c; 57m0.510s (4010,263Hz; -> 4000Hz???) yup, 4003Hz */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001350 /* val = 0xff05; 5m11.556s (... -> 44100Hz) */
1351 /* val = 0xff03; 10m21.529s (21872,463Hz; -> 22050Hz???) */
1352 /* val = 0xff0f; 20m41.883s (10937,993Hz; -> 11025Hz???) */
1353 /* val = 0xff0d; 41m23.135s (5523,600Hz; -> 5512Hz???) */
1354 /* val = 0xff0e; 28m30.777s (8017Hz; -> 8000Hz???) */
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001355
Andreas Mohr8d9a1142010-12-27 21:16:49 +01001356 val |= freq;
1357
Linus Torvalds1da177e2005-04-16 15:20:36 -07001358 if (channels == 2)
1359 val |= SOUNDFORMAT_FLAG_2CHANNELS;
1360
1361 if (format_width == 16)
1362 val |= SOUNDFORMAT_FLAG_16BIT;
1363
Andreas Mohrda237f32010-12-27 21:17:26 +01001364 spin_lock_irqsave(codec->lock, flags);
Andreas Mohr02330fb2008-05-16 12:18:29 +02001365
Linus Torvalds1da177e2005-04-16 15:20:36 -07001366 /* set bitrate/format */
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001367 snd_azf3328_codec_outw(codec, IDX_IO_CODEC_SOUNDFORMAT, val);
Andreas Mohr02330fb2008-05-16 12:18:29 +02001368
Linus Torvalds1da177e2005-04-16 15:20:36 -07001369 /* changing the bitrate/format settings switches off the
1370 * audio output with an annoying click in case of 8/16bit format change
1371 * (maybe shutting down DAC/ADC?), thus immediately
1372 * do some tweaking to reenable it and get rid of the clicking
1373 * (FIXME: yes, it works, but what exactly am I doing here?? :)
1374 * FIXME: does this have some side effects for full-duplex
1375 * or other dramatic side effects? */
Andreas Mohradf59312010-12-27 21:16:43 +01001376 /* do it for non-capture codecs only */
Andreas Mohrda237f32010-12-27 21:17:26 +01001377 if (codec->type != AZF_CODEC_CAPTURE)
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001378 snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS,
1379 snd_azf3328_codec_inw(codec, IDX_IO_CODEC_DMA_FLAGS) |
1380 DMA_RUN_SOMETHING1 |
1381 DMA_RUN_SOMETHING2 |
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001382 SOMETHING_ALMOST_ALWAYS_SET |
1383 DMA_EPILOGUE_SOMETHING |
1384 DMA_SOMETHING_ELSE
1385 );
Linus Torvalds1da177e2005-04-16 15:20:36 -07001386
Andreas Mohrda237f32010-12-27 21:17:26 +01001387 spin_unlock_irqrestore(codec->lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001388 snd_azf3328_dbgcallleave();
1389}
1390
Andreas Mohr02330fb2008-05-16 12:18:29 +02001391static inline void
Andreas Mohrda237f32010-12-27 21:17:26 +01001392snd_azf3328_codec_setfmt_lowpower(struct snd_azf3328_codec_data *codec
Andreas Mohr02330fb2008-05-16 12:18:29 +02001393)
1394{
1395 /* choose lowest frequency for low power consumption.
1396 * While this will cause louder noise due to rather coarse frequency,
1397 * it should never matter since output should always
1398 * get disabled properly when idle anyway. */
Andreas Mohrda237f32010-12-27 21:17:26 +01001399 snd_azf3328_codec_setfmt(codec, AZF_FREQ_4000, 8, 1);
Andreas Mohr02330fb2008-05-16 12:18:29 +02001400}
1401
Andreas Mohr627d3e72008-06-23 11:50:47 +02001402static void
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001403snd_azf3328_ctrl_reg_6AH_update(struct snd_azf3328 *chip,
Andreas Mohr627d3e72008-06-23 11:50:47 +02001404 unsigned bitmask,
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001405 bool enable
Andreas Mohr627d3e72008-06-23 11:50:47 +02001406)
1407{
Andreas Mohr78df6172009-07-12 22:17:54 +02001408 bool do_mask = !enable;
1409 if (do_mask)
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001410 chip->shadow_reg_ctrl_6AH |= bitmask;
Andreas Mohr78df6172009-07-12 22:17:54 +02001411 else
1412 chip->shadow_reg_ctrl_6AH &= ~bitmask;
1413 snd_azf3328_dbgcodec("6AH_update mask 0x%04x do_mask %d: val 0x%04x\n",
1414 bitmask, do_mask, chip->shadow_reg_ctrl_6AH);
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001415 snd_azf3328_ctrl_outw(chip, IDX_IO_6AH, chip->shadow_reg_ctrl_6AH);
Andreas Mohr627d3e72008-06-23 11:50:47 +02001416}
1417
Andreas Mohr02330fb2008-05-16 12:18:29 +02001418static inline void
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001419snd_azf3328_ctrl_enable_codecs(struct snd_azf3328 *chip, bool enable)
Andreas Mohr02330fb2008-05-16 12:18:29 +02001420{
Andreas Mohr78df6172009-07-12 22:17:54 +02001421 snd_azf3328_dbgcodec("codec_enable %d\n", enable);
Andreas Mohr02330fb2008-05-16 12:18:29 +02001422 /* no idea what exactly is being done here, but I strongly assume it's
1423 * PM related */
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001424 snd_azf3328_ctrl_reg_6AH_update(
Andreas Mohr627d3e72008-06-23 11:50:47 +02001425 chip, IO_6A_PAUSE_PLAYBACK_BIT8, enable
Andreas Mohr02330fb2008-05-16 12:18:29 +02001426 );
1427}
1428
1429static void
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001430snd_azf3328_ctrl_codec_activity(struct snd_azf3328 *chip,
1431 enum snd_azf3328_codec_type codec_type,
1432 bool enable
Andreas Mohr02330fb2008-05-16 12:18:29 +02001433)
1434{
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001435 struct snd_azf3328_codec_data *codec = &chip->codecs[codec_type];
1436 bool need_change = (codec->running != enable);
Andreas Mohr02330fb2008-05-16 12:18:29 +02001437
Andreas Mohr78df6172009-07-12 22:17:54 +02001438 snd_azf3328_dbgcodec(
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001439 "codec_activity: %s codec, enable %d, need_change %d\n",
1440 codec->name, enable, need_change
Andreas Mohr02330fb2008-05-16 12:18:29 +02001441 );
1442 if (need_change) {
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001443 static const struct {
1444 enum snd_azf3328_codec_type other1;
1445 enum snd_azf3328_codec_type other2;
1446 } peer_codecs[3] =
1447 { { AZF_CODEC_CAPTURE, AZF_CODEC_I2S_OUT },
1448 { AZF_CODEC_PLAYBACK, AZF_CODEC_I2S_OUT },
1449 { AZF_CODEC_PLAYBACK, AZF_CODEC_CAPTURE } };
1450 bool call_function;
1451
1452 if (enable)
1453 /* if enable codec, call enable_codecs func
1454 to enable codec supply... */
1455 call_function = 1;
1456 else {
1457 /* ...otherwise call enable_codecs func
1458 (which globally shuts down operation of codecs)
1459 only in case the other codecs are currently
1460 not active either! */
Andreas Mohr78df6172009-07-12 22:17:54 +02001461 call_function =
1462 ((!chip->codecs[peer_codecs[codec_type].other1]
1463 .running)
1464 && (!chip->codecs[peer_codecs[codec_type].other2]
1465 .running));
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001466 }
1467 if (call_function)
1468 snd_azf3328_ctrl_enable_codecs(chip, enable);
Andreas Mohr02330fb2008-05-16 12:18:29 +02001469
1470 /* ...and adjust clock, too
1471 * (reduce noise and power consumption) */
1472 if (!enable)
Andreas Mohrda237f32010-12-27 21:17:26 +01001473 snd_azf3328_codec_setfmt_lowpower(codec);
Andreas Mohr78df6172009-07-12 22:17:54 +02001474 codec->running = enable;
Andreas Mohr02330fb2008-05-16 12:18:29 +02001475 }
Andreas Mohr02330fb2008-05-16 12:18:29 +02001476}
1477
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001478static void
Andreas Mohrda237f32010-12-27 21:17:26 +01001479snd_azf3328_codec_setdmaa(struct snd_azf3328_codec_data *codec,
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001480 unsigned long addr,
Andreas Mohr689c6912010-12-27 21:17:35 +01001481 unsigned int period_bytes,
1482 unsigned int buffer_bytes
Andreas Mohr02330fb2008-05-16 12:18:29 +02001483)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001484{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001485 snd_azf3328_dbgcallenter();
Andreas Mohr689c6912010-12-27 21:17:35 +01001486 WARN_ONCE(period_bytes & 1, "odd period length!?\n");
1487 WARN_ONCE(buffer_bytes != 2 * period_bytes,
1488 "missed our input expectations! %u vs. %u\n",
1489 buffer_bytes, period_bytes);
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001490 if (!codec->running) {
1491 /* AZF3328 uses a two buffer pointer DMA transfer approach */
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001492
Andreas Mohr689c6912010-12-27 21:17:35 +01001493 unsigned long flags;
Andreas Mohr02330fb2008-05-16 12:18:29 +02001494
1495 /* width 32bit (prevent overflow): */
Andreas Mohr689c6912010-12-27 21:17:35 +01001496 u32 area_length;
1497 struct codec_setup_io {
1498 u32 dma_start_1;
1499 u32 dma_start_2;
1500 u32 dma_lengths;
1501 } __attribute__((packed)) setup_io;
Andreas Mohr02330fb2008-05-16 12:18:29 +02001502
Andreas Mohr689c6912010-12-27 21:17:35 +01001503 area_length = buffer_bytes/2;
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001504
Andreas Mohr689c6912010-12-27 21:17:35 +01001505 setup_io.dma_start_1 = addr;
1506 setup_io.dma_start_2 = addr+area_length;
1507
1508 snd_azf3328_dbgcodec(
1509 "setdma: buffers %08x[%u] / %08x[%u], %u, %u\n",
1510 setup_io.dma_start_1, area_length,
1511 setup_io.dma_start_2, area_length,
1512 period_bytes, buffer_bytes);
1513
1514 /* Hmm, are we really supposed to decrement this by 1??
1515 Most definitely certainly not: configuring full length does
1516 work properly (i.e. likely better), and BTW we
1517 violated possibly differing frame sizes with this...
1518
1519 area_length--; |* max. index *|
1520 */
Andreas Mohr79741502010-11-21 12:09:32 +01001521
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001522 /* build combined I/O buffer length word */
Andreas Mohr689c6912010-12-27 21:17:35 +01001523 setup_io.dma_lengths = (area_length << 16) | (area_length);
1524
Andreas Mohrda237f32010-12-27 21:17:26 +01001525 spin_lock_irqsave(codec->lock, flags);
Andreas Mohr689c6912010-12-27 21:17:35 +01001526 snd_azf3328_codec_outl_multi(
1527 codec, IDX_IO_CODEC_DMA_START_1, &setup_io, 3
1528 );
Andreas Mohrda237f32010-12-27 21:17:26 +01001529 spin_unlock_irqrestore(codec->lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001530 }
1531 snd_azf3328_dbgcallleave();
1532}
1533
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001534static int
Andreas Mohrda237f32010-12-27 21:17:26 +01001535snd_azf3328_pcm_prepare(struct snd_pcm_substream *substream)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001536{
Takashi Iwai95de7762005-11-17 15:02:42 +01001537 struct snd_pcm_runtime *runtime = substream->runtime;
Andreas Mohrda237f32010-12-27 21:17:26 +01001538 struct snd_azf3328_codec_data *codec = runtime->private_data;
Andreas Mohr34585592010-12-27 21:17:11 +01001539#if 0
Linus Torvalds1da177e2005-04-16 15:20:36 -07001540 unsigned int size = snd_pcm_lib_buffer_bytes(substream);
1541 unsigned int count = snd_pcm_lib_period_bytes(substream);
1542#endif
1543
1544 snd_azf3328_dbgcallenter();
Andreas Mohr34585592010-12-27 21:17:11 +01001545
1546 codec->dma_base = runtime->dma_addr;
1547
Linus Torvalds1da177e2005-04-16 15:20:36 -07001548#if 0
Andreas Mohrda237f32010-12-27 21:17:26 +01001549 snd_azf3328_codec_setfmt(codec,
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001550 runtime->rate,
1551 snd_pcm_format_width(runtime->format),
1552 runtime->channels);
Andreas Mohrda237f32010-12-27 21:17:26 +01001553 snd_azf3328_codec_setdmaa(codec,
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001554 runtime->dma_addr, count, size);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001555#endif
1556 snd_azf3328_dbgcallleave();
1557 return 0;
1558}
1559
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001560static int
Andreas Mohrda237f32010-12-27 21:17:26 +01001561snd_azf3328_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001562{
Takashi Iwai95de7762005-11-17 15:02:42 +01001563 struct snd_azf3328 *chip = snd_pcm_substream_chip(substream);
1564 struct snd_pcm_runtime *runtime = substream->runtime;
Andreas Mohrda237f32010-12-27 21:17:26 +01001565 struct snd_azf3328_codec_data *codec = runtime->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001566 int result = 0;
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001567 u16 flags1;
1568 bool previously_muted = 0;
Andreas Mohrda237f32010-12-27 21:17:26 +01001569 bool is_main_mixer_playback_codec = (AZF_CODEC_PLAYBACK == codec->type);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001570
Andreas Mohrda237f32010-12-27 21:17:26 +01001571 snd_azf3328_dbgcalls("snd_azf3328_pcm_trigger cmd %d\n", cmd);
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001572
Linus Torvalds1da177e2005-04-16 15:20:36 -07001573 switch (cmd) {
1574 case SNDRV_PCM_TRIGGER_START:
Andreas Mohr78df6172009-07-12 22:17:54 +02001575 snd_azf3328_dbgcodec("START %s\n", codec->name);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001576
Andreas Mohrda237f32010-12-27 21:17:26 +01001577 if (is_main_mixer_playback_codec) {
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001578 /* mute WaveOut (avoid clicking during setup) */
1579 previously_muted =
Andreas Mohrb5dc20c2011-02-19 00:49:32 +01001580 snd_azf3328_mixer_mute_control_pcm(
1581 chip, 1
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001582 );
1583 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001584
Andreas Mohrda237f32010-12-27 21:17:26 +01001585 snd_azf3328_codec_setfmt(codec,
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001586 runtime->rate,
1587 snd_pcm_format_width(runtime->format),
1588 runtime->channels);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001589
Andreas Mohrda237f32010-12-27 21:17:26 +01001590 spin_lock(codec->lock);
Andreas Mohr02330fb2008-05-16 12:18:29 +02001591 /* first, remember current value: */
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001592 flags1 = snd_azf3328_codec_inw(codec, IDX_IO_CODEC_DMA_FLAGS);
Andreas Mohr02330fb2008-05-16 12:18:29 +02001593
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001594 /* stop transfer */
1595 flags1 &= ~DMA_RESUME;
1596 snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, flags1);
Andreas Mohr02330fb2008-05-16 12:18:29 +02001597
Linus Torvalds1da177e2005-04-16 15:20:36 -07001598 /* FIXME: clear interrupts or what??? */
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001599 snd_azf3328_codec_outw(codec, IDX_IO_CODEC_IRQTYPE, 0xffff);
Andreas Mohrda237f32010-12-27 21:17:26 +01001600 spin_unlock(codec->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001601
Andreas Mohrda237f32010-12-27 21:17:26 +01001602 snd_azf3328_codec_setdmaa(codec, runtime->dma_addr,
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001603 snd_pcm_lib_period_bytes(substream),
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001604 snd_pcm_lib_buffer_bytes(substream)
1605 );
Linus Torvalds1da177e2005-04-16 15:20:36 -07001606
Andreas Mohrda237f32010-12-27 21:17:26 +01001607 spin_lock(codec->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001608#ifdef WIN9X
1609 /* FIXME: enable playback/recording??? */
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001610 flags1 |= DMA_RUN_SOMETHING1 | DMA_RUN_SOMETHING2;
1611 snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, flags1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001612
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001613 /* start transfer again */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001614 /* FIXME: what is this value (0x0010)??? */
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001615 flags1 |= DMA_RESUME | DMA_EPILOGUE_SOMETHING;
1616 snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, flags1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001617#else /* NT4 */
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001618 snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS,
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001619 0x0000);
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001620 snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS,
1621 DMA_RUN_SOMETHING1);
1622 snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS,
1623 DMA_RUN_SOMETHING1 |
1624 DMA_RUN_SOMETHING2);
1625 snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS,
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001626 DMA_RESUME |
1627 SOMETHING_ALMOST_ALWAYS_SET |
1628 DMA_EPILOGUE_SOMETHING |
1629 DMA_SOMETHING_ELSE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001630#endif
Andreas Mohrda237f32010-12-27 21:17:26 +01001631 spin_unlock(codec->lock);
1632 snd_azf3328_ctrl_codec_activity(chip, codec->type, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001633
Andreas Mohrda237f32010-12-27 21:17:26 +01001634 if (is_main_mixer_playback_codec) {
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001635 /* now unmute WaveOut */
1636 if (!previously_muted)
Andreas Mohrb5dc20c2011-02-19 00:49:32 +01001637 snd_azf3328_mixer_mute_control_pcm(
1638 chip, 0
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001639 );
1640 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001641
Andreas Mohr78df6172009-07-12 22:17:54 +02001642 snd_azf3328_dbgcodec("STARTED %s\n", codec->name);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001643 break;
Andreas Mohrca54bde2006-05-17 11:02:24 +02001644 case SNDRV_PCM_TRIGGER_RESUME:
Andreas Mohr78df6172009-07-12 22:17:54 +02001645 snd_azf3328_dbgcodec("RESUME %s\n", codec->name);
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001646 /* resume codec if we were active */
Andreas Mohrda237f32010-12-27 21:17:26 +01001647 spin_lock(codec->lock);
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001648 if (codec->running)
1649 snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS,
1650 snd_azf3328_codec_inw(
1651 codec, IDX_IO_CODEC_DMA_FLAGS
1652 ) | DMA_RESUME
1653 );
Andreas Mohrda237f32010-12-27 21:17:26 +01001654 spin_unlock(codec->lock);
Andreas Mohrca54bde2006-05-17 11:02:24 +02001655 break;
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001656 case SNDRV_PCM_TRIGGER_STOP:
Andreas Mohr78df6172009-07-12 22:17:54 +02001657 snd_azf3328_dbgcodec("STOP %s\n", codec->name);
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001658
Andreas Mohrda237f32010-12-27 21:17:26 +01001659 if (is_main_mixer_playback_codec) {
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001660 /* mute WaveOut (avoid clicking during setup) */
1661 previously_muted =
Andreas Mohrb5dc20c2011-02-19 00:49:32 +01001662 snd_azf3328_mixer_mute_control_pcm(
1663 chip, 1
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001664 );
1665 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001666
Andreas Mohrda237f32010-12-27 21:17:26 +01001667 spin_lock(codec->lock);
Andreas Mohr02330fb2008-05-16 12:18:29 +02001668 /* first, remember current value: */
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001669 flags1 = snd_azf3328_codec_inw(codec, IDX_IO_CODEC_DMA_FLAGS);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001670
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001671 /* stop transfer */
1672 flags1 &= ~DMA_RESUME;
1673 snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, flags1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001674
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001675 /* hmm, is this really required? we're resetting the same bit
1676 * immediately thereafter... */
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001677 flags1 |= DMA_RUN_SOMETHING1;
1678 snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, flags1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001679
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001680 flags1 &= ~DMA_RUN_SOMETHING1;
1681 snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, flags1);
Andreas Mohrda237f32010-12-27 21:17:26 +01001682 spin_unlock(codec->lock);
1683 snd_azf3328_ctrl_codec_activity(chip, codec->type, 0);
Andreas Mohr02330fb2008-05-16 12:18:29 +02001684
Andreas Mohrda237f32010-12-27 21:17:26 +01001685 if (is_main_mixer_playback_codec) {
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001686 /* now unmute WaveOut */
1687 if (!previously_muted)
Andreas Mohrb5dc20c2011-02-19 00:49:32 +01001688 snd_azf3328_mixer_mute_control_pcm(
1689 chip, 0
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001690 );
1691 }
Andreas Mohr02330fb2008-05-16 12:18:29 +02001692
Andreas Mohr78df6172009-07-12 22:17:54 +02001693 snd_azf3328_dbgcodec("STOPPED %s\n", codec->name);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001694 break;
Andreas Mohrca54bde2006-05-17 11:02:24 +02001695 case SNDRV_PCM_TRIGGER_SUSPEND:
Andreas Mohr78df6172009-07-12 22:17:54 +02001696 snd_azf3328_dbgcodec("SUSPEND %s\n", codec->name);
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001697 /* make sure codec is stopped */
1698 snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS,
1699 snd_azf3328_codec_inw(
1700 codec, IDX_IO_CODEC_DMA_FLAGS
1701 ) & ~DMA_RESUME
1702 );
Andreas Mohrca54bde2006-05-17 11:02:24 +02001703 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001704 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
Takashi Iwai99b359b2005-10-20 18:26:44 +02001705 snd_printk(KERN_ERR "FIXME: SNDRV_PCM_TRIGGER_PAUSE_PUSH NIY!\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001706 break;
1707 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
Takashi Iwai99b359b2005-10-20 18:26:44 +02001708 snd_printk(KERN_ERR "FIXME: SNDRV_PCM_TRIGGER_PAUSE_RELEASE NIY!\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001709 break;
1710 default:
Andreas Mohr78df6172009-07-12 22:17:54 +02001711 snd_printk(KERN_ERR "FIXME: unknown trigger mode!\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001712 return -EINVAL;
1713 }
Andreas Mohr02330fb2008-05-16 12:18:29 +02001714
Linus Torvalds1da177e2005-04-16 15:20:36 -07001715 snd_azf3328_dbgcallleave();
1716 return result;
1717}
1718
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001719static snd_pcm_uframes_t
Andreas Mohrda237f32010-12-27 21:17:26 +01001720snd_azf3328_pcm_pointer(struct snd_pcm_substream *substream
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001721)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001722{
Andreas Mohrda237f32010-12-27 21:17:26 +01001723 const struct snd_azf3328_codec_data *codec =
1724 substream->runtime->private_data;
Andreas Mohr34585592010-12-27 21:17:11 +01001725 unsigned long result;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001726 snd_pcm_uframes_t frmres;
1727
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001728 result = snd_azf3328_codec_inl(codec, IDX_IO_CODEC_DMA_CURRPOS);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001729
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001730 /* calculate offset */
Andreas Mohr34585592010-12-27 21:17:11 +01001731#ifdef QUERY_HARDWARE
1732 result -= snd_azf3328_codec_inl(codec, IDX_IO_CODEC_DMA_START_1);
1733#else
1734 result -= codec->dma_base;
1735#endif
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001736 frmres = bytes_to_frames( substream->runtime, result);
Andreas Mohradf59312010-12-27 21:16:43 +01001737 snd_azf3328_dbgcodec("%08li %s @ 0x%8lx, frames %8ld\n",
1738 jiffies, codec->name, result, frmres);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001739 return frmres;
1740}
1741
Andreas Mohr02330fb2008-05-16 12:18:29 +02001742/******************************************************************/
1743
1744#ifdef SUPPORT_GAMEPORT
1745static inline void
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001746snd_azf3328_gameport_irq_enable(struct snd_azf3328 *chip,
1747 bool enable
1748)
Andreas Mohr02330fb2008-05-16 12:18:29 +02001749{
1750 snd_azf3328_io_reg_setb(
1751 chip->game_io+IDX_GAME_HWCONFIG,
1752 GAME_HWCFG_IRQ_ENABLE,
1753 enable
1754 );
1755}
1756
1757static inline void
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001758snd_azf3328_gameport_legacy_address_enable(struct snd_azf3328 *chip,
1759 bool enable
1760)
Andreas Mohr02330fb2008-05-16 12:18:29 +02001761{
1762 snd_azf3328_io_reg_setb(
1763 chip->game_io+IDX_GAME_HWCONFIG,
1764 GAME_HWCFG_LEGACY_ADDRESS_ENABLE,
1765 enable
1766 );
1767}
1768
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001769static void
1770snd_azf3328_gameport_set_counter_frequency(struct snd_azf3328 *chip,
1771 unsigned int freq_cfg
1772)
Andreas Mohr02330fb2008-05-16 12:18:29 +02001773{
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001774 snd_azf3328_io_reg_setb(
1775 chip->game_io+IDX_GAME_HWCONFIG,
1776 0x02,
1777 (freq_cfg & 1) != 0
1778 );
1779 snd_azf3328_io_reg_setb(
1780 chip->game_io+IDX_GAME_HWCONFIG,
1781 0x04,
1782 (freq_cfg & 2) != 0
1783 );
1784}
1785
1786static inline void
1787snd_azf3328_gameport_axis_circuit_enable(struct snd_azf3328 *chip, bool enable)
1788{
1789 snd_azf3328_ctrl_reg_6AH_update(
Andreas Mohr627d3e72008-06-23 11:50:47 +02001790 chip, IO_6A_SOMETHING2_GAMEPORT, enable
Andreas Mohr02330fb2008-05-16 12:18:29 +02001791 );
1792}
1793
1794static inline void
1795snd_azf3328_gameport_interrupt(struct snd_azf3328 *chip)
1796{
1797 /*
1798 * skeleton handler only
1799 * (we do not want axis reading in interrupt handler - too much load!)
1800 */
1801 snd_azf3328_dbggame("gameport irq\n");
1802
1803 /* this should ACK the gameport IRQ properly, hopefully. */
1804 snd_azf3328_game_inw(chip, IDX_GAME_AXIS_VALUE);
1805}
1806
1807static int
1808snd_azf3328_gameport_open(struct gameport *gameport, int mode)
1809{
1810 struct snd_azf3328 *chip = gameport_get_port_data(gameport);
1811 int res;
1812
1813 snd_azf3328_dbggame("gameport_open, mode %d\n", mode);
1814 switch (mode) {
1815 case GAMEPORT_MODE_COOKED:
1816 case GAMEPORT_MODE_RAW:
1817 res = 0;
1818 break;
1819 default:
1820 res = -1;
1821 break;
1822 }
1823
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001824 snd_azf3328_gameport_set_counter_frequency(chip,
1825 GAME_HWCFG_ADC_COUNTER_FREQ_STD);
Andreas Mohr02330fb2008-05-16 12:18:29 +02001826 snd_azf3328_gameport_axis_circuit_enable(chip, (res == 0));
1827
1828 return res;
1829}
1830
1831static void
1832snd_azf3328_gameport_close(struct gameport *gameport)
1833{
1834 struct snd_azf3328 *chip = gameport_get_port_data(gameport);
1835
1836 snd_azf3328_dbggame("gameport_close\n");
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001837 snd_azf3328_gameport_set_counter_frequency(chip,
1838 GAME_HWCFG_ADC_COUNTER_FREQ_1_200);
Andreas Mohr02330fb2008-05-16 12:18:29 +02001839 snd_azf3328_gameport_axis_circuit_enable(chip, 0);
1840}
1841
1842static int
1843snd_azf3328_gameport_cooked_read(struct gameport *gameport,
1844 int *axes,
1845 int *buttons
1846)
1847{
1848 struct snd_azf3328 *chip = gameport_get_port_data(gameport);
1849 int i;
1850 u8 val;
1851 unsigned long flags;
1852
Takashi Iwaida3cec32008-08-08 17:12:14 +02001853 if (snd_BUG_ON(!chip))
1854 return 0;
Andreas Mohr02330fb2008-05-16 12:18:29 +02001855
1856 spin_lock_irqsave(&chip->reg_lock, flags);
1857 val = snd_azf3328_game_inb(chip, IDX_GAME_LEGACY_COMPATIBLE);
1858 *buttons = (~(val) >> 4) & 0xf;
1859
1860 /* ok, this one is a bit dirty: cooked_read is being polled by a timer,
1861 * thus we're atomic and cannot actively wait in here
1862 * (which would be useful for us since it probably would be better
1863 * to trigger a measurement in here, then wait a short amount of
1864 * time until it's finished, then read values of _this_ measurement).
1865 *
1866 * Thus we simply resort to reading values if they're available already
1867 * and trigger the next measurement.
1868 */
1869
1870 val = snd_azf3328_game_inb(chip, IDX_GAME_AXES_CONFIG);
1871 if (val & GAME_AXES_SAMPLING_READY) {
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001872 for (i = 0; i < ARRAY_SIZE(chip->axes); ++i) {
Andreas Mohr02330fb2008-05-16 12:18:29 +02001873 /* configure the axis to read */
1874 val = (i << 4) | 0x0f;
1875 snd_azf3328_game_outb(chip, IDX_GAME_AXES_CONFIG, val);
1876
1877 chip->axes[i] = snd_azf3328_game_inw(
1878 chip, IDX_GAME_AXIS_VALUE
1879 );
1880 }
1881 }
1882
Andreas Mohradf59312010-12-27 21:16:43 +01001883 /* trigger next sampling of axes, to be evaluated the next time we
Andreas Mohr02330fb2008-05-16 12:18:29 +02001884 * enter this function */
1885
1886 /* for some very, very strange reason we cannot enable
1887 * Measurement Ready monitoring for all axes here,
1888 * at least not when only one joystick connected */
1889 val = 0x03; /* we're able to monitor axes 1 and 2 only */
1890 snd_azf3328_game_outb(chip, IDX_GAME_AXES_CONFIG, val);
1891
1892 snd_azf3328_game_outw(chip, IDX_GAME_AXIS_VALUE, 0xffff);
1893 spin_unlock_irqrestore(&chip->reg_lock, flags);
1894
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001895 for (i = 0; i < ARRAY_SIZE(chip->axes); i++) {
Andreas Mohr02330fb2008-05-16 12:18:29 +02001896 axes[i] = chip->axes[i];
1897 if (axes[i] == 0xffff)
1898 axes[i] = -1;
1899 }
1900
1901 snd_azf3328_dbggame("cooked_read: axes %d %d %d %d buttons %d\n",
1902 axes[0], axes[1], axes[2], axes[3], *buttons
1903 );
1904
1905 return 0;
1906}
1907
1908static int __devinit
1909snd_azf3328_gameport(struct snd_azf3328 *chip, int dev)
1910{
1911 struct gameport *gp;
1912
Andreas Mohr02330fb2008-05-16 12:18:29 +02001913 chip->gameport = gp = gameport_allocate_port();
1914 if (!gp) {
1915 printk(KERN_ERR "azt3328: cannot alloc memory for gameport\n");
1916 return -ENOMEM;
1917 }
1918
1919 gameport_set_name(gp, "AZF3328 Gameport");
1920 gameport_set_phys(gp, "pci%s/gameport0", pci_name(chip->pci));
1921 gameport_set_dev_parent(gp, &chip->pci->dev);
Andreas Mohr627d3e72008-06-23 11:50:47 +02001922 gp->io = chip->game_io;
Andreas Mohr02330fb2008-05-16 12:18:29 +02001923 gameport_set_port_data(gp, chip);
1924
1925 gp->open = snd_azf3328_gameport_open;
1926 gp->close = snd_azf3328_gameport_close;
1927 gp->fuzz = 16; /* seems ok */
1928 gp->cooked_read = snd_azf3328_gameport_cooked_read;
1929
1930 /* DISABLE legacy address: we don't need it! */
1931 snd_azf3328_gameport_legacy_address_enable(chip, 0);
1932
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001933 snd_azf3328_gameport_set_counter_frequency(chip,
1934 GAME_HWCFG_ADC_COUNTER_FREQ_1_200);
Andreas Mohr02330fb2008-05-16 12:18:29 +02001935 snd_azf3328_gameport_axis_circuit_enable(chip, 0);
1936
1937 gameport_register_port(chip->gameport);
1938
1939 return 0;
1940}
1941
1942static void
1943snd_azf3328_gameport_free(struct snd_azf3328 *chip)
1944{
1945 if (chip->gameport) {
1946 gameport_unregister_port(chip->gameport);
1947 chip->gameport = NULL;
1948 }
1949 snd_azf3328_gameport_irq_enable(chip, 0);
1950}
1951#else
1952static inline int
1953snd_azf3328_gameport(struct snd_azf3328 *chip, int dev) { return -ENOSYS; }
1954static inline void
1955snd_azf3328_gameport_free(struct snd_azf3328 *chip) { }
1956static inline void
1957snd_azf3328_gameport_interrupt(struct snd_azf3328 *chip)
1958{
1959 printk(KERN_WARNING "huh, game port IRQ occurred!?\n");
1960}
1961#endif /* SUPPORT_GAMEPORT */
1962
1963/******************************************************************/
1964
Andreas Mohr627d3e72008-06-23 11:50:47 +02001965static inline void
1966snd_azf3328_irq_log_unknown_type(u8 which)
1967{
Andreas Mohr78df6172009-07-12 22:17:54 +02001968 snd_azf3328_dbgcodec(
Andreas Mohr627d3e72008-06-23 11:50:47 +02001969 "azt3328: unknown IRQ type (%x) occurred, please report!\n",
1970 which
1971 );
1972}
1973
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001974static inline void
Andreas Mohrda237f32010-12-27 21:17:26 +01001975snd_azf3328_pcm_interrupt(const struct snd_azf3328_codec_data *first_codec,
1976 u8 status
1977)
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001978{
1979 u8 which;
1980 enum snd_azf3328_codec_type codec_type;
Andreas Mohrda237f32010-12-27 21:17:26 +01001981 const struct snd_azf3328_codec_data *codec = first_codec;
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001982
1983 for (codec_type = AZF_CODEC_PLAYBACK;
1984 codec_type <= AZF_CODEC_I2S_OUT;
Andreas Mohrda237f32010-12-27 21:17:26 +01001985 ++codec_type, ++codec) {
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001986
1987 /* skip codec if there's no interrupt for it */
1988 if (!(status & (1 << codec_type)))
1989 continue;
1990
Andreas Mohrda237f32010-12-27 21:17:26 +01001991 spin_lock(codec->lock);
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001992 which = snd_azf3328_codec_inb(codec, IDX_IO_CODEC_IRQTYPE);
1993 /* ack all IRQ types immediately */
1994 snd_azf3328_codec_outb(codec, IDX_IO_CODEC_IRQTYPE, which);
Andreas Mohrda237f32010-12-27 21:17:26 +01001995 spin_unlock(codec->lock);
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001996
Andreas Mohrda237f32010-12-27 21:17:26 +01001997 if (codec->substream) {
Andreas Mohr78df6172009-07-12 22:17:54 +02001998 snd_pcm_period_elapsed(codec->substream);
1999 snd_azf3328_dbgcodec("%s period done (#%x), @ %x\n",
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002000 codec->name,
2001 which,
2002 snd_azf3328_codec_inl(
2003 codec, IDX_IO_CODEC_DMA_CURRPOS
2004 )
2005 );
2006 } else
2007 printk(KERN_WARNING "azt3328: irq handler problem!\n");
2008 if (which & IRQ_SOMETHING)
2009 snd_azf3328_irq_log_unknown_type(which);
2010 }
2011}
2012
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002013static irqreturn_t
David Howells7d12e782006-10-05 14:55:46 +01002014snd_azf3328_interrupt(int irq, void *dev_id)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002015{
Takashi Iwai95de7762005-11-17 15:02:42 +01002016 struct snd_azf3328 *chip = dev_id;
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002017 u8 status;
Andreas Mohr78df6172009-07-12 22:17:54 +02002018#if DEBUG_CODEC
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002019 static unsigned long irq_count;
Andreas Mohr02330fb2008-05-16 12:18:29 +02002020#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07002021
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002022 status = snd_azf3328_ctrl_inb(chip, IDX_IO_IRQSTATUS);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002023
2024 /* fast path out, to ease interrupt sharing */
Andreas Mohr02330fb2008-05-16 12:18:29 +02002025 if (!(status &
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002026 (IRQ_PLAYBACK|IRQ_RECORDING|IRQ_I2S_OUT
2027 |IRQ_GAMEPORT|IRQ_MPU401|IRQ_TIMER)
Andreas Mohr02330fb2008-05-16 12:18:29 +02002028 ))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002029 return IRQ_NONE; /* must be interrupt for another device */
2030
Andreas Mohr78df6172009-07-12 22:17:54 +02002031 snd_azf3328_dbgcodec(
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002032 "irq_count %ld! IDX_IO_IRQSTATUS %04x\n",
Andreas Mohr627d3e72008-06-23 11:50:47 +02002033 irq_count++ /* debug-only */,
Andreas Mohr627d3e72008-06-23 11:50:47 +02002034 status
2035 );
Andreas Mohr02330fb2008-05-16 12:18:29 +02002036
Andreas Mohre2f87262006-05-17 11:04:19 +02002037 if (status & IRQ_TIMER) {
Andreas Mohr78df6172009-07-12 22:17:54 +02002038 /* snd_azf3328_dbgcodec("timer %ld\n",
Andreas Mohr02330fb2008-05-16 12:18:29 +02002039 snd_azf3328_codec_inl(chip, IDX_IO_TIMER_VALUE)
2040 & TIMER_VALUE_MASK
2041 ); */
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002042 if (chip->timer)
2043 snd_timer_interrupt(chip->timer, chip->timer->sticks);
2044 /* ACK timer */
2045 spin_lock(&chip->reg_lock);
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002046 snd_azf3328_ctrl_outb(chip, IDX_IO_TIMER_VALUE + 3, 0x07);
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002047 spin_unlock(&chip->reg_lock);
Andreas Mohr78df6172009-07-12 22:17:54 +02002048 snd_azf3328_dbgcodec("azt3328: timer IRQ\n");
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002049 }
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002050
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002051 if (status & (IRQ_PLAYBACK|IRQ_RECORDING|IRQ_I2S_OUT))
Andreas Mohrda237f32010-12-27 21:17:26 +01002052 snd_azf3328_pcm_interrupt(chip->codecs, status);
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002053
Andreas Mohr02330fb2008-05-16 12:18:29 +02002054 if (status & IRQ_GAMEPORT)
2055 snd_azf3328_gameport_interrupt(chip);
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002056
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002057 /* MPU401 has less critical IRQ requirements
2058 * than timer and playback/recording, right? */
Andreas Mohre2f87262006-05-17 11:04:19 +02002059 if (status & IRQ_MPU401) {
David Howells7d12e782006-10-05 14:55:46 +01002060 snd_mpu401_uart_interrupt(irq, chip->rmidi->private_data);
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002061
2062 /* hmm, do we have to ack the IRQ here somehow?
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002063 * If so, then I don't know how yet... */
Andreas Mohr78df6172009-07-12 22:17:54 +02002064 snd_azf3328_dbgcodec("azt3328: MPU401 IRQ\n");
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002065 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002066 return IRQ_HANDLED;
2067}
2068
2069/*****************************************************************/
2070
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002071/* as long as we think we have identical snd_pcm_hardware parameters
2072 for playback, capture and i2s out, we can use the same physical struct
2073 since the struct is simply being copied into a member.
2074*/
2075static const struct snd_pcm_hardware snd_azf3328_hardware =
Linus Torvalds1da177e2005-04-16 15:20:36 -07002076{
2077 /* FIXME!! Correct? */
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002078 .info = SNDRV_PCM_INFO_MMAP |
2079 SNDRV_PCM_INFO_INTERLEAVED |
2080 SNDRV_PCM_INFO_MMAP_VALID,
2081 .formats = SNDRV_PCM_FMTBIT_S8 |
2082 SNDRV_PCM_FMTBIT_U8 |
2083 SNDRV_PCM_FMTBIT_S16_LE |
2084 SNDRV_PCM_FMTBIT_U16_LE,
2085 .rates = SNDRV_PCM_RATE_5512 |
2086 SNDRV_PCM_RATE_8000_48000 |
2087 SNDRV_PCM_RATE_KNOT,
Andreas Mohr02330fb2008-05-16 12:18:29 +02002088 .rate_min = AZF_FREQ_4000,
2089 .rate_max = AZF_FREQ_66200,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002090 .channels_min = 1,
2091 .channels_max = 2,
Andreas Mohr79741502010-11-21 12:09:32 +01002092 .buffer_bytes_max = (64*1024),
2093 .period_bytes_min = 1024,
2094 .period_bytes_max = (32*1024),
2095 /* We simply have two DMA areas (instead of a list of descriptors
2096 such as other cards); I believe that this is a fixed hardware
2097 attribute and there isn't much driver magic to be done to expand it.
2098 Thus indicate that we have at least and at most 2 periods. */
2099 .periods_min = 2,
2100 .periods_max = 2,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002101 /* FIXME: maybe that card actually has a FIFO?
2102 * Hmm, it seems newer revisions do have one, but we still don't know
2103 * its size... */
2104 .fifo_size = 0,
2105};
2106
Linus Torvalds1da177e2005-04-16 15:20:36 -07002107
2108static unsigned int snd_azf3328_fixed_rates[] = {
Andreas Mohr02330fb2008-05-16 12:18:29 +02002109 AZF_FREQ_4000,
2110 AZF_FREQ_4800,
2111 AZF_FREQ_5512,
2112 AZF_FREQ_6620,
2113 AZF_FREQ_8000,
2114 AZF_FREQ_9600,
2115 AZF_FREQ_11025,
2116 AZF_FREQ_13240,
2117 AZF_FREQ_16000,
2118 AZF_FREQ_22050,
2119 AZF_FREQ_32000,
2120 AZF_FREQ_44100,
2121 AZF_FREQ_48000,
2122 AZF_FREQ_66200
2123};
2124
Takashi Iwai95de7762005-11-17 15:02:42 +01002125static struct snd_pcm_hw_constraint_list snd_azf3328_hw_constraints_rates = {
Andreas Mohr02330fb2008-05-16 12:18:29 +02002126 .count = ARRAY_SIZE(snd_azf3328_fixed_rates),
Linus Torvalds1da177e2005-04-16 15:20:36 -07002127 .list = snd_azf3328_fixed_rates,
2128 .mask = 0,
2129};
2130
2131/*****************************************************************/
2132
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002133static int
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002134snd_azf3328_pcm_open(struct snd_pcm_substream *substream,
2135 enum snd_azf3328_codec_type codec_type
2136)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002137{
Takashi Iwai95de7762005-11-17 15:02:42 +01002138 struct snd_azf3328 *chip = snd_pcm_substream_chip(substream);
2139 struct snd_pcm_runtime *runtime = substream->runtime;
Andreas Mohrda237f32010-12-27 21:17:26 +01002140 struct snd_azf3328_codec_data *codec = &chip->codecs[codec_type];
Linus Torvalds1da177e2005-04-16 15:20:36 -07002141
2142 snd_azf3328_dbgcallenter();
Andreas Mohrda237f32010-12-27 21:17:26 +01002143 codec->substream = substream;
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002144
2145 /* same parameters for all our codecs - at least we think so... */
2146 runtime->hw = snd_azf3328_hardware;
2147
Linus Torvalds1da177e2005-04-16 15:20:36 -07002148 snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
2149 &snd_azf3328_hw_constraints_rates);
Andreas Mohrda237f32010-12-27 21:17:26 +01002150 runtime->private_data = codec;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002151 snd_azf3328_dbgcallleave();
2152 return 0;
2153}
2154
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002155static int
Andreas Mohrda237f32010-12-27 21:17:26 +01002156snd_azf3328_pcm_playback_open(struct snd_pcm_substream *substream)
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002157{
2158 return snd_azf3328_pcm_open(substream, AZF_CODEC_PLAYBACK);
2159}
2160
2161static int
Andreas Mohrda237f32010-12-27 21:17:26 +01002162snd_azf3328_pcm_capture_open(struct snd_pcm_substream *substream)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002163{
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002164 return snd_azf3328_pcm_open(substream, AZF_CODEC_CAPTURE);
2165}
2166
2167static int
Andreas Mohrda237f32010-12-27 21:17:26 +01002168snd_azf3328_pcm_i2s_out_open(struct snd_pcm_substream *substream)
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002169{
2170 return snd_azf3328_pcm_open(substream, AZF_CODEC_I2S_OUT);
2171}
2172
2173static int
Andreas Mohrda237f32010-12-27 21:17:26 +01002174snd_azf3328_pcm_close(struct snd_pcm_substream *substream
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002175)
2176{
Andreas Mohrda237f32010-12-27 21:17:26 +01002177 struct snd_azf3328_codec_data *codec =
2178 substream->runtime->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002179
2180 snd_azf3328_dbgcallenter();
Andreas Mohrda237f32010-12-27 21:17:26 +01002181 codec->substream = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002182 snd_azf3328_dbgcallleave();
2183 return 0;
2184}
2185
Linus Torvalds1da177e2005-04-16 15:20:36 -07002186/******************************************************************/
2187
Takashi Iwai95de7762005-11-17 15:02:42 +01002188static struct snd_pcm_ops snd_azf3328_playback_ops = {
Andreas Mohrda237f32010-12-27 21:17:26 +01002189 .open = snd_azf3328_pcm_playback_open,
2190 .close = snd_azf3328_pcm_close,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002191 .ioctl = snd_pcm_lib_ioctl,
2192 .hw_params = snd_azf3328_hw_params,
2193 .hw_free = snd_azf3328_hw_free,
Andreas Mohrda237f32010-12-27 21:17:26 +01002194 .prepare = snd_azf3328_pcm_prepare,
2195 .trigger = snd_azf3328_pcm_trigger,
2196 .pointer = snd_azf3328_pcm_pointer
Linus Torvalds1da177e2005-04-16 15:20:36 -07002197};
2198
Takashi Iwai95de7762005-11-17 15:02:42 +01002199static struct snd_pcm_ops snd_azf3328_capture_ops = {
Andreas Mohrda237f32010-12-27 21:17:26 +01002200 .open = snd_azf3328_pcm_capture_open,
2201 .close = snd_azf3328_pcm_close,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002202 .ioctl = snd_pcm_lib_ioctl,
2203 .hw_params = snd_azf3328_hw_params,
2204 .hw_free = snd_azf3328_hw_free,
Andreas Mohrda237f32010-12-27 21:17:26 +01002205 .prepare = snd_azf3328_pcm_prepare,
2206 .trigger = snd_azf3328_pcm_trigger,
2207 .pointer = snd_azf3328_pcm_pointer
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002208};
2209
2210static struct snd_pcm_ops snd_azf3328_i2s_out_ops = {
Andreas Mohrda237f32010-12-27 21:17:26 +01002211 .open = snd_azf3328_pcm_i2s_out_open,
2212 .close = snd_azf3328_pcm_close,
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002213 .ioctl = snd_pcm_lib_ioctl,
2214 .hw_params = snd_azf3328_hw_params,
2215 .hw_free = snd_azf3328_hw_free,
Andreas Mohrda237f32010-12-27 21:17:26 +01002216 .prepare = snd_azf3328_pcm_prepare,
2217 .trigger = snd_azf3328_pcm_trigger,
2218 .pointer = snd_azf3328_pcm_pointer
Linus Torvalds1da177e2005-04-16 15:20:36 -07002219};
2220
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002221static int __devinit
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002222snd_azf3328_pcm(struct snd_azf3328 *chip)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002223{
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002224enum { AZF_PCMDEV_STD, AZF_PCMDEV_I2S_OUT, NUM_AZF_PCMDEVS }; /* pcm devices */
2225
Takashi Iwai95de7762005-11-17 15:02:42 +01002226 struct snd_pcm *pcm;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002227 int err;
2228
2229 snd_azf3328_dbgcallenter();
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002230
2231 err = snd_pcm_new(chip->card, "AZF3328 DSP", AZF_PCMDEV_STD,
2232 1, 1, &pcm);
2233 if (err < 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002234 return err;
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002235 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
2236 &snd_azf3328_playback_ops);
2237 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
2238 &snd_azf3328_capture_ops);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002239
2240 pcm->private_data = chip;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002241 pcm->info_flags = 0;
2242 strcpy(pcm->name, chip->card->shortname);
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002243 /* same pcm object for playback/capture (see snd_pcm_new() above) */
2244 chip->pcm[AZF_CODEC_PLAYBACK] = pcm;
2245 chip->pcm[AZF_CODEC_CAPTURE] = pcm;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002246
2247 snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002248 snd_dma_pci_data(chip->pci),
2249 64*1024, 64*1024);
2250
2251 err = snd_pcm_new(chip->card, "AZF3328 I2S OUT", AZF_PCMDEV_I2S_OUT,
2252 1, 0, &pcm);
2253 if (err < 0)
2254 return err;
2255 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
2256 &snd_azf3328_i2s_out_ops);
2257
2258 pcm->private_data = chip;
2259 pcm->info_flags = 0;
2260 strcpy(pcm->name, chip->card->shortname);
2261 chip->pcm[AZF_CODEC_I2S_OUT] = pcm;
2262
2263 snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
2264 snd_dma_pci_data(chip->pci),
2265 64*1024, 64*1024);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002266
2267 snd_azf3328_dbgcallleave();
2268 return 0;
2269}
2270
2271/******************************************************************/
2272
Andreas Mohr02330fb2008-05-16 12:18:29 +02002273/*** NOTE: the physical timer resolution actually is 1024000 ticks per second
2274 *** (probably derived from main crystal via a divider of 24),
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002275 *** but announcing those attributes to user-space would make programs
2276 *** configure the timer to a 1 tick value, resulting in an absolutely fatal
2277 *** timer IRQ storm.
2278 *** Thus I chose to announce a down-scaled virtual timer to the outside and
2279 *** calculate real timer countdown values internally.
2280 *** (the scale factor can be set via module parameter "seqtimer_scaling").
2281 ***/
2282
2283static int
Takashi Iwai95de7762005-11-17 15:02:42 +01002284snd_azf3328_timer_start(struct snd_timer *timer)
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002285{
Takashi Iwai95de7762005-11-17 15:02:42 +01002286 struct snd_azf3328 *chip;
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002287 unsigned long flags;
2288 unsigned int delay;
2289
2290 snd_azf3328_dbgcallenter();
2291 chip = snd_timer_chip(timer);
2292 delay = ((timer->sticks * seqtimer_scaling) - 1) & TIMER_VALUE_MASK;
Andreas Mohre2f87262006-05-17 11:04:19 +02002293 if (delay < 49) {
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002294 /* uhoh, that's not good, since user-space won't know about
2295 * this timing tweak
2296 * (we need to do it to avoid a lockup, though) */
2297
2298 snd_azf3328_dbgtimer("delay was too low (%d)!\n", delay);
2299 delay = 49; /* minimum time is 49 ticks */
2300 }
Andreas Mohradf59312010-12-27 21:16:43 +01002301 snd_azf3328_dbgtimer("setting timer countdown value %d\n", delay);
Andreas Mohr02330fb2008-05-16 12:18:29 +02002302 delay |= TIMER_COUNTDOWN_ENABLE | TIMER_IRQ_ENABLE;
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002303 spin_lock_irqsave(&chip->reg_lock, flags);
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002304 snd_azf3328_ctrl_outl(chip, IDX_IO_TIMER_VALUE, delay);
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002305 spin_unlock_irqrestore(&chip->reg_lock, flags);
2306 snd_azf3328_dbgcallleave();
2307 return 0;
2308}
2309
2310static int
Takashi Iwai95de7762005-11-17 15:02:42 +01002311snd_azf3328_timer_stop(struct snd_timer *timer)
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002312{
Takashi Iwai95de7762005-11-17 15:02:42 +01002313 struct snd_azf3328 *chip;
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002314 unsigned long flags;
2315
2316 snd_azf3328_dbgcallenter();
2317 chip = snd_timer_chip(timer);
2318 spin_lock_irqsave(&chip->reg_lock, flags);
2319 /* disable timer countdown and interrupt */
Andreas Mohr79741502010-11-21 12:09:32 +01002320 /* Hmm, should we write TIMER_IRQ_ACK here?
2321 YES indeed, otherwise a rogue timer operation - which prompts
2322 ALSA(?) to call repeated stop() in vain, but NOT start() -
2323 will never end (value 0x03 is kept shown in control byte).
2324 Simply manually poking 0x04 _once_ immediately successfully stops
2325 the hardware/ALSA interrupt activity. */
2326 snd_azf3328_ctrl_outb(chip, IDX_IO_TIMER_VALUE + 3, 0x04);
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002327 spin_unlock_irqrestore(&chip->reg_lock, flags);
2328 snd_azf3328_dbgcallleave();
2329 return 0;
2330}
2331
2332
2333static int
Takashi Iwai95de7762005-11-17 15:02:42 +01002334snd_azf3328_timer_precise_resolution(struct snd_timer *timer,
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002335 unsigned long *num, unsigned long *den)
2336{
2337 snd_azf3328_dbgcallenter();
2338 *num = 1;
2339 *den = 1024000 / seqtimer_scaling;
2340 snd_azf3328_dbgcallleave();
2341 return 0;
2342}
2343
Takashi Iwai95de7762005-11-17 15:02:42 +01002344static struct snd_timer_hardware snd_azf3328_timer_hw = {
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002345 .flags = SNDRV_TIMER_HW_AUTO,
2346 .resolution = 977, /* 1000000/1024000 = 0.9765625us */
2347 .ticks = 1024000, /* max tick count, defined by the value register; actually it's not 1024000, but 1048576, but we don't care */
2348 .start = snd_azf3328_timer_start,
2349 .stop = snd_azf3328_timer_stop,
2350 .precise_resolution = snd_azf3328_timer_precise_resolution,
2351};
2352
2353static int __devinit
Takashi Iwai95de7762005-11-17 15:02:42 +01002354snd_azf3328_timer(struct snd_azf3328 *chip, int device)
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002355{
Takashi Iwai95de7762005-11-17 15:02:42 +01002356 struct snd_timer *timer = NULL;
2357 struct snd_timer_id tid;
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002358 int err;
2359
2360 snd_azf3328_dbgcallenter();
2361 tid.dev_class = SNDRV_TIMER_CLASS_CARD;
2362 tid.dev_sclass = SNDRV_TIMER_SCLASS_NONE;
2363 tid.card = chip->card->number;
2364 tid.device = device;
2365 tid.subdevice = 0;
2366
2367 snd_azf3328_timer_hw.resolution *= seqtimer_scaling;
2368 snd_azf3328_timer_hw.ticks /= seqtimer_scaling;
Andreas Mohr02330fb2008-05-16 12:18:29 +02002369
2370 err = snd_timer_new(chip->card, "AZF3328", &tid, &timer);
2371 if (err < 0)
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002372 goto out;
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002373
2374 strcpy(timer->name, "AZF3328 timer");
2375 timer->private_data = chip;
2376 timer->hw = snd_azf3328_timer_hw;
2377
2378 chip->timer = timer;
2379
Andreas Mohr02330fb2008-05-16 12:18:29 +02002380 snd_azf3328_timer_stop(timer);
2381
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002382 err = 0;
2383
2384out:
2385 snd_azf3328_dbgcallleave();
2386 return err;
2387}
2388
2389/******************************************************************/
2390
Andreas Mohr02330fb2008-05-16 12:18:29 +02002391static int
2392snd_azf3328_free(struct snd_azf3328 *chip)
2393{
2394 if (chip->irq < 0)
2395 goto __end_hw;
2396
Andreas Mohrb5dc20c2011-02-19 00:49:32 +01002397 snd_azf3328_mixer_reset(chip);
Andreas Mohr02330fb2008-05-16 12:18:29 +02002398
2399 snd_azf3328_timer_stop(chip->timer);
2400 snd_azf3328_gameport_free(chip);
2401
2402 if (chip->irq >= 0)
2403 synchronize_irq(chip->irq);
2404__end_hw:
2405 if (chip->irq >= 0)
2406 free_irq(chip->irq, chip);
2407 pci_release_regions(chip->pci);
2408 pci_disable_device(chip->pci);
2409
2410 kfree(chip);
2411 return 0;
2412}
2413
2414static int
2415snd_azf3328_dev_free(struct snd_device *device)
2416{
2417 struct snd_azf3328 *chip = device->device_data;
2418 return snd_azf3328_free(chip);
2419}
2420
Linus Torvalds1da177e2005-04-16 15:20:36 -07002421#if 0
2422/* check whether a bit can be modified */
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002423static void
Andreas Mohr02330fb2008-05-16 12:18:29 +02002424snd_azf3328_test_bit(unsigned unsigned reg, int bit)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002425{
2426 unsigned char val, valoff, valon;
2427
2428 val = inb(reg);
2429
2430 outb(val & ~(1 << bit), reg);
2431 valoff = inb(reg);
2432
2433 outb(val|(1 << bit), reg);
2434 valon = inb(reg);
Andreas Mohr02330fb2008-05-16 12:18:29 +02002435
Linus Torvalds1da177e2005-04-16 15:20:36 -07002436 outb(val, reg);
2437
Andreas Mohr78df6172009-07-12 22:17:54 +02002438 printk(KERN_DEBUG "reg %04x bit %d: %02x %02x %02x\n",
Andreas Mohr02330fb2008-05-16 12:18:29 +02002439 reg, bit, val, valoff, valon
2440 );
Linus Torvalds1da177e2005-04-16 15:20:36 -07002441}
2442#endif
2443
Andreas Mohr02330fb2008-05-16 12:18:29 +02002444static inline void
Takashi Iwai95de7762005-11-17 15:02:42 +01002445snd_azf3328_debug_show_ports(const struct snd_azf3328 *chip)
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002446{
Andreas Mohr02330fb2008-05-16 12:18:29 +02002447#if DEBUG_MISC
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002448 u16 tmp;
2449
Andreas Mohr02330fb2008-05-16 12:18:29 +02002450 snd_azf3328_dbgmisc(
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002451 "ctrl_io 0x%lx, game_io 0x%lx, mpu_io 0x%lx, "
Andreas Mohr02330fb2008-05-16 12:18:29 +02002452 "opl3_io 0x%lx, mixer_io 0x%lx, irq %d\n",
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002453 chip->ctrl_io, chip->game_io, chip->mpu_io,
Andreas Mohr02330fb2008-05-16 12:18:29 +02002454 chip->opl3_io, chip->mixer_io, chip->irq
2455 );
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002456
Andreas Mohr02330fb2008-05-16 12:18:29 +02002457 snd_azf3328_dbgmisc("game %02x %02x %02x %02x %02x %02x\n",
2458 snd_azf3328_game_inb(chip, 0),
2459 snd_azf3328_game_inb(chip, 1),
2460 snd_azf3328_game_inb(chip, 2),
2461 snd_azf3328_game_inb(chip, 3),
2462 snd_azf3328_game_inb(chip, 4),
2463 snd_azf3328_game_inb(chip, 5)
2464 );
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002465
Andreas Mohr02330fb2008-05-16 12:18:29 +02002466 for (tmp = 0; tmp < 0x07; tmp += 1)
2467 snd_azf3328_dbgmisc("mpu_io 0x%04x\n", inb(chip->mpu_io + tmp));
2468
2469 for (tmp = 0; tmp <= 0x07; tmp += 1)
2470 snd_azf3328_dbgmisc("0x%02x: game200 0x%04x, game208 0x%04x\n",
2471 tmp, inb(0x200 + tmp), inb(0x208 + tmp));
2472
2473 for (tmp = 0; tmp <= 0x01; tmp += 1)
2474 snd_azf3328_dbgmisc(
2475 "0x%02x: mpu300 0x%04x, mpu310 0x%04x, mpu320 0x%04x, "
2476 "mpu330 0x%04x opl388 0x%04x opl38c 0x%04x\n",
2477 tmp,
2478 inb(0x300 + tmp),
2479 inb(0x310 + tmp),
2480 inb(0x320 + tmp),
2481 inb(0x330 + tmp),
2482 inb(0x388 + tmp),
2483 inb(0x38c + tmp)
2484 );
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002485
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002486 for (tmp = 0; tmp < AZF_IO_SIZE_CTRL; tmp += 2)
2487 snd_azf3328_dbgmisc("ctrl 0x%02x: 0x%04x\n",
2488 tmp, snd_azf3328_ctrl_inw(chip, tmp)
Andreas Mohr02330fb2008-05-16 12:18:29 +02002489 );
Andreas Mohre24a1212007-03-26 12:49:45 +02002490
2491 for (tmp = 0; tmp < AZF_IO_SIZE_MIXER; tmp += 2)
Andreas Mohr02330fb2008-05-16 12:18:29 +02002492 snd_azf3328_dbgmisc("mixer 0x%02x: 0x%04x\n",
2493 tmp, snd_azf3328_mixer_inw(chip, tmp)
2494 );
2495#endif /* DEBUG_MISC */
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002496}
2497
2498static int __devinit
Takashi Iwai95de7762005-11-17 15:02:42 +01002499snd_azf3328_create(struct snd_card *card,
Andreas Mohr02330fb2008-05-16 12:18:29 +02002500 struct pci_dev *pci,
2501 unsigned long device_type,
2502 struct snd_azf3328 **rchip)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002503{
Takashi Iwai95de7762005-11-17 15:02:42 +01002504 struct snd_azf3328 *chip;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002505 int err;
Takashi Iwai95de7762005-11-17 15:02:42 +01002506 static struct snd_device_ops ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002507 .dev_free = snd_azf3328_dev_free,
2508 };
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002509 u8 dma_init;
2510 enum snd_azf3328_codec_type codec_type;
Andreas Mohrda237f32010-12-27 21:17:26 +01002511 struct snd_azf3328_codec_data *codec_setup;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002512
2513 *rchip = NULL;
2514
Andreas Mohr02330fb2008-05-16 12:18:29 +02002515 err = pci_enable_device(pci);
2516 if (err < 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002517 return err;
2518
Takashi Iwaie560d8d2005-09-09 14:21:46 +02002519 chip = kzalloc(sizeof(*chip), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002520 if (chip == NULL) {
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002521 err = -ENOMEM;
2522 goto out_err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002523 }
2524 spin_lock_init(&chip->reg_lock);
2525 chip->card = card;
2526 chip->pci = pci;
2527 chip->irq = -1;
2528
2529 /* check if we can restrict PCI DMA transfers to 24 bits */
Yang Hongyang2f4f27d2009-04-06 19:01:18 -07002530 if (pci_set_dma_mask(pci, DMA_BIT_MASK(24)) < 0 ||
2531 pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(24)) < 0) {
Andreas Mohr02330fb2008-05-16 12:18:29 +02002532 snd_printk(KERN_ERR "architecture does not support "
2533 "24bit PCI busmaster DMA\n"
2534 );
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002535 err = -ENXIO;
2536 goto out_err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002537 }
2538
Andreas Mohr02330fb2008-05-16 12:18:29 +02002539 err = pci_request_regions(pci, "Aztech AZF3328");
2540 if (err < 0)
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002541 goto out_err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002542
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002543 chip->ctrl_io = pci_resource_start(pci, 0);
Andreas Mohr02330fb2008-05-16 12:18:29 +02002544 chip->game_io = pci_resource_start(pci, 1);
2545 chip->mpu_io = pci_resource_start(pci, 2);
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002546 chip->opl3_io = pci_resource_start(pci, 3);
Andreas Mohr02330fb2008-05-16 12:18:29 +02002547 chip->mixer_io = pci_resource_start(pci, 4);
2548
Andreas Mohr9fd8d362010-12-27 21:17:00 +01002549 codec_setup = &chip->codecs[AZF_CODEC_PLAYBACK];
2550 codec_setup->io_base = chip->ctrl_io + AZF_IO_OFFS_CODEC_PLAYBACK;
Andreas Mohrda237f32010-12-27 21:17:26 +01002551 codec_setup->lock = &chip->reg_lock;
2552 codec_setup->type = AZF_CODEC_PLAYBACK;
Andreas Mohr9fd8d362010-12-27 21:17:00 +01002553 codec_setup->name = "PLAYBACK";
2554
2555 codec_setup = &chip->codecs[AZF_CODEC_CAPTURE];
2556 codec_setup->io_base = chip->ctrl_io + AZF_IO_OFFS_CODEC_CAPTURE;
Andreas Mohrda237f32010-12-27 21:17:26 +01002557 codec_setup->lock = &chip->reg_lock;
2558 codec_setup->type = AZF_CODEC_CAPTURE;
Andreas Mohr9fd8d362010-12-27 21:17:00 +01002559 codec_setup->name = "CAPTURE";
2560
2561 codec_setup = &chip->codecs[AZF_CODEC_I2S_OUT];
2562 codec_setup->io_base = chip->ctrl_io + AZF_IO_OFFS_CODEC_I2S_OUT;
Andreas Mohrda237f32010-12-27 21:17:26 +01002563 codec_setup->lock = &chip->reg_lock;
2564 codec_setup->type = AZF_CODEC_I2S_OUT;
Andreas Mohr9fd8d362010-12-27 21:17:00 +01002565 codec_setup->name = "I2S_OUT";
Linus Torvalds1da177e2005-04-16 15:20:36 -07002566
Takashi Iwai437a5a42006-11-21 12:14:23 +01002567 if (request_irq(pci->irq, snd_azf3328_interrupt,
2568 IRQF_SHARED, card->shortname, chip)) {
Takashi Iwai99b359b2005-10-20 18:26:44 +02002569 snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002570 err = -EBUSY;
2571 goto out_err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002572 }
2573 chip->irq = pci->irq;
2574 pci_set_master(pci);
2575 synchronize_irq(chip->irq);
2576
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002577 snd_azf3328_debug_show_ports(chip);
Andreas Mohr02330fb2008-05-16 12:18:29 +02002578
2579 err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
2580 if (err < 0)
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002581 goto out_err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002582
2583 /* create mixer interface & switches */
Andreas Mohr02330fb2008-05-16 12:18:29 +02002584 err = snd_azf3328_mixer_new(chip);
2585 if (err < 0)
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002586 goto out_err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002587
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002588 /* standard codec init stuff */
2589 /* default DMA init value */
2590 dma_init = DMA_RUN_SOMETHING2|DMA_EPILOGUE_SOMETHING|DMA_SOMETHING_ELSE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002591
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002592 for (codec_type = AZF_CODEC_PLAYBACK;
2593 codec_type <= AZF_CODEC_I2S_OUT; ++codec_type) {
2594 struct snd_azf3328_codec_data *codec =
2595 &chip->codecs[codec_type];
Linus Torvalds1da177e2005-04-16 15:20:36 -07002596
Andreas Mohradf59312010-12-27 21:16:43 +01002597 /* shutdown codecs to reduce power / noise */
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002598 /* have ...ctrl_codec_activity() act properly */
2599 codec->running = 1;
2600 snd_azf3328_ctrl_codec_activity(chip, codec_type, 0);
2601
Andreas Mohrda237f32010-12-27 21:17:26 +01002602 spin_lock_irq(codec->lock);
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002603 snd_azf3328_codec_outb(codec, IDX_IO_CODEC_DMA_FLAGS,
2604 dma_init);
Andreas Mohrda237f32010-12-27 21:17:26 +01002605 spin_unlock_irq(codec->lock);
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002606 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002607
2608 snd_card_set_dev(card, &pci->dev);
2609
2610 *rchip = chip;
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002611
2612 err = 0;
2613 goto out;
2614
2615out_err:
2616 if (chip)
2617 snd_azf3328_free(chip);
2618 pci_disable_device(pci);
2619
2620out:
2621 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002622}
2623
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002624static int __devinit
2625snd_azf3328_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002626{
2627 static int dev;
Takashi Iwai95de7762005-11-17 15:02:42 +01002628 struct snd_card *card;
2629 struct snd_azf3328 *chip;
2630 struct snd_opl3 *opl3;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002631 int err;
2632
2633 snd_azf3328_dbgcallenter();
2634 if (dev >= SNDRV_CARDS)
2635 return -ENODEV;
2636 if (!enable[dev]) {
2637 dev++;
2638 return -ENOENT;
2639 }
2640
Takashi Iwaie58de7b2008-12-28 16:44:30 +01002641 err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
2642 if (err < 0)
2643 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002644
2645 strcpy(card->driver, "AZF3328");
2646 strcpy(card->shortname, "Aztech AZF3328 (PCI168)");
2647
Andreas Mohr02330fb2008-05-16 12:18:29 +02002648 err = snd_azf3328_create(card, pci, pci_id->driver_data, &chip);
2649 if (err < 0)
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002650 goto out_err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002651
Andreas Mohrca54bde2006-05-17 11:02:24 +02002652 card->private_data = chip;
2653
Andreas Mohr78df6172009-07-12 22:17:54 +02002654 /* chose to use MPU401_HW_AZT2320 ID instead of MPU401_HW_MPU401,
2655 since our hardware ought to be similar, thus use same ID. */
Andreas Mohr02330fb2008-05-16 12:18:29 +02002656 err = snd_mpu401_uart_new(
Andreas Mohr78df6172009-07-12 22:17:54 +02002657 card, 0,
2658 MPU401_HW_AZT2320, chip->mpu_io, MPU401_INFO_INTEGRATED,
Andreas Mohr02330fb2008-05-16 12:18:29 +02002659 pci->irq, 0, &chip->rmidi
2660 );
2661 if (err < 0) {
2662 snd_printk(KERN_ERR "azf3328: no MPU-401 device at 0x%lx?\n",
2663 chip->mpu_io
2664 );
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002665 goto out_err;
2666 }
2667
Andreas Mohr02330fb2008-05-16 12:18:29 +02002668 err = snd_azf3328_timer(chip, 0);
2669 if (err < 0)
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002670 goto out_err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002671
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002672 err = snd_azf3328_pcm(chip);
Andreas Mohr02330fb2008-05-16 12:18:29 +02002673 if (err < 0)
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002674 goto out_err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002675
Andreas Mohr02330fb2008-05-16 12:18:29 +02002676 if (snd_opl3_create(card, chip->opl3_io, chip->opl3_io+2,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002677 OPL3_HW_AUTO, 1, &opl3) < 0) {
Takashi Iwai99b359b2005-10-20 18:26:44 +02002678 snd_printk(KERN_ERR "azf3328: no OPL3 device at 0x%lx-0x%lx?\n",
Andreas Mohr02330fb2008-05-16 12:18:29 +02002679 chip->opl3_io, chip->opl3_io+2
2680 );
Linus Torvalds1da177e2005-04-16 15:20:36 -07002681 } else {
Andreas Mohr02330fb2008-05-16 12:18:29 +02002682 /* need to use IDs 1, 2 since ID 0 is snd_azf3328_timer above */
2683 err = snd_opl3_timer_new(opl3, 1, 2);
2684 if (err < 0)
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002685 goto out_err;
Andreas Mohr02330fb2008-05-16 12:18:29 +02002686 err = snd_opl3_hwdep_new(opl3, 0, 1, NULL);
2687 if (err < 0)
2688 goto out_err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002689 }
2690
Andreas Mohrca54bde2006-05-17 11:02:24 +02002691 opl3->private_data = chip;
2692
Linus Torvalds1da177e2005-04-16 15:20:36 -07002693 sprintf(card->longname, "%s at 0x%lx, irq %i",
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002694 card->shortname, chip->ctrl_io, chip->irq);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002695
Andreas Mohr02330fb2008-05-16 12:18:29 +02002696 err = snd_card_register(card);
2697 if (err < 0)
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002698 goto out_err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002699
2700#ifdef MODULE
Andreas Mohr78df6172009-07-12 22:17:54 +02002701 printk(KERN_INFO
Andreas Mohre24a1212007-03-26 12:49:45 +02002702"azt3328: Sound driver for Aztech AZF3328-based soundcards such as PCI168.\n"
2703"azt3328: Hardware was completely undocumented, unfortunately.\n"
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002704"azt3328: Feel free to contact andi AT lisas.de for bug reports etc.!\n"
2705"azt3328: User-scalable sequencer timer set to %dHz (1024000Hz / %d).\n",
2706 1024000 / seqtimer_scaling, seqtimer_scaling);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002707#endif
2708
Andreas Mohr02330fb2008-05-16 12:18:29 +02002709 snd_azf3328_gameport(chip, dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002710
2711 pci_set_drvdata(pci, card);
2712 dev++;
2713
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002714 err = 0;
2715 goto out;
Andreas Mohr02330fb2008-05-16 12:18:29 +02002716
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002717out_err:
Andreas Mohr02330fb2008-05-16 12:18:29 +02002718 snd_printk(KERN_ERR "azf3328: something failed, exiting\n");
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002719 snd_card_free(card);
Andreas Mohr02330fb2008-05-16 12:18:29 +02002720
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002721out:
Linus Torvalds1da177e2005-04-16 15:20:36 -07002722 snd_azf3328_dbgcallleave();
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002723 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002724}
2725
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002726static void __devexit
2727snd_azf3328_remove(struct pci_dev *pci)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002728{
2729 snd_azf3328_dbgcallenter();
2730 snd_card_free(pci_get_drvdata(pci));
2731 pci_set_drvdata(pci, NULL);
2732 snd_azf3328_dbgcallleave();
2733}
2734
Andreas Mohrca54bde2006-05-17 11:02:24 +02002735#ifdef CONFIG_PM
Andreas Mohr78df6172009-07-12 22:17:54 +02002736static inline void
2737snd_azf3328_suspend_regs(unsigned long io_addr, unsigned count, u32 *saved_regs)
2738{
2739 unsigned reg;
2740
2741 for (reg = 0; reg < count; ++reg) {
2742 *saved_regs = inl(io_addr);
2743 snd_azf3328_dbgpm("suspend: io 0x%04lx: 0x%08x\n",
2744 io_addr, *saved_regs);
2745 ++saved_regs;
2746 io_addr += sizeof(*saved_regs);
2747 }
2748}
2749
Andreas Mohrb5dc20c2011-02-19 00:49:32 +01002750static inline void
2751snd_azf3328_resume_regs(const u32 *saved_regs,
2752 unsigned long io_addr,
2753 unsigned count
2754)
2755{
2756 unsigned reg;
2757
2758 for (reg = 0; reg < count; ++reg) {
2759 outl(*saved_regs, io_addr);
2760 snd_azf3328_dbgpm("resume: io 0x%04lx: 0x%08x --> 0x%08x\n",
2761 io_addr, *saved_regs, inl(io_addr));
2762 ++saved_regs;
2763 io_addr += sizeof(*saved_regs);
2764 }
2765}
2766
2767static inline void
2768snd_azf3328_suspend_ac97(struct snd_azf3328 *chip)
2769{
2770#ifdef AZF_USE_AC97_LAYER
2771 snd_ac97_suspend(chip->ac97);
2772#else
2773 snd_azf3328_suspend_regs(chip->mixer_io,
2774 ARRAY_SIZE(chip->saved_regs_mixer), chip->saved_regs_mixer);
2775
2776 /* make sure to disable master volume etc. to prevent looping sound */
2777 snd_azf3328_mixer_mute_control_master(chip, 1);
2778 snd_azf3328_mixer_mute_control_pcm(chip, 1);
2779#endif /* AZF_USE_AC97_LAYER */
2780}
2781
2782static inline void
2783snd_azf3328_resume_ac97(const struct snd_azf3328 *chip)
2784{
2785#ifdef AZF_USE_AC97_LAYER
2786 snd_ac97_resume(chip->ac97);
2787#else
2788 snd_azf3328_resume_regs(chip->saved_regs_mixer, chip->mixer_io,
2789 ARRAY_SIZE(chip->saved_regs_mixer));
2790
2791 /* unfortunately with 32bit transfers, IDX_MIXER_PLAY_MASTER (0x02)
2792 and IDX_MIXER_RESET (offset 0x00) get touched at the same time,
2793 resulting in a mixer reset condition persisting until _after_
2794 master vol was restored. Thus master vol needs an extra restore. */
2795 outw(((u16 *)chip->saved_regs_mixer)[1], chip->mixer_io + 2);
2796#endif /* AZF_USE_AC97_LAYER */
2797}
2798
Andreas Mohrca54bde2006-05-17 11:02:24 +02002799static int
2800snd_azf3328_suspend(struct pci_dev *pci, pm_message_t state)
2801{
2802 struct snd_card *card = pci_get_drvdata(pci);
2803 struct snd_azf3328 *chip = card->private_data;
Andreas Mohr78df6172009-07-12 22:17:54 +02002804 u16 *saved_regs_ctrl_u16;
Andreas Mohrca54bde2006-05-17 11:02:24 +02002805
2806 snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
Andreas Mohr02330fb2008-05-16 12:18:29 +02002807
Andreas Mohradf59312010-12-27 21:16:43 +01002808 /* same pcm object for playback/capture */
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002809 snd_pcm_suspend_all(chip->pcm[AZF_CODEC_PLAYBACK]);
2810 snd_pcm_suspend_all(chip->pcm[AZF_CODEC_I2S_OUT]);
Andreas Mohrca54bde2006-05-17 11:02:24 +02002811
Andreas Mohrb5dc20c2011-02-19 00:49:32 +01002812 snd_azf3328_suspend_ac97(chip);
Andreas Mohr02330fb2008-05-16 12:18:29 +02002813
Andreas Mohr78df6172009-07-12 22:17:54 +02002814 snd_azf3328_suspend_regs(chip->ctrl_io,
2815 ARRAY_SIZE(chip->saved_regs_ctrl), chip->saved_regs_ctrl);
Andreas Mohr627d3e72008-06-23 11:50:47 +02002816
2817 /* manually store the one currently relevant write-only reg, too */
Andreas Mohr78df6172009-07-12 22:17:54 +02002818 saved_regs_ctrl_u16 = (u16 *)chip->saved_regs_ctrl;
2819 saved_regs_ctrl_u16[IDX_IO_6AH / 2] = chip->shadow_reg_ctrl_6AH;
Andreas Mohr627d3e72008-06-23 11:50:47 +02002820
Andreas Mohr78df6172009-07-12 22:17:54 +02002821 snd_azf3328_suspend_regs(chip->game_io,
2822 ARRAY_SIZE(chip->saved_regs_game), chip->saved_regs_game);
2823 snd_azf3328_suspend_regs(chip->mpu_io,
2824 ARRAY_SIZE(chip->saved_regs_mpu), chip->saved_regs_mpu);
2825 snd_azf3328_suspend_regs(chip->opl3_io,
2826 ARRAY_SIZE(chip->saved_regs_opl3), chip->saved_regs_opl3);
Andreas Mohrca54bde2006-05-17 11:02:24 +02002827
Andreas Mohrca54bde2006-05-17 11:02:24 +02002828 pci_disable_device(pci);
2829 pci_save_state(pci);
Takashi Iwai30b35392006-10-11 18:52:53 +02002830 pci_set_power_state(pci, pci_choose_state(pci, state));
Andreas Mohrca54bde2006-05-17 11:02:24 +02002831 return 0;
2832}
2833
2834static int
2835snd_azf3328_resume(struct pci_dev *pci)
2836{
2837 struct snd_card *card = pci_get_drvdata(pci);
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002838 const struct snd_azf3328 *chip = card->private_data;
Andreas Mohrca54bde2006-05-17 11:02:24 +02002839
Andreas Mohrca54bde2006-05-17 11:02:24 +02002840 pci_set_power_state(pci, PCI_D0);
Takashi Iwai30b35392006-10-11 18:52:53 +02002841 pci_restore_state(pci);
2842 if (pci_enable_device(pci) < 0) {
2843 printk(KERN_ERR "azt3328: pci_enable_device failed, "
2844 "disabling device\n");
2845 snd_card_disconnect(card);
2846 return -EIO;
2847 }
Andreas Mohrca54bde2006-05-17 11:02:24 +02002848 pci_set_master(pci);
2849
Andreas Mohr78df6172009-07-12 22:17:54 +02002850 snd_azf3328_resume_regs(chip->saved_regs_game, chip->game_io,
2851 ARRAY_SIZE(chip->saved_regs_game));
2852 snd_azf3328_resume_regs(chip->saved_regs_mpu, chip->mpu_io,
2853 ARRAY_SIZE(chip->saved_regs_mpu));
2854 snd_azf3328_resume_regs(chip->saved_regs_opl3, chip->opl3_io,
2855 ARRAY_SIZE(chip->saved_regs_opl3));
2856
Andreas Mohrb5dc20c2011-02-19 00:49:32 +01002857 snd_azf3328_resume_ac97(chip);
Andreas Mohr78df6172009-07-12 22:17:54 +02002858
2859 snd_azf3328_resume_regs(chip->saved_regs_ctrl, chip->ctrl_io,
2860 ARRAY_SIZE(chip->saved_regs_ctrl));
Andreas Mohrca54bde2006-05-17 11:02:24 +02002861
2862 snd_power_change_state(card, SNDRV_CTL_POWER_D0);
2863 return 0;
2864}
Andreas Mohr02330fb2008-05-16 12:18:29 +02002865#endif /* CONFIG_PM */
Andreas Mohrca54bde2006-05-17 11:02:24 +02002866
2867
Linus Torvalds1da177e2005-04-16 15:20:36 -07002868static struct pci_driver driver = {
2869 .name = "AZF3328",
2870 .id_table = snd_azf3328_ids,
2871 .probe = snd_azf3328_probe,
2872 .remove = __devexit_p(snd_azf3328_remove),
Andreas Mohrca54bde2006-05-17 11:02:24 +02002873#ifdef CONFIG_PM
2874 .suspend = snd_azf3328_suspend,
2875 .resume = snd_azf3328_resume,
2876#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07002877};
2878
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002879static int __init
2880alsa_card_azf3328_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002881{
2882 int err;
2883 snd_azf3328_dbgcallenter();
Takashi Iwai01d25d42005-04-11 16:58:24 +02002884 err = pci_register_driver(&driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002885 snd_azf3328_dbgcallleave();
2886 return err;
2887}
2888
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002889static void __exit
2890alsa_card_azf3328_exit(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002891{
2892 snd_azf3328_dbgcallenter();
2893 pci_unregister_driver(&driver);
2894 snd_azf3328_dbgcallleave();
2895}
2896
2897module_init(alsa_card_azf3328_init)
2898module_exit(alsa_card_azf3328_exit)