blob: d5a00f337ab95e968e22690ec1312efca1a0b0bc [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)
Lucas De Marchi25985ed2011-03-30 22:57:33 -0300143 * - plug card into a different PCI slot, preferably one that isn't shared
Linus Torvalds1da177e2005-04-16 15:20:36 -0700144 * 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>
Paul Gortmaker65a77212011-07-15 13:13:37 -0400189#include <linux/module.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
Linus Torvalds1da177e2005-04-16 15:20:36 -0700256#if DEBUG_MIXER
Takashi Iwaiee419652009-02-05 16:11:31 +0100257#define snd_azf3328_dbgmixer(format, args...) printk(KERN_DEBUG format, ##args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700258#else
259#define snd_azf3328_dbgmixer(format, args...)
Andreas Mohr02330fb2008-05-16 12:18:29 +0200260#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700261
Andreas Mohr78df6172009-07-12 22:17:54 +0200262#if DEBUG_CODEC
263#define snd_azf3328_dbgcodec(format, args...) printk(KERN_DEBUG format, ##args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700264#else
Andreas Mohr78df6172009-07-12 22:17:54 +0200265#define snd_azf3328_dbgcodec(format, args...)
Andreas Mohr02330fb2008-05-16 12:18:29 +0200266#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700267
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200268#if DEBUG_MISC
Takashi Iwaiee419652009-02-05 16:11:31 +0100269#define snd_azf3328_dbgtimer(format, args...) printk(KERN_DEBUG format, ##args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700270#else
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200271#define snd_azf3328_dbgtimer(format, args...)
Andreas Mohr02330fb2008-05-16 12:18:29 +0200272#endif
273
274#if DEBUG_GAME
Takashi Iwaiee419652009-02-05 16:11:31 +0100275#define snd_azf3328_dbggame(format, args...) printk(KERN_DEBUG format, ##args)
Andreas Mohr02330fb2008-05-16 12:18:29 +0200276#else
277#define snd_azf3328_dbggame(format, args...)
278#endif
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200279
Andreas Mohr78df6172009-07-12 22:17:54 +0200280#if DEBUG_PM
281#define snd_azf3328_dbgpm(format, args...) printk(KERN_DEBUG format, ##args)
282#else
283#define snd_azf3328_dbgpm(format, args...)
284#endif
285
Linus Torvalds1da177e2005-04-16 15:20:36 -0700286static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
287module_param_array(index, int, NULL, 0444);
288MODULE_PARM_DESC(index, "Index value for AZF3328 soundcard.");
289
290static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
291module_param_array(id, charp, NULL, 0444);
292MODULE_PARM_DESC(id, "ID string for AZF3328 soundcard.");
293
Rusty Russella67ff6a2011-12-15 13:49:36 +1030294static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700295module_param_array(enable, bool, NULL, 0444);
296MODULE_PARM_DESC(enable, "Enable AZF3328 soundcard.");
297
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200298static int seqtimer_scaling = 128;
299module_param(seqtimer_scaling, int, 0444);
300MODULE_PARM_DESC(seqtimer_scaling, "Set 1024000Hz sequencer timer scale factor (lockup danger!). Default 128.");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700301
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200302enum snd_azf3328_codec_type {
Andreas Mohradf59312010-12-27 21:16:43 +0100303 /* warning: fixed indices (also used for bitmask checks!) */
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200304 AZF_CODEC_PLAYBACK = 0,
305 AZF_CODEC_CAPTURE = 1,
306 AZF_CODEC_I2S_OUT = 2,
Andreas Mohr02330fb2008-05-16 12:18:29 +0200307};
308
Andreas Mohrda237f32010-12-27 21:17:26 +0100309struct snd_azf3328_codec_data {
310 unsigned long io_base; /* keep first! (avoid offset calc) */
311 unsigned int dma_base; /* helper to avoid an indirection in hotpath */
312 spinlock_t *lock; /* TODO: convert to our own per-codec lock member */
313 struct snd_pcm_substream *substream;
314 bool running;
315 enum snd_azf3328_codec_type type;
316 const char *name;
317};
318
Takashi Iwai95de7762005-11-17 15:02:42 +0100319struct snd_azf3328 {
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200320 /* often-used fields towards beginning, then grouped */
Andreas Mohr02330fb2008-05-16 12:18:29 +0200321
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200322 unsigned long ctrl_io; /* usually 0xb000, size 128 */
Andreas Mohr02330fb2008-05-16 12:18:29 +0200323 unsigned long game_io; /* usually 0xb400, size 8 */
324 unsigned long mpu_io; /* usually 0xb800, size 4 */
325 unsigned long opl3_io; /* usually 0xbc00, size 8 */
326 unsigned long mixer_io; /* usually 0xc000, size 64 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700327
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200328 spinlock_t reg_lock;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700329
Takashi Iwai95de7762005-11-17 15:02:42 +0100330 struct snd_timer *timer;
Andreas Mohr02330fb2008-05-16 12:18:29 +0200331
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200332 struct snd_pcm *pcm[3];
333
334 /* playback, recording and I2S out codecs */
335 struct snd_azf3328_codec_data codecs[3];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700336
Andreas Mohrb5dc20c2011-02-19 00:49:32 +0100337#ifdef AZF_USE_AC97_LAYER
338 struct snd_ac97 *ac97;
339#endif
340
Takashi Iwai95de7762005-11-17 15:02:42 +0100341 struct snd_card *card;
342 struct snd_rawmidi *rmidi;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700343
Andreas Mohr02330fb2008-05-16 12:18:29 +0200344#ifdef SUPPORT_GAMEPORT
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200345 struct gameport *gameport;
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200346 u16 axes[4];
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200347#endif
348
349 struct pci_dev *pci;
350 int irq;
Andreas Mohrca54bde2006-05-17 11:02:24 +0200351
Andreas Mohr627d3e72008-06-23 11:50:47 +0200352 /* register 0x6a is write-only, thus need to remember setting.
353 * If we need to add more registers here, then we might try to fold this
354 * into some transparent combined shadow register handling with
355 * CONFIG_PM register storage below, but that's slightly difficult. */
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200356 u16 shadow_reg_ctrl_6AH;
Andreas Mohr627d3e72008-06-23 11:50:47 +0200357
Takashi Iwaic7561cd2012-08-14 18:12:04 +0200358#ifdef CONFIG_PM_SLEEP
Andreas Mohrca54bde2006-05-17 11:02:24 +0200359 /* register value containers for power management
Andreas Mohr78df6172009-07-12 22:17:54 +0200360 * Note: not always full I/O range preserved (similar to Win driver!) */
361 u32 saved_regs_ctrl[AZF_ALIGN(AZF_IO_SIZE_CTRL_PM) / 4];
362 u32 saved_regs_game[AZF_ALIGN(AZF_IO_SIZE_GAME_PM) / 4];
363 u32 saved_regs_mpu[AZF_ALIGN(AZF_IO_SIZE_MPU_PM) / 4];
364 u32 saved_regs_opl3[AZF_ALIGN(AZF_IO_SIZE_OPL3_PM) / 4];
365 u32 saved_regs_mixer[AZF_ALIGN(AZF_IO_SIZE_MIXER_PM) / 4];
Andreas Mohrca54bde2006-05-17 11:02:24 +0200366#endif
Takashi Iwai95de7762005-11-17 15:02:42 +0100367};
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200368
Alexey Dobriyancebe41d2010-02-06 00:21:03 +0200369static DEFINE_PCI_DEVICE_TABLE(snd_azf3328_ids) = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700370 { 0x122D, 0x50DC, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* PCI168/3328 */
371 { 0x122D, 0x80DA, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* 3328 */
372 { 0, }
373};
374
375MODULE_DEVICE_TABLE(pci, snd_azf3328_ids);
376
Andreas Mohr02330fb2008-05-16 12:18:29 +0200377
378static int
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200379snd_azf3328_io_reg_setb(unsigned reg, u8 mask, bool do_set)
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200380{
Andreas Mohradf59312010-12-27 21:16:43 +0100381 /* Well, strictly spoken, the inb/outb sequence isn't atomic
382 and would need locking. However we currently don't care
383 since it potentially complicates matters. */
Andreas Mohr02330fb2008-05-16 12:18:29 +0200384 u8 prev = inb(reg), new;
385
386 new = (do_set) ? (prev|mask) : (prev & ~mask);
387 /* we need to always write the new value no matter whether it differs
388 * or not, since some register bits don't indicate their setting */
389 outb(new, reg);
390 if (new != prev)
391 return 1;
392
393 return 0;
394}
395
Andreas Mohr02330fb2008-05-16 12:18:29 +0200396static inline void
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200397snd_azf3328_codec_outb(const struct snd_azf3328_codec_data *codec,
398 unsigned reg,
399 u8 value
400)
Andreas Mohr02330fb2008-05-16 12:18:29 +0200401{
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200402 outb(value, codec->io_base + reg);
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200403}
404
405static inline u8
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200406snd_azf3328_codec_inb(const struct snd_azf3328_codec_data *codec, unsigned reg)
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200407{
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200408 return inb(codec->io_base + reg);
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200409}
410
411static inline void
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200412snd_azf3328_codec_outw(const struct snd_azf3328_codec_data *codec,
413 unsigned reg,
414 u16 value
415)
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200416{
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200417 outw(value, codec->io_base + reg);
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200418}
419
420static inline u16
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200421snd_azf3328_codec_inw(const struct snd_azf3328_codec_data *codec, unsigned reg)
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200422{
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200423 return inw(codec->io_base + reg);
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200424}
425
426static inline void
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200427snd_azf3328_codec_outl(const struct snd_azf3328_codec_data *codec,
428 unsigned reg,
429 u32 value
430)
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200431{
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200432 outl(value, codec->io_base + reg);
Andreas Mohr02330fb2008-05-16 12:18:29 +0200433}
434
Andreas Mohr689c6912010-12-27 21:17:35 +0100435static inline void
436snd_azf3328_codec_outl_multi(const struct snd_azf3328_codec_data *codec,
437 unsigned reg, const void *buffer, int count
438)
439{
440 unsigned long addr = codec->io_base + reg;
441 if (count) {
442 const u32 *buf = buffer;
443 do {
444 outl(*buf++, addr);
445 addr += 4;
446 } while (--count);
447 }
448}
449
Andreas Mohr02330fb2008-05-16 12:18:29 +0200450static inline u32
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200451snd_azf3328_codec_inl(const struct snd_azf3328_codec_data *codec, unsigned reg)
Andreas Mohr02330fb2008-05-16 12:18:29 +0200452{
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200453 return inl(codec->io_base + reg);
454}
455
456static inline void
457snd_azf3328_ctrl_outb(const struct snd_azf3328 *chip, unsigned reg, u8 value)
458{
459 outb(value, chip->ctrl_io + reg);
460}
461
462static inline u8
463snd_azf3328_ctrl_inb(const struct snd_azf3328 *chip, unsigned reg)
464{
465 return inb(chip->ctrl_io + reg);
466}
467
468static inline void
469snd_azf3328_ctrl_outw(const struct snd_azf3328 *chip, unsigned reg, u16 value)
470{
471 outw(value, chip->ctrl_io + reg);
472}
473
474static inline void
475snd_azf3328_ctrl_outl(const struct snd_azf3328 *chip, unsigned reg, u32 value)
476{
477 outl(value, chip->ctrl_io + reg);
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200478}
479
480static inline void
Andreas Mohr02330fb2008-05-16 12:18:29 +0200481snd_azf3328_game_outb(const struct snd_azf3328 *chip, unsigned reg, u8 value)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700482{
Andreas Mohr02330fb2008-05-16 12:18:29 +0200483 outb(value, chip->game_io + reg);
484}
485
486static inline void
487snd_azf3328_game_outw(const struct snd_azf3328 *chip, unsigned reg, u16 value)
488{
489 outw(value, chip->game_io + reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700490}
491
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200492static inline u8
Andreas Mohr02330fb2008-05-16 12:18:29 +0200493snd_azf3328_game_inb(const struct snd_azf3328 *chip, unsigned reg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700494{
Andreas Mohr02330fb2008-05-16 12:18:29 +0200495 return inb(chip->game_io + reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700496}
497
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200498static inline u16
Andreas Mohr02330fb2008-05-16 12:18:29 +0200499snd_azf3328_game_inw(const struct snd_azf3328 *chip, unsigned reg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700500{
Andreas Mohr02330fb2008-05-16 12:18:29 +0200501 return inw(chip->game_io + reg);
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200502}
503
Andreas Mohr02330fb2008-05-16 12:18:29 +0200504static inline void
505snd_azf3328_mixer_outw(const struct snd_azf3328 *chip, unsigned reg, u16 value)
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200506{
Andreas Mohr02330fb2008-05-16 12:18:29 +0200507 outw(value, chip->mixer_io + reg);
508}
509
510static inline u16
511snd_azf3328_mixer_inw(const struct snd_azf3328 *chip, unsigned reg)
512{
513 return inw(chip->mixer_io + reg);
514}
515
516#define AZF_MUTE_BIT 0x80
517
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200518static bool
Andreas Mohrb5dc20c2011-02-19 00:49:32 +0100519snd_azf3328_mixer_mute_control(const struct snd_azf3328 *chip,
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200520 unsigned reg, bool do_mute
Andreas Mohr02330fb2008-05-16 12:18:29 +0200521)
522{
523 unsigned long portbase = chip->mixer_io + reg + 1;
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200524 bool updated;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700525
526 /* the mute bit is on the *second* (i.e. right) register of a
527 * left/right channel setting */
Andreas Mohr02330fb2008-05-16 12:18:29 +0200528 updated = snd_azf3328_io_reg_setb(portbase, AZF_MUTE_BIT, do_mute);
529
530 /* indicate whether it was muted before */
531 return (do_mute) ? !updated : updated;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700532}
533
Andreas Mohrb5dc20c2011-02-19 00:49:32 +0100534static inline bool
535snd_azf3328_mixer_mute_control_master(const struct snd_azf3328 *chip,
536 bool do_mute
537)
538{
539 return snd_azf3328_mixer_mute_control(
540 chip,
541 IDX_MIXER_PLAY_MASTER,
542 do_mute
543 );
544}
545
546static inline bool
547snd_azf3328_mixer_mute_control_pcm(const struct snd_azf3328 *chip,
548 bool do_mute
549)
550{
551 return snd_azf3328_mixer_mute_control(
552 chip,
553 IDX_MIXER_WAVEOUT,
554 do_mute
555 );
556}
557
558static inline void
559snd_azf3328_mixer_reset(const struct snd_azf3328 *chip)
560{
561 /* reset (close) mixer:
562 * first mute master volume, then reset
563 */
564 snd_azf3328_mixer_mute_control_master(chip, 1);
565 snd_azf3328_mixer_outw(chip, IDX_MIXER_RESET, 0x0000);
566}
567
568#ifdef AZF_USE_AC97_LAYER
569
570static inline void
571snd_azf3328_mixer_ac97_map_unsupported(unsigned short reg, const char *mode)
572{
573 /* need to add some more or less clever emulation? */
574 printk(KERN_WARNING
575 "azt3328: missing %s emulation for AC97 register 0x%02x!\n",
576 mode, reg);
577}
578
579/*
580 * Need to have _special_ AC97 mixer hardware register index mapper,
581 * to compensate for the issue of a rather AC97-incompatible hardware layout.
582 */
583#define AZF_REG_MASK 0x3f
584#define AZF_AC97_REG_UNSUPPORTED 0x8000
585#define AZF_AC97_REG_REAL_IO_READ 0x4000
586#define AZF_AC97_REG_REAL_IO_WRITE 0x2000
587#define AZF_AC97_REG_REAL_IO_RW \
588 (AZF_AC97_REG_REAL_IO_READ | AZF_AC97_REG_REAL_IO_WRITE)
589#define AZF_AC97_REG_EMU_IO_READ 0x0400
590#define AZF_AC97_REG_EMU_IO_WRITE 0x0200
591#define AZF_AC97_REG_EMU_IO_RW \
592 (AZF_AC97_REG_EMU_IO_READ | AZF_AC97_REG_EMU_IO_WRITE)
593static unsigned short
594snd_azf3328_mixer_ac97_map_reg_idx(unsigned short reg)
595{
596 static const struct {
597 unsigned short azf_reg;
598 } azf_reg_mapper[] = {
599 /* Especially when taking into consideration
600 * mono/stereo-based sequence of azf vs. AC97 control series,
601 * it's quite obvious that azf simply got rid
602 * of the AC97_HEADPHONE control at its intended offset,
603 * thus shifted _all_ controls by one,
604 * and _then_ simply added it as an FMSYNTH control at the end,
605 * to make up for the offset.
606 * This means we'll have to translate indices here as
607 * needed and then do some tiny AC97 patch action
608 * (snd_ac97_rename_vol_ctl() etc.) - that's it.
609 */
610 { /* AC97_RESET */ IDX_MIXER_RESET
611 | AZF_AC97_REG_REAL_IO_WRITE
612 | AZF_AC97_REG_EMU_IO_READ },
613 { /* AC97_MASTER */ IDX_MIXER_PLAY_MASTER },
614 /* note large shift: AC97_HEADPHONE to IDX_MIXER_FMSYNTH! */
615 { /* AC97_HEADPHONE */ IDX_MIXER_FMSYNTH },
616 { /* AC97_MASTER_MONO */ IDX_MIXER_MODEMOUT },
617 { /* AC97_MASTER_TONE */ IDX_MIXER_BASSTREBLE },
618 { /* AC97_PC_BEEP */ IDX_MIXER_PCBEEP },
619 { /* AC97_PHONE */ IDX_MIXER_MODEMIN },
620 { /* AC97_MIC */ IDX_MIXER_MIC },
621 { /* AC97_LINE */ IDX_MIXER_LINEIN },
622 { /* AC97_CD */ IDX_MIXER_CDAUDIO },
623 { /* AC97_VIDEO */ IDX_MIXER_VIDEO },
624 { /* AC97_AUX */ IDX_MIXER_AUX },
625 { /* AC97_PCM */ IDX_MIXER_WAVEOUT },
626 { /* AC97_REC_SEL */ IDX_MIXER_REC_SELECT },
627 { /* AC97_REC_GAIN */ IDX_MIXER_REC_VOLUME },
628 { /* AC97_REC_GAIN_MIC */ AZF_AC97_REG_EMU_IO_RW },
629 { /* AC97_GENERAL_PURPOSE */ IDX_MIXER_ADVCTL2 },
630 { /* AC97_3D_CONTROL */ IDX_MIXER_ADVCTL1 },
631 };
632
633 unsigned short reg_azf = AZF_AC97_REG_UNSUPPORTED;
634
635 /* azf3328 supports the low-numbered and low-spec:ed range
636 of AC97 regs only */
637 if (reg <= AC97_3D_CONTROL) {
638 unsigned short reg_idx = reg / 2;
639 reg_azf = azf_reg_mapper[reg_idx].azf_reg;
640 /* a translation-only entry means it's real read/write: */
641 if (!(reg_azf & ~AZF_REG_MASK))
642 reg_azf |= AZF_AC97_REG_REAL_IO_RW;
643 } else {
644 switch (reg) {
645 case AC97_POWERDOWN:
646 reg_azf = AZF_AC97_REG_EMU_IO_RW;
647 break;
648 case AC97_EXTENDED_ID:
649 reg_azf = AZF_AC97_REG_EMU_IO_READ;
650 break;
651 case AC97_EXTENDED_STATUS:
652 /* I don't know what the h*ll AC97 layer
653 * would consult this _extended_ register for
654 * given a base-AC97-advertised card,
655 * but let's just emulate it anyway :-P
656 */
657 reg_azf = AZF_AC97_REG_EMU_IO_RW;
658 break;
659 case AC97_VENDOR_ID1:
660 case AC97_VENDOR_ID2:
661 reg_azf = AZF_AC97_REG_EMU_IO_READ;
662 break;
663 }
664 }
665 return reg_azf;
666}
667
668static const unsigned short
669azf_emulated_ac97_caps =
670 AC97_BC_DEDICATED_MIC |
671 AC97_BC_BASS_TREBLE |
672 /* Headphone is an FM Synth control here */
673 AC97_BC_HEADPHONE |
674 /* no AC97_BC_LOUDNESS! */
675 /* mask 0x7c00 is
676 vendor-specific 3D enhancement
677 vendor indicator.
678 Since there actually _is_ an
679 entry for Aztech Labs
680 (13), make damn sure
681 to indicate it. */
682 (13 << 10);
683
684static const unsigned short
685azf_emulated_ac97_powerdown =
686 /* pretend everything to be active */
687 AC97_PD_ADC_STATUS |
688 AC97_PD_DAC_STATUS |
689 AC97_PD_MIXER_STATUS |
690 AC97_PD_VREF_STATUS;
691
692/*
693 * Emulated, _inofficial_ vendor ID
694 * (there might be some devices such as the MR 2800-W
695 * which could reveal the real Aztech AC97 ID).
696 * We choose to use "AZT" prefix, and then use 1 to indicate PCI168
697 * (better don't use 0x68 since there's a PCI368 as well).
698 */
699static const unsigned int
700azf_emulated_ac97_vendor_id = 0x415a5401;
701
702static unsigned short
703snd_azf3328_mixer_ac97_read(struct snd_ac97 *ac97, unsigned short reg_ac97)
704{
705 const struct snd_azf3328 *chip = ac97->private_data;
706 unsigned short reg_azf = snd_azf3328_mixer_ac97_map_reg_idx(reg_ac97);
707 unsigned short reg_val = 0;
Peter Senna Tschudine0f17c72013-09-22 20:44:12 +0200708 bool unsupported = false;
Andreas Mohrb5dc20c2011-02-19 00:49:32 +0100709
710 snd_azf3328_dbgmixer(
711 "snd_azf3328_mixer_ac97_read reg_ac97 %u\n",
712 reg_ac97
713 );
714 if (reg_azf & AZF_AC97_REG_UNSUPPORTED)
Peter Senna Tschudine0f17c72013-09-22 20:44:12 +0200715 unsupported = true;
Andreas Mohrb5dc20c2011-02-19 00:49:32 +0100716 else {
717 if (reg_azf & AZF_AC97_REG_REAL_IO_READ)
718 reg_val = snd_azf3328_mixer_inw(chip,
719 reg_azf & AZF_REG_MASK);
720 else {
721 /*
722 * Proceed with dummy I/O read,
723 * to ensure compatible timing where this may matter.
724 * (ALSA AC97 layer usually doesn't call I/O functions
725 * due to intelligent I/O caching anyway)
726 * Choose a mixer register that's thoroughly unrelated
727 * to common audio (try to minimize distortion).
728 */
729 snd_azf3328_mixer_inw(chip, IDX_MIXER_SOMETHING30H);
730 }
731
732 if (reg_azf & AZF_AC97_REG_EMU_IO_READ) {
733 switch (reg_ac97) {
734 case AC97_RESET:
735 reg_val |= azf_emulated_ac97_caps;
736 break;
737 case AC97_POWERDOWN:
738 reg_val |= azf_emulated_ac97_powerdown;
739 break;
740 case AC97_EXTENDED_ID:
741 case AC97_EXTENDED_STATUS:
742 /* AFAICS we simply can't support anything: */
743 reg_val |= 0;
744 break;
745 case AC97_VENDOR_ID1:
746 reg_val = azf_emulated_ac97_vendor_id >> 16;
747 break;
748 case AC97_VENDOR_ID2:
749 reg_val = azf_emulated_ac97_vendor_id & 0xffff;
750 break;
751 default:
Peter Senna Tschudine0f17c72013-09-22 20:44:12 +0200752 unsupported = true;
Andreas Mohrb5dc20c2011-02-19 00:49:32 +0100753 break;
754 }
755 }
756 }
757 if (unsupported)
758 snd_azf3328_mixer_ac97_map_unsupported(reg_ac97, "read");
759
760 return reg_val;
761}
762
763static void
764snd_azf3328_mixer_ac97_write(struct snd_ac97 *ac97,
765 unsigned short reg_ac97, unsigned short val)
766{
767 const struct snd_azf3328 *chip = ac97->private_data;
768 unsigned short reg_azf = snd_azf3328_mixer_ac97_map_reg_idx(reg_ac97);
Peter Senna Tschudine0f17c72013-09-22 20:44:12 +0200769 bool unsupported = false;
Andreas Mohrb5dc20c2011-02-19 00:49:32 +0100770
771 snd_azf3328_dbgmixer(
772 "snd_azf3328_mixer_ac97_write reg_ac97 %u val %u\n",
773 reg_ac97, val
774 );
775 if (reg_azf & AZF_AC97_REG_UNSUPPORTED)
Peter Senna Tschudine0f17c72013-09-22 20:44:12 +0200776 unsupported = true;
Andreas Mohrb5dc20c2011-02-19 00:49:32 +0100777 else {
778 if (reg_azf & AZF_AC97_REG_REAL_IO_WRITE)
779 snd_azf3328_mixer_outw(
780 chip,
781 reg_azf & AZF_REG_MASK,
782 val
783 );
784 else
785 if (reg_azf & AZF_AC97_REG_EMU_IO_WRITE) {
786 switch (reg_ac97) {
787 case AC97_REC_GAIN_MIC:
788 case AC97_POWERDOWN:
789 case AC97_EXTENDED_STATUS:
790 /*
791 * Silently swallow these writes.
792 * Since for most registers our card doesn't
793 * actually support a comparable feature,
794 * this is exactly what we should do here.
795 * The AC97 layer's I/O caching probably
796 * automatically takes care of all the rest...
797 * (remembers written values etc.)
798 */
799 break;
800 default:
Peter Senna Tschudine0f17c72013-09-22 20:44:12 +0200801 unsupported = true;
Andreas Mohrb5dc20c2011-02-19 00:49:32 +0100802 break;
803 }
804 }
805 }
806 if (unsupported)
807 snd_azf3328_mixer_ac97_map_unsupported(reg_ac97, "write");
808}
809
Bill Pembertone23e7a12012-12-06 12:35:10 -0500810static int
Andreas Mohrb5dc20c2011-02-19 00:49:32 +0100811snd_azf3328_mixer_new(struct snd_azf3328 *chip)
812{
813 struct snd_ac97_bus *bus;
814 struct snd_ac97_template ac97;
815 static struct snd_ac97_bus_ops ops = {
816 .write = snd_azf3328_mixer_ac97_write,
817 .read = snd_azf3328_mixer_ac97_read,
818 };
819 int rc;
820
821 memset(&ac97, 0, sizeof(ac97));
822 ac97.scaps = AC97_SCAP_SKIP_MODEM
823 | AC97_SCAP_AUDIO /* we support audio! */
824 | AC97_SCAP_NO_SPDIF;
825 ac97.private_data = chip;
826 ac97.pci = chip->pci;
827
828 /*
829 * ALSA's AC97 layer has terrible init crackling issues,
830 * unfortunately, and since it makes use of AC97_RESET,
831 * there's no use trying to mute Master Playback proactively.
832 */
833
834 rc = snd_ac97_bus(chip->card, 0, &ops, NULL, &bus);
835 if (!rc)
836 rc = snd_ac97_mixer(bus, &ac97, &chip->ac97);
837 /*
838 * Make sure to complain loudly in case of AC97 init failure,
839 * since failure may happen quite often,
840 * due to this card being a very quirky AC97 "lookalike".
841 */
842 if (rc)
843 printk(KERN_ERR "azt3328: AC97 init failed, err %d!\n", rc);
844
845 /* If we return an error here, then snd_card_free() should
846 * free up any ac97 codecs that got created, as well as the bus.
847 */
848 return rc;
849}
850#else /* AZF_USE_AC97_LAYER */
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200851static void
Andreas Mohr02330fb2008-05-16 12:18:29 +0200852snd_azf3328_mixer_write_volume_gradually(const struct snd_azf3328 *chip,
853 unsigned reg,
854 unsigned char dst_vol_left,
855 unsigned char dst_vol_right,
856 int chan_sel, int delay
857)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700858{
Andreas Mohr02330fb2008-05-16 12:18:29 +0200859 unsigned long portbase = chip->mixer_io + reg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700860 unsigned char curr_vol_left = 0, curr_vol_right = 0;
Andreas Mohr02330fb2008-05-16 12:18:29 +0200861 int left_change = 0, right_change = 0;
862
Andreas Mohr02330fb2008-05-16 12:18:29 +0200863 if (chan_sel & SET_CHAN_LEFT) {
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200864 curr_vol_left = inb(portbase + 1);
Andreas Mohr02330fb2008-05-16 12:18:29 +0200865
866 /* take care of muting flag contained in left channel */
867 if (curr_vol_left & AZF_MUTE_BIT)
868 dst_vol_left |= AZF_MUTE_BIT;
869 else
870 dst_vol_left &= ~AZF_MUTE_BIT;
871
872 left_change = (curr_vol_left > dst_vol_left) ? -1 : 1;
873 }
874
875 if (chan_sel & SET_CHAN_RIGHT) {
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200876 curr_vol_right = inb(portbase + 0);
Andreas Mohr02330fb2008-05-16 12:18:29 +0200877
878 right_change = (curr_vol_right > dst_vol_right) ? -1 : 1;
879 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700880
Andreas Mohre2f87262006-05-17 11:04:19 +0200881 do {
Andreas Mohr02330fb2008-05-16 12:18:29 +0200882 if (left_change) {
883 if (curr_vol_left != dst_vol_left) {
884 curr_vol_left += left_change;
885 outb(curr_vol_left, portbase + 1);
886 } else
887 left_change = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700888 }
Andreas Mohr02330fb2008-05-16 12:18:29 +0200889 if (right_change) {
890 if (curr_vol_right != dst_vol_right) {
891 curr_vol_right += right_change;
892
Linus Torvalds1da177e2005-04-16 15:20:36 -0700893 /* during volume change, the right channel is crackling
894 * somewhat more than the left channel, unfortunately.
895 * This seems to be a hardware issue. */
Andreas Mohr02330fb2008-05-16 12:18:29 +0200896 outb(curr_vol_right, portbase + 0);
897 } else
898 right_change = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700899 }
900 if (delay)
901 mdelay(delay);
Andreas Mohr02330fb2008-05-16 12:18:29 +0200902 } while ((left_change) || (right_change));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700903}
904
905/*
906 * general mixer element
907 */
Takashi Iwai95de7762005-11-17 15:02:42 +0100908struct azf3328_mixer_reg {
Andreas Mohr02330fb2008-05-16 12:18:29 +0200909 unsigned reg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700910 unsigned int lchan_shift, rchan_shift;
911 unsigned int mask;
912 unsigned int invert: 1;
913 unsigned int stereo: 1;
914 unsigned int enum_c: 4;
Takashi Iwai95de7762005-11-17 15:02:42 +0100915};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700916
917#define COMPOSE_MIXER_REG(reg,lchan_shift,rchan_shift,mask,invert,stereo,enum_c) \
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200918 ((reg) | (lchan_shift << 8) | (rchan_shift << 12) | \
919 (mask << 16) | \
920 (invert << 24) | \
921 (stereo << 25) | \
922 (enum_c << 26))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700923
Takashi Iwai95de7762005-11-17 15:02:42 +0100924static void snd_azf3328_mixer_reg_decode(struct azf3328_mixer_reg *r, unsigned long val)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700925{
926 r->reg = val & 0xff;
927 r->lchan_shift = (val >> 8) & 0x0f;
928 r->rchan_shift = (val >> 12) & 0x0f;
929 r->mask = (val >> 16) & 0xff;
930 r->invert = (val >> 24) & 1;
931 r->stereo = (val >> 25) & 1;
932 r->enum_c = (val >> 26) & 0x0f;
933}
934
935/*
936 * mixer switches/volumes
937 */
938
939#define AZF3328_MIXER_SWITCH(xname, reg, shift, invert) \
940{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
941 .info = snd_azf3328_info_mixer, \
942 .get = snd_azf3328_get_mixer, .put = snd_azf3328_put_mixer, \
943 .private_value = COMPOSE_MIXER_REG(reg, shift, 0, 0x1, invert, 0, 0), \
944}
945
946#define AZF3328_MIXER_VOL_STEREO(xname, reg, mask, invert) \
947{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
948 .info = snd_azf3328_info_mixer, \
949 .get = snd_azf3328_get_mixer, .put = snd_azf3328_put_mixer, \
950 .private_value = COMPOSE_MIXER_REG(reg, 8, 0, mask, invert, 1, 0), \
951}
952
953#define AZF3328_MIXER_VOL_MONO(xname, reg, mask, is_right_chan) \
954{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
955 .info = snd_azf3328_info_mixer, \
956 .get = snd_azf3328_get_mixer, .put = snd_azf3328_put_mixer, \
957 .private_value = COMPOSE_MIXER_REG(reg, is_right_chan ? 0 : 8, 0, mask, 1, 0, 0), \
958}
959
960#define AZF3328_MIXER_VOL_SPECIAL(xname, reg, mask, shift, invert) \
961{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
962 .info = snd_azf3328_info_mixer, \
963 .get = snd_azf3328_get_mixer, .put = snd_azf3328_put_mixer, \
964 .private_value = COMPOSE_MIXER_REG(reg, shift, 0, mask, invert, 0, 0), \
965}
966
967#define AZF3328_MIXER_ENUM(xname, reg, enum_c, shift) \
968{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
969 .info = snd_azf3328_info_mixer_enum, \
970 .get = snd_azf3328_get_mixer_enum, .put = snd_azf3328_put_mixer_enum, \
971 .private_value = COMPOSE_MIXER_REG(reg, shift, 0, 0, 0, 0, enum_c), \
972}
973
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200974static int
Takashi Iwai95de7762005-11-17 15:02:42 +0100975snd_azf3328_info_mixer(struct snd_kcontrol *kcontrol,
976 struct snd_ctl_elem_info *uinfo)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700977{
Takashi Iwai95de7762005-11-17 15:02:42 +0100978 struct azf3328_mixer_reg reg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700979
Linus Torvalds1da177e2005-04-16 15:20:36 -0700980 snd_azf3328_mixer_reg_decode(&reg, kcontrol->private_value);
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200981 uinfo->type = reg.mask == 1 ?
982 SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700983 uinfo->count = reg.stereo + 1;
984 uinfo->value.integer.min = 0;
985 uinfo->value.integer.max = reg.mask;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700986 return 0;
987}
988
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200989static int
Takashi Iwai95de7762005-11-17 15:02:42 +0100990snd_azf3328_get_mixer(struct snd_kcontrol *kcontrol,
991 struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700992{
Takashi Iwai95de7762005-11-17 15:02:42 +0100993 struct snd_azf3328 *chip = snd_kcontrol_chip(kcontrol);
994 struct azf3328_mixer_reg reg;
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200995 u16 oreg, val;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700996
Linus Torvalds1da177e2005-04-16 15:20:36 -0700997 snd_azf3328_mixer_reg_decode(&reg, kcontrol->private_value);
998
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200999 oreg = snd_azf3328_mixer_inw(chip, reg.reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001000 val = (oreg >> reg.lchan_shift) & reg.mask;
1001 if (reg.invert)
1002 val = reg.mask - val;
1003 ucontrol->value.integer.value[0] = val;
1004 if (reg.stereo) {
1005 val = (oreg >> reg.rchan_shift) & reg.mask;
1006 if (reg.invert)
1007 val = reg.mask - val;
1008 ucontrol->value.integer.value[1] = val;
1009 }
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001010 snd_azf3328_dbgmixer("get: %02x is %04x -> vol %02lx|%02lx "
1011 "(shift %02d|%02d, mask %02x, inv. %d, stereo %d)\n",
1012 reg.reg, oreg,
1013 ucontrol->value.integer.value[0], ucontrol->value.integer.value[1],
1014 reg.lchan_shift, reg.rchan_shift, reg.mask, reg.invert, reg.stereo);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001015 return 0;
1016}
1017
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001018static int
Takashi Iwai95de7762005-11-17 15:02:42 +01001019snd_azf3328_put_mixer(struct snd_kcontrol *kcontrol,
1020 struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001021{
Takashi Iwai95de7762005-11-17 15:02:42 +01001022 struct snd_azf3328 *chip = snd_kcontrol_chip(kcontrol);
1023 struct azf3328_mixer_reg reg;
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001024 u16 oreg, nreg, val;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001025
Linus Torvalds1da177e2005-04-16 15:20:36 -07001026 snd_azf3328_mixer_reg_decode(&reg, kcontrol->private_value);
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001027 oreg = snd_azf3328_mixer_inw(chip, reg.reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001028 val = ucontrol->value.integer.value[0] & reg.mask;
1029 if (reg.invert)
1030 val = reg.mask - val;
1031 nreg = oreg & ~(reg.mask << reg.lchan_shift);
1032 nreg |= (val << reg.lchan_shift);
1033 if (reg.stereo) {
1034 val = ucontrol->value.integer.value[1] & reg.mask;
1035 if (reg.invert)
1036 val = reg.mask - val;
1037 nreg &= ~(reg.mask << reg.rchan_shift);
1038 nreg |= (val << reg.rchan_shift);
1039 }
1040 if (reg.mask >= 0x07) /* it's a volume control, so better take care */
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001041 snd_azf3328_mixer_write_volume_gradually(
1042 chip, reg.reg, nreg >> 8, nreg & 0xff,
1043 /* just set both channels, doesn't matter */
1044 SET_CHAN_LEFT|SET_CHAN_RIGHT,
1045 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001046 else
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001047 snd_azf3328_mixer_outw(chip, reg.reg, nreg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001048
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001049 snd_azf3328_dbgmixer("put: %02x to %02lx|%02lx, "
1050 "oreg %04x; shift %02d|%02d -> nreg %04x; after: %04x\n",
1051 reg.reg, ucontrol->value.integer.value[0], ucontrol->value.integer.value[1],
1052 oreg, reg.lchan_shift, reg.rchan_shift,
1053 nreg, snd_azf3328_mixer_inw(chip, reg.reg));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001054 return (nreg != oreg);
1055}
1056
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001057static int
Takashi Iwai95de7762005-11-17 15:02:42 +01001058snd_azf3328_info_mixer_enum(struct snd_kcontrol *kcontrol,
1059 struct snd_ctl_elem_info *uinfo)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001060{
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001061 static const char * const texts1[] = {
Andreas Mohr13769e32006-05-17 11:03:16 +02001062 "Mic1", "Mic2"
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001063 };
1064 static const char * const texts2[] = {
Andreas Mohr13769e32006-05-17 11:03:16 +02001065 "Mix", "Mic"
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001066 };
1067 static const char * const texts3[] = {
Andreas Mohr02330fb2008-05-16 12:18:29 +02001068 "Mic", "CD", "Video", "Aux",
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001069 "Line", "Mix", "Mix Mono", "Phone"
Linus Torvalds1da177e2005-04-16 15:20:36 -07001070 };
Andreas Mohr13769e32006-05-17 11:03:16 +02001071 static const char * const texts4[] = {
1072 "pre 3D", "post 3D"
1073 };
Takashi Iwai95de7762005-11-17 15:02:42 +01001074 struct azf3328_mixer_reg reg;
Andreas Mohr627d3e72008-06-23 11:50:47 +02001075 const char * const *p = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001076
1077 snd_azf3328_mixer_reg_decode(&reg, kcontrol->private_value);
1078 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
1079 uinfo->count = (reg.reg == IDX_MIXER_REC_SELECT) ? 2 : 1;
1080 uinfo->value.enumerated.items = reg.enum_c;
1081 if (uinfo->value.enumerated.item > reg.enum_c - 1U)
1082 uinfo->value.enumerated.item = reg.enum_c - 1U;
Andreas Mohre2f87262006-05-17 11:04:19 +02001083 if (reg.reg == IDX_MIXER_ADVCTL2) {
Andreas Mohr13769e32006-05-17 11:03:16 +02001084 switch(reg.lchan_shift) {
1085 case 8: /* modem out sel */
Andreas Mohr627d3e72008-06-23 11:50:47 +02001086 p = texts1;
Andreas Mohr13769e32006-05-17 11:03:16 +02001087 break;
1088 case 9: /* mono sel source */
Andreas Mohr627d3e72008-06-23 11:50:47 +02001089 p = texts2;
Andreas Mohr13769e32006-05-17 11:03:16 +02001090 break;
1091 case 15: /* PCM Out Path */
Andreas Mohr627d3e72008-06-23 11:50:47 +02001092 p = texts4;
Andreas Mohr13769e32006-05-17 11:03:16 +02001093 break;
1094 }
Andreas Mohre2f87262006-05-17 11:04:19 +02001095 } else
Andreas Mohr02330fb2008-05-16 12:18:29 +02001096 if (reg.reg == IDX_MIXER_REC_SELECT)
Andreas Mohr627d3e72008-06-23 11:50:47 +02001097 p = texts3;
Andreas Mohr02330fb2008-05-16 12:18:29 +02001098
Andreas Mohr627d3e72008-06-23 11:50:47 +02001099 strcpy(uinfo->value.enumerated.name, p[uinfo->value.enumerated.item]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001100 return 0;
1101}
1102
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001103static int
Takashi Iwai95de7762005-11-17 15:02:42 +01001104snd_azf3328_get_mixer_enum(struct snd_kcontrol *kcontrol,
1105 struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001106{
Takashi Iwai95de7762005-11-17 15:02:42 +01001107 struct snd_azf3328 *chip = snd_kcontrol_chip(kcontrol);
1108 struct azf3328_mixer_reg reg;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001109 unsigned short val;
Andreas Mohr02330fb2008-05-16 12:18:29 +02001110
Linus Torvalds1da177e2005-04-16 15:20:36 -07001111 snd_azf3328_mixer_reg_decode(&reg, kcontrol->private_value);
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001112 val = snd_azf3328_mixer_inw(chip, reg.reg);
Andreas Mohre2f87262006-05-17 11:04:19 +02001113 if (reg.reg == IDX_MIXER_REC_SELECT) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001114 ucontrol->value.enumerated.item[0] = (val >> 8) & (reg.enum_c - 1);
1115 ucontrol->value.enumerated.item[1] = (val >> 0) & (reg.enum_c - 1);
Andreas Mohre2f87262006-05-17 11:04:19 +02001116 } else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001117 ucontrol->value.enumerated.item[0] = (val >> reg.lchan_shift) & (reg.enum_c - 1);
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001118
1119 snd_azf3328_dbgmixer("get_enum: %02x is %04x -> %d|%d (shift %02d, enum_c %d)\n",
1120 reg.reg, val, ucontrol->value.enumerated.item[0], ucontrol->value.enumerated.item[1],
1121 reg.lchan_shift, reg.enum_c);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001122 return 0;
1123}
1124
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001125static int
Takashi Iwai95de7762005-11-17 15:02:42 +01001126snd_azf3328_put_mixer_enum(struct snd_kcontrol *kcontrol,
1127 struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001128{
Takashi Iwai95de7762005-11-17 15:02:42 +01001129 struct snd_azf3328 *chip = snd_kcontrol_chip(kcontrol);
1130 struct azf3328_mixer_reg reg;
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001131 u16 oreg, nreg, val;
Andreas Mohr02330fb2008-05-16 12:18:29 +02001132
Linus Torvalds1da177e2005-04-16 15:20:36 -07001133 snd_azf3328_mixer_reg_decode(&reg, kcontrol->private_value);
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001134 oreg = snd_azf3328_mixer_inw(chip, reg.reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001135 val = oreg;
Andreas Mohre2f87262006-05-17 11:04:19 +02001136 if (reg.reg == IDX_MIXER_REC_SELECT) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001137 if (ucontrol->value.enumerated.item[0] > reg.enum_c - 1U ||
1138 ucontrol->value.enumerated.item[1] > reg.enum_c - 1U)
1139 return -EINVAL;
1140 val = (ucontrol->value.enumerated.item[0] << 8) |
1141 (ucontrol->value.enumerated.item[1] << 0);
Andreas Mohre2f87262006-05-17 11:04:19 +02001142 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001143 if (ucontrol->value.enumerated.item[0] > reg.enum_c - 1U)
1144 return -EINVAL;
1145 val &= ~((reg.enum_c - 1) << reg.lchan_shift);
1146 val |= (ucontrol->value.enumerated.item[0] << reg.lchan_shift);
1147 }
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001148 snd_azf3328_mixer_outw(chip, reg.reg, val);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001149 nreg = val;
1150
1151 snd_azf3328_dbgmixer("put_enum: %02x to %04x, oreg %04x\n", reg.reg, val, oreg);
1152 return (nreg != oreg);
1153}
1154
Bill Pembertone23e7a12012-12-06 12:35:10 -05001155static struct snd_kcontrol_new snd_azf3328_mixer_controls[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001156 AZF3328_MIXER_SWITCH("Master Playback Switch", IDX_MIXER_PLAY_MASTER, 15, 1),
1157 AZF3328_MIXER_VOL_STEREO("Master Playback Volume", IDX_MIXER_PLAY_MASTER, 0x1f, 1),
Andreas Mohr627d3e72008-06-23 11:50:47 +02001158 AZF3328_MIXER_SWITCH("PCM Playback Switch", IDX_MIXER_WAVEOUT, 15, 1),
1159 AZF3328_MIXER_VOL_STEREO("PCM Playback Volume",
1160 IDX_MIXER_WAVEOUT, 0x1f, 1),
1161 AZF3328_MIXER_SWITCH("PCM 3D Bypass Playback Switch",
1162 IDX_MIXER_ADVCTL2, 7, 1),
Linus Torvalds1da177e2005-04-16 15:20:36 -07001163 AZF3328_MIXER_SWITCH("FM Playback Switch", IDX_MIXER_FMSYNTH, 15, 1),
1164 AZF3328_MIXER_VOL_STEREO("FM Playback Volume", IDX_MIXER_FMSYNTH, 0x1f, 1),
1165 AZF3328_MIXER_SWITCH("CD Playback Switch", IDX_MIXER_CDAUDIO, 15, 1),
1166 AZF3328_MIXER_VOL_STEREO("CD Playback Volume", IDX_MIXER_CDAUDIO, 0x1f, 1),
1167 AZF3328_MIXER_SWITCH("Capture Switch", IDX_MIXER_REC_VOLUME, 15, 1),
1168 AZF3328_MIXER_VOL_STEREO("Capture Volume", IDX_MIXER_REC_VOLUME, 0x0f, 0),
1169 AZF3328_MIXER_ENUM("Capture Source", IDX_MIXER_REC_SELECT, 8, 0),
1170 AZF3328_MIXER_SWITCH("Mic Playback Switch", IDX_MIXER_MIC, 15, 1),
1171 AZF3328_MIXER_VOL_MONO("Mic Playback Volume", IDX_MIXER_MIC, 0x1f, 1),
1172 AZF3328_MIXER_SWITCH("Mic Boost (+20dB)", IDX_MIXER_MIC, 6, 0),
1173 AZF3328_MIXER_SWITCH("Line Playback Switch", IDX_MIXER_LINEIN, 15, 1),
1174 AZF3328_MIXER_VOL_STEREO("Line Playback Volume", IDX_MIXER_LINEIN, 0x1f, 1),
Jaroslav Kyselad355c82a2009-11-03 15:47:25 +01001175 AZF3328_MIXER_SWITCH("Beep Playback Switch", IDX_MIXER_PCBEEP, 15, 1),
1176 AZF3328_MIXER_VOL_SPECIAL("Beep Playback Volume", IDX_MIXER_PCBEEP, 0x0f, 1, 1),
Linus Torvalds1da177e2005-04-16 15:20:36 -07001177 AZF3328_MIXER_SWITCH("Video Playback Switch", IDX_MIXER_VIDEO, 15, 1),
1178 AZF3328_MIXER_VOL_STEREO("Video Playback Volume", IDX_MIXER_VIDEO, 0x1f, 1),
1179 AZF3328_MIXER_SWITCH("Aux Playback Switch", IDX_MIXER_AUX, 15, 1),
1180 AZF3328_MIXER_VOL_STEREO("Aux Playback Volume", IDX_MIXER_AUX, 0x1f, 1),
1181 AZF3328_MIXER_SWITCH("Modem Playback Switch", IDX_MIXER_MODEMOUT, 15, 1),
1182 AZF3328_MIXER_VOL_MONO("Modem Playback Volume", IDX_MIXER_MODEMOUT, 0x1f, 1),
1183 AZF3328_MIXER_SWITCH("Modem Capture Switch", IDX_MIXER_MODEMIN, 15, 1),
1184 AZF3328_MIXER_VOL_MONO("Modem Capture Volume", IDX_MIXER_MODEMIN, 0x1f, 1),
Andreas Mohr13769e32006-05-17 11:03:16 +02001185 AZF3328_MIXER_ENUM("Mic Select", IDX_MIXER_ADVCTL2, 2, 8),
1186 AZF3328_MIXER_ENUM("Mono Output Select", IDX_MIXER_ADVCTL2, 2, 9),
Andreas Mohre24a1212007-03-26 12:49:45 +02001187 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 -07001188 AZF3328_MIXER_VOL_SPECIAL("Tone Control - Treble", IDX_MIXER_BASSTREBLE, 0x07, 1, 0),
1189 AZF3328_MIXER_VOL_SPECIAL("Tone Control - Bass", IDX_MIXER_BASSTREBLE, 0x07, 9, 0),
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001190 AZF3328_MIXER_SWITCH("3D Control - Switch", IDX_MIXER_ADVCTL2, 13, 0),
Andreas Mohr13769e32006-05-17 11:03:16 +02001191 AZF3328_MIXER_VOL_SPECIAL("3D Control - Width", IDX_MIXER_ADVCTL1, 0x07, 1, 0), /* "3D Width" */
1192 AZF3328_MIXER_VOL_SPECIAL("3D Control - Depth", IDX_MIXER_ADVCTL1, 0x03, 8, 0), /* "Hifi 3D" */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001193#if MIXER_TESTING
1194 AZF3328_MIXER_SWITCH("0", IDX_MIXER_ADVCTL2, 0, 0),
1195 AZF3328_MIXER_SWITCH("1", IDX_MIXER_ADVCTL2, 1, 0),
1196 AZF3328_MIXER_SWITCH("2", IDX_MIXER_ADVCTL2, 2, 0),
1197 AZF3328_MIXER_SWITCH("3", IDX_MIXER_ADVCTL2, 3, 0),
1198 AZF3328_MIXER_SWITCH("4", IDX_MIXER_ADVCTL2, 4, 0),
1199 AZF3328_MIXER_SWITCH("5", IDX_MIXER_ADVCTL2, 5, 0),
1200 AZF3328_MIXER_SWITCH("6", IDX_MIXER_ADVCTL2, 6, 0),
1201 AZF3328_MIXER_SWITCH("7", IDX_MIXER_ADVCTL2, 7, 0),
1202 AZF3328_MIXER_SWITCH("8", IDX_MIXER_ADVCTL2, 8, 0),
1203 AZF3328_MIXER_SWITCH("9", IDX_MIXER_ADVCTL2, 9, 0),
1204 AZF3328_MIXER_SWITCH("10", IDX_MIXER_ADVCTL2, 10, 0),
1205 AZF3328_MIXER_SWITCH("11", IDX_MIXER_ADVCTL2, 11, 0),
1206 AZF3328_MIXER_SWITCH("12", IDX_MIXER_ADVCTL2, 12, 0),
1207 AZF3328_MIXER_SWITCH("13", IDX_MIXER_ADVCTL2, 13, 0),
1208 AZF3328_MIXER_SWITCH("14", IDX_MIXER_ADVCTL2, 14, 0),
1209 AZF3328_MIXER_SWITCH("15", IDX_MIXER_ADVCTL2, 15, 0),
1210#endif
1211};
1212
Bill Pembertone23e7a12012-12-06 12:35:10 -05001213static u16 snd_azf3328_init_values[][2] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001214 { IDX_MIXER_PLAY_MASTER, MIXER_MUTE_MASK|0x1f1f },
1215 { IDX_MIXER_MODEMOUT, MIXER_MUTE_MASK|0x1f1f },
1216 { IDX_MIXER_BASSTREBLE, 0x0000 },
1217 { IDX_MIXER_PCBEEP, MIXER_MUTE_MASK|0x1f1f },
1218 { IDX_MIXER_MODEMIN, MIXER_MUTE_MASK|0x1f1f },
1219 { IDX_MIXER_MIC, MIXER_MUTE_MASK|0x001f },
1220 { IDX_MIXER_LINEIN, MIXER_MUTE_MASK|0x1f1f },
1221 { IDX_MIXER_CDAUDIO, MIXER_MUTE_MASK|0x1f1f },
1222 { IDX_MIXER_VIDEO, MIXER_MUTE_MASK|0x1f1f },
1223 { IDX_MIXER_AUX, MIXER_MUTE_MASK|0x1f1f },
1224 { IDX_MIXER_WAVEOUT, MIXER_MUTE_MASK|0x1f1f },
1225 { IDX_MIXER_FMSYNTH, MIXER_MUTE_MASK|0x1f1f },
1226 { IDX_MIXER_REC_VOLUME, MIXER_MUTE_MASK|0x0707 },
1227};
1228
Bill Pembertone23e7a12012-12-06 12:35:10 -05001229static int
Takashi Iwai95de7762005-11-17 15:02:42 +01001230snd_azf3328_mixer_new(struct snd_azf3328 *chip)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001231{
Takashi Iwai95de7762005-11-17 15:02:42 +01001232 struct snd_card *card;
1233 const struct snd_kcontrol_new *sw;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001234 unsigned int idx;
1235 int err;
1236
Takashi Iwaida3cec32008-08-08 17:12:14 +02001237 if (snd_BUG_ON(!chip || !chip->card))
1238 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001239
1240 card = chip->card;
1241
1242 /* mixer reset */
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001243 snd_azf3328_mixer_outw(chip, IDX_MIXER_RESET, 0x0000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001244
1245 /* mute and zero volume channels */
Andreas Mohr02330fb2008-05-16 12:18:29 +02001246 for (idx = 0; idx < ARRAY_SIZE(snd_azf3328_init_values); ++idx) {
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001247 snd_azf3328_mixer_outw(chip,
1248 snd_azf3328_init_values[idx][0],
1249 snd_azf3328_init_values[idx][1]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001250 }
Andreas Mohr02330fb2008-05-16 12:18:29 +02001251
Linus Torvalds1da177e2005-04-16 15:20:36 -07001252 /* add mixer controls */
1253 sw = snd_azf3328_mixer_controls;
Andreas Mohr02330fb2008-05-16 12:18:29 +02001254 for (idx = 0; idx < ARRAY_SIZE(snd_azf3328_mixer_controls);
1255 ++idx, ++sw) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001256 if ((err = snd_ctl_add(chip->card, snd_ctl_new1(sw, chip))) < 0)
1257 return err;
1258 }
1259 snd_component_add(card, "AZF3328 mixer");
1260 strcpy(card->mixername, "AZF3328 mixer");
1261
Linus Torvalds1da177e2005-04-16 15:20:36 -07001262 return 0;
1263}
Andreas Mohrb5dc20c2011-02-19 00:49:32 +01001264#endif /* AZF_USE_AC97_LAYER */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001265
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001266static int
Takashi Iwai95de7762005-11-17 15:02:42 +01001267snd_azf3328_hw_params(struct snd_pcm_substream *substream,
1268 struct snd_pcm_hw_params *hw_params)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001269{
Takashi Iwai4162cd32014-02-25 12:59:05 +01001270 return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001271}
1272
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001273static int
Takashi Iwai95de7762005-11-17 15:02:42 +01001274snd_azf3328_hw_free(struct snd_pcm_substream *substream)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001275{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001276 snd_pcm_lib_free_pages(substream);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001277 return 0;
1278}
1279
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001280static void
Andreas Mohrda237f32010-12-27 21:17:26 +01001281snd_azf3328_codec_setfmt(struct snd_azf3328_codec_data *codec,
Andreas Mohr627d3e72008-06-23 11:50:47 +02001282 enum azf_freq_t bitrate,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001283 unsigned int format_width,
1284 unsigned int channels
1285)
1286{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001287 unsigned long flags;
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001288 u16 val = 0xff00;
Andreas Mohr8d9a1142010-12-27 21:16:49 +01001289 u8 freq = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001290
Linus Torvalds1da177e2005-04-16 15:20:36 -07001291 switch (bitrate) {
Andreas Mohrc9ba3742011-01-25 06:46:31 +01001292 case AZF_FREQ_4000: freq = SOUNDFORMAT_FREQ_SUSPECTED_4000; break;
1293 case AZF_FREQ_4800: freq = SOUNDFORMAT_FREQ_SUSPECTED_4800; break;
1294 case AZF_FREQ_5512:
1295 /* the AZF3328 names it "5510" for some strange reason */
1296 freq = SOUNDFORMAT_FREQ_5510; break;
1297 case AZF_FREQ_6620: freq = SOUNDFORMAT_FREQ_6620; break;
1298 case AZF_FREQ_8000: freq = SOUNDFORMAT_FREQ_8000; break;
1299 case AZF_FREQ_9600: freq = SOUNDFORMAT_FREQ_9600; break;
1300 case AZF_FREQ_11025: freq = SOUNDFORMAT_FREQ_11025; break;
1301 case AZF_FREQ_13240: freq = SOUNDFORMAT_FREQ_SUSPECTED_13240; break;
1302 case AZF_FREQ_16000: freq = SOUNDFORMAT_FREQ_16000; break;
1303 case AZF_FREQ_22050: freq = SOUNDFORMAT_FREQ_22050; break;
1304 case AZF_FREQ_32000: freq = SOUNDFORMAT_FREQ_32000; break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001305 default:
Takashi Iwai99b359b2005-10-20 18:26:44 +02001306 snd_printk(KERN_WARNING "unknown bitrate %d, assuming 44.1kHz!\n", bitrate);
Andreas Mohr02330fb2008-05-16 12:18:29 +02001307 /* fall-through */
Andreas Mohrc9ba3742011-01-25 06:46:31 +01001308 case AZF_FREQ_44100: freq = SOUNDFORMAT_FREQ_44100; break;
1309 case AZF_FREQ_48000: freq = SOUNDFORMAT_FREQ_48000; break;
1310 case AZF_FREQ_66200: freq = SOUNDFORMAT_FREQ_SUSPECTED_66200; break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001311 }
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001312 /* val = 0xff07; 3m27.993s (65301Hz; -> 64000Hz???) hmm, 66120, 65967, 66123 */
1313 /* val = 0xff09; 17m15.098s (13123,478Hz; -> 12000Hz???) hmm, 13237.2Hz? */
1314 /* val = 0xff0a; 47m30.599s (4764,891Hz; -> 4800Hz???) yup, 4803Hz */
1315 /* val = 0xff0c; 57m0.510s (4010,263Hz; -> 4000Hz???) yup, 4003Hz */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001316 /* val = 0xff05; 5m11.556s (... -> 44100Hz) */
1317 /* val = 0xff03; 10m21.529s (21872,463Hz; -> 22050Hz???) */
1318 /* val = 0xff0f; 20m41.883s (10937,993Hz; -> 11025Hz???) */
1319 /* val = 0xff0d; 41m23.135s (5523,600Hz; -> 5512Hz???) */
1320 /* val = 0xff0e; 28m30.777s (8017Hz; -> 8000Hz???) */
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001321
Andreas Mohr8d9a1142010-12-27 21:16:49 +01001322 val |= freq;
1323
Linus Torvalds1da177e2005-04-16 15:20:36 -07001324 if (channels == 2)
1325 val |= SOUNDFORMAT_FLAG_2CHANNELS;
1326
1327 if (format_width == 16)
1328 val |= SOUNDFORMAT_FLAG_16BIT;
1329
Andreas Mohrda237f32010-12-27 21:17:26 +01001330 spin_lock_irqsave(codec->lock, flags);
Andreas Mohr02330fb2008-05-16 12:18:29 +02001331
Linus Torvalds1da177e2005-04-16 15:20:36 -07001332 /* set bitrate/format */
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001333 snd_azf3328_codec_outw(codec, IDX_IO_CODEC_SOUNDFORMAT, val);
Andreas Mohr02330fb2008-05-16 12:18:29 +02001334
Linus Torvalds1da177e2005-04-16 15:20:36 -07001335 /* changing the bitrate/format settings switches off the
1336 * audio output with an annoying click in case of 8/16bit format change
1337 * (maybe shutting down DAC/ADC?), thus immediately
1338 * do some tweaking to reenable it and get rid of the clicking
1339 * (FIXME: yes, it works, but what exactly am I doing here?? :)
1340 * FIXME: does this have some side effects for full-duplex
1341 * or other dramatic side effects? */
Andreas Mohradf59312010-12-27 21:16:43 +01001342 /* do it for non-capture codecs only */
Andreas Mohrda237f32010-12-27 21:17:26 +01001343 if (codec->type != AZF_CODEC_CAPTURE)
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001344 snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS,
1345 snd_azf3328_codec_inw(codec, IDX_IO_CODEC_DMA_FLAGS) |
1346 DMA_RUN_SOMETHING1 |
1347 DMA_RUN_SOMETHING2 |
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001348 SOMETHING_ALMOST_ALWAYS_SET |
1349 DMA_EPILOGUE_SOMETHING |
1350 DMA_SOMETHING_ELSE
1351 );
Linus Torvalds1da177e2005-04-16 15:20:36 -07001352
Andreas Mohrda237f32010-12-27 21:17:26 +01001353 spin_unlock_irqrestore(codec->lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001354}
1355
Andreas Mohr02330fb2008-05-16 12:18:29 +02001356static inline void
Andreas Mohrda237f32010-12-27 21:17:26 +01001357snd_azf3328_codec_setfmt_lowpower(struct snd_azf3328_codec_data *codec
Andreas Mohr02330fb2008-05-16 12:18:29 +02001358)
1359{
1360 /* choose lowest frequency for low power consumption.
1361 * While this will cause louder noise due to rather coarse frequency,
1362 * it should never matter since output should always
1363 * get disabled properly when idle anyway. */
Andreas Mohrda237f32010-12-27 21:17:26 +01001364 snd_azf3328_codec_setfmt(codec, AZF_FREQ_4000, 8, 1);
Andreas Mohr02330fb2008-05-16 12:18:29 +02001365}
1366
Andreas Mohr627d3e72008-06-23 11:50:47 +02001367static void
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001368snd_azf3328_ctrl_reg_6AH_update(struct snd_azf3328 *chip,
Andreas Mohr627d3e72008-06-23 11:50:47 +02001369 unsigned bitmask,
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001370 bool enable
Andreas Mohr627d3e72008-06-23 11:50:47 +02001371)
1372{
Andreas Mohr78df6172009-07-12 22:17:54 +02001373 bool do_mask = !enable;
1374 if (do_mask)
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001375 chip->shadow_reg_ctrl_6AH |= bitmask;
Andreas Mohr78df6172009-07-12 22:17:54 +02001376 else
1377 chip->shadow_reg_ctrl_6AH &= ~bitmask;
1378 snd_azf3328_dbgcodec("6AH_update mask 0x%04x do_mask %d: val 0x%04x\n",
1379 bitmask, do_mask, chip->shadow_reg_ctrl_6AH);
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001380 snd_azf3328_ctrl_outw(chip, IDX_IO_6AH, chip->shadow_reg_ctrl_6AH);
Andreas Mohr627d3e72008-06-23 11:50:47 +02001381}
1382
Andreas Mohr02330fb2008-05-16 12:18:29 +02001383static inline void
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001384snd_azf3328_ctrl_enable_codecs(struct snd_azf3328 *chip, bool enable)
Andreas Mohr02330fb2008-05-16 12:18:29 +02001385{
Andreas Mohr78df6172009-07-12 22:17:54 +02001386 snd_azf3328_dbgcodec("codec_enable %d\n", enable);
Andreas Mohr02330fb2008-05-16 12:18:29 +02001387 /* no idea what exactly is being done here, but I strongly assume it's
1388 * PM related */
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001389 snd_azf3328_ctrl_reg_6AH_update(
Andreas Mohr627d3e72008-06-23 11:50:47 +02001390 chip, IO_6A_PAUSE_PLAYBACK_BIT8, enable
Andreas Mohr02330fb2008-05-16 12:18:29 +02001391 );
1392}
1393
1394static void
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001395snd_azf3328_ctrl_codec_activity(struct snd_azf3328 *chip,
1396 enum snd_azf3328_codec_type codec_type,
1397 bool enable
Andreas Mohr02330fb2008-05-16 12:18:29 +02001398)
1399{
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001400 struct snd_azf3328_codec_data *codec = &chip->codecs[codec_type];
1401 bool need_change = (codec->running != enable);
Andreas Mohr02330fb2008-05-16 12:18:29 +02001402
Andreas Mohr78df6172009-07-12 22:17:54 +02001403 snd_azf3328_dbgcodec(
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001404 "codec_activity: %s codec, enable %d, need_change %d\n",
1405 codec->name, enable, need_change
Andreas Mohr02330fb2008-05-16 12:18:29 +02001406 );
1407 if (need_change) {
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001408 static const struct {
1409 enum snd_azf3328_codec_type other1;
1410 enum snd_azf3328_codec_type other2;
1411 } peer_codecs[3] =
1412 { { AZF_CODEC_CAPTURE, AZF_CODEC_I2S_OUT },
1413 { AZF_CODEC_PLAYBACK, AZF_CODEC_I2S_OUT },
1414 { AZF_CODEC_PLAYBACK, AZF_CODEC_CAPTURE } };
1415 bool call_function;
1416
1417 if (enable)
1418 /* if enable codec, call enable_codecs func
1419 to enable codec supply... */
1420 call_function = 1;
1421 else {
1422 /* ...otherwise call enable_codecs func
1423 (which globally shuts down operation of codecs)
1424 only in case the other codecs are currently
1425 not active either! */
Andreas Mohr78df6172009-07-12 22:17:54 +02001426 call_function =
1427 ((!chip->codecs[peer_codecs[codec_type].other1]
1428 .running)
1429 && (!chip->codecs[peer_codecs[codec_type].other2]
1430 .running));
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001431 }
1432 if (call_function)
1433 snd_azf3328_ctrl_enable_codecs(chip, enable);
Andreas Mohr02330fb2008-05-16 12:18:29 +02001434
1435 /* ...and adjust clock, too
1436 * (reduce noise and power consumption) */
1437 if (!enable)
Andreas Mohrda237f32010-12-27 21:17:26 +01001438 snd_azf3328_codec_setfmt_lowpower(codec);
Andreas Mohr78df6172009-07-12 22:17:54 +02001439 codec->running = enable;
Andreas Mohr02330fb2008-05-16 12:18:29 +02001440 }
Andreas Mohr02330fb2008-05-16 12:18:29 +02001441}
1442
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001443static void
Andreas Mohrda237f32010-12-27 21:17:26 +01001444snd_azf3328_codec_setdmaa(struct snd_azf3328_codec_data *codec,
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001445 unsigned long addr,
Andreas Mohr689c6912010-12-27 21:17:35 +01001446 unsigned int period_bytes,
1447 unsigned int buffer_bytes
Andreas Mohr02330fb2008-05-16 12:18:29 +02001448)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001449{
Andreas Mohr689c6912010-12-27 21:17:35 +01001450 WARN_ONCE(period_bytes & 1, "odd period length!?\n");
1451 WARN_ONCE(buffer_bytes != 2 * period_bytes,
1452 "missed our input expectations! %u vs. %u\n",
1453 buffer_bytes, period_bytes);
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001454 if (!codec->running) {
1455 /* AZF3328 uses a two buffer pointer DMA transfer approach */
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001456
Andreas Mohr689c6912010-12-27 21:17:35 +01001457 unsigned long flags;
Andreas Mohr02330fb2008-05-16 12:18:29 +02001458
1459 /* width 32bit (prevent overflow): */
Andreas Mohr689c6912010-12-27 21:17:35 +01001460 u32 area_length;
1461 struct codec_setup_io {
1462 u32 dma_start_1;
1463 u32 dma_start_2;
1464 u32 dma_lengths;
1465 } __attribute__((packed)) setup_io;
Andreas Mohr02330fb2008-05-16 12:18:29 +02001466
Andreas Mohr689c6912010-12-27 21:17:35 +01001467 area_length = buffer_bytes/2;
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001468
Andreas Mohr689c6912010-12-27 21:17:35 +01001469 setup_io.dma_start_1 = addr;
1470 setup_io.dma_start_2 = addr+area_length;
1471
1472 snd_azf3328_dbgcodec(
1473 "setdma: buffers %08x[%u] / %08x[%u], %u, %u\n",
1474 setup_io.dma_start_1, area_length,
1475 setup_io.dma_start_2, area_length,
1476 period_bytes, buffer_bytes);
1477
1478 /* Hmm, are we really supposed to decrement this by 1??
1479 Most definitely certainly not: configuring full length does
1480 work properly (i.e. likely better), and BTW we
1481 violated possibly differing frame sizes with this...
1482
1483 area_length--; |* max. index *|
1484 */
Andreas Mohr79741502010-11-21 12:09:32 +01001485
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001486 /* build combined I/O buffer length word */
Andreas Mohr689c6912010-12-27 21:17:35 +01001487 setup_io.dma_lengths = (area_length << 16) | (area_length);
1488
Andreas Mohrda237f32010-12-27 21:17:26 +01001489 spin_lock_irqsave(codec->lock, flags);
Andreas Mohr689c6912010-12-27 21:17:35 +01001490 snd_azf3328_codec_outl_multi(
1491 codec, IDX_IO_CODEC_DMA_START_1, &setup_io, 3
1492 );
Andreas Mohrda237f32010-12-27 21:17:26 +01001493 spin_unlock_irqrestore(codec->lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001494 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001495}
1496
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001497static int
Andreas Mohrda237f32010-12-27 21:17:26 +01001498snd_azf3328_pcm_prepare(struct snd_pcm_substream *substream)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001499{
Takashi Iwai95de7762005-11-17 15:02:42 +01001500 struct snd_pcm_runtime *runtime = substream->runtime;
Andreas Mohrda237f32010-12-27 21:17:26 +01001501 struct snd_azf3328_codec_data *codec = runtime->private_data;
Andreas Mohr34585592010-12-27 21:17:11 +01001502#if 0
Linus Torvalds1da177e2005-04-16 15:20:36 -07001503 unsigned int size = snd_pcm_lib_buffer_bytes(substream);
1504 unsigned int count = snd_pcm_lib_period_bytes(substream);
1505#endif
1506
Andreas Mohr34585592010-12-27 21:17:11 +01001507 codec->dma_base = runtime->dma_addr;
1508
Linus Torvalds1da177e2005-04-16 15:20:36 -07001509#if 0
Andreas Mohrda237f32010-12-27 21:17:26 +01001510 snd_azf3328_codec_setfmt(codec,
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001511 runtime->rate,
1512 snd_pcm_format_width(runtime->format),
1513 runtime->channels);
Andreas Mohrda237f32010-12-27 21:17:26 +01001514 snd_azf3328_codec_setdmaa(codec,
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001515 runtime->dma_addr, count, size);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001516#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001517 return 0;
1518}
1519
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001520static int
Andreas Mohrda237f32010-12-27 21:17:26 +01001521snd_azf3328_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001522{
Takashi Iwai95de7762005-11-17 15:02:42 +01001523 struct snd_azf3328 *chip = snd_pcm_substream_chip(substream);
1524 struct snd_pcm_runtime *runtime = substream->runtime;
Andreas Mohrda237f32010-12-27 21:17:26 +01001525 struct snd_azf3328_codec_data *codec = runtime->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001526 int result = 0;
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001527 u16 flags1;
Peter Senna Tschudine0f17c72013-09-22 20:44:12 +02001528 bool previously_muted = false;
Andreas Mohrda237f32010-12-27 21:17:26 +01001529 bool is_main_mixer_playback_codec = (AZF_CODEC_PLAYBACK == codec->type);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001530
Linus Torvalds1da177e2005-04-16 15:20:36 -07001531 switch (cmd) {
1532 case SNDRV_PCM_TRIGGER_START:
Andreas Mohr78df6172009-07-12 22:17:54 +02001533 snd_azf3328_dbgcodec("START %s\n", codec->name);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001534
Andreas Mohrda237f32010-12-27 21:17:26 +01001535 if (is_main_mixer_playback_codec) {
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001536 /* mute WaveOut (avoid clicking during setup) */
1537 previously_muted =
Andreas Mohrb5dc20c2011-02-19 00:49:32 +01001538 snd_azf3328_mixer_mute_control_pcm(
1539 chip, 1
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001540 );
1541 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001542
Andreas Mohrda237f32010-12-27 21:17:26 +01001543 snd_azf3328_codec_setfmt(codec,
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001544 runtime->rate,
1545 snd_pcm_format_width(runtime->format),
1546 runtime->channels);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001547
Andreas Mohrda237f32010-12-27 21:17:26 +01001548 spin_lock(codec->lock);
Andreas Mohr02330fb2008-05-16 12:18:29 +02001549 /* first, remember current value: */
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001550 flags1 = snd_azf3328_codec_inw(codec, IDX_IO_CODEC_DMA_FLAGS);
Andreas Mohr02330fb2008-05-16 12:18:29 +02001551
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001552 /* stop transfer */
1553 flags1 &= ~DMA_RESUME;
1554 snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, flags1);
Andreas Mohr02330fb2008-05-16 12:18:29 +02001555
Linus Torvalds1da177e2005-04-16 15:20:36 -07001556 /* FIXME: clear interrupts or what??? */
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001557 snd_azf3328_codec_outw(codec, IDX_IO_CODEC_IRQTYPE, 0xffff);
Andreas Mohrda237f32010-12-27 21:17:26 +01001558 spin_unlock(codec->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001559
Andreas Mohrda237f32010-12-27 21:17:26 +01001560 snd_azf3328_codec_setdmaa(codec, runtime->dma_addr,
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001561 snd_pcm_lib_period_bytes(substream),
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001562 snd_pcm_lib_buffer_bytes(substream)
1563 );
Linus Torvalds1da177e2005-04-16 15:20:36 -07001564
Andreas Mohrda237f32010-12-27 21:17:26 +01001565 spin_lock(codec->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001566#ifdef WIN9X
1567 /* FIXME: enable playback/recording??? */
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001568 flags1 |= DMA_RUN_SOMETHING1 | DMA_RUN_SOMETHING2;
1569 snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, flags1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001570
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001571 /* start transfer again */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001572 /* FIXME: what is this value (0x0010)??? */
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001573 flags1 |= DMA_RESUME | DMA_EPILOGUE_SOMETHING;
1574 snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, flags1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001575#else /* NT4 */
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001576 snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS,
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001577 0x0000);
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001578 snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS,
1579 DMA_RUN_SOMETHING1);
1580 snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS,
1581 DMA_RUN_SOMETHING1 |
1582 DMA_RUN_SOMETHING2);
1583 snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS,
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001584 DMA_RESUME |
1585 SOMETHING_ALMOST_ALWAYS_SET |
1586 DMA_EPILOGUE_SOMETHING |
1587 DMA_SOMETHING_ELSE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001588#endif
Andreas Mohrda237f32010-12-27 21:17:26 +01001589 spin_unlock(codec->lock);
1590 snd_azf3328_ctrl_codec_activity(chip, codec->type, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001591
Andreas Mohrda237f32010-12-27 21:17:26 +01001592 if (is_main_mixer_playback_codec) {
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001593 /* now unmute WaveOut */
1594 if (!previously_muted)
Andreas Mohrb5dc20c2011-02-19 00:49:32 +01001595 snd_azf3328_mixer_mute_control_pcm(
1596 chip, 0
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001597 );
1598 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001599
Andreas Mohr78df6172009-07-12 22:17:54 +02001600 snd_azf3328_dbgcodec("STARTED %s\n", codec->name);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001601 break;
Andreas Mohrca54bde2006-05-17 11:02:24 +02001602 case SNDRV_PCM_TRIGGER_RESUME:
Andreas Mohr78df6172009-07-12 22:17:54 +02001603 snd_azf3328_dbgcodec("RESUME %s\n", codec->name);
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001604 /* resume codec if we were active */
Andreas Mohrda237f32010-12-27 21:17:26 +01001605 spin_lock(codec->lock);
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001606 if (codec->running)
1607 snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS,
1608 snd_azf3328_codec_inw(
1609 codec, IDX_IO_CODEC_DMA_FLAGS
1610 ) | DMA_RESUME
1611 );
Andreas Mohrda237f32010-12-27 21:17:26 +01001612 spin_unlock(codec->lock);
Andreas Mohrca54bde2006-05-17 11:02:24 +02001613 break;
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001614 case SNDRV_PCM_TRIGGER_STOP:
Andreas Mohr78df6172009-07-12 22:17:54 +02001615 snd_azf3328_dbgcodec("STOP %s\n", codec->name);
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001616
Andreas Mohrda237f32010-12-27 21:17:26 +01001617 if (is_main_mixer_playback_codec) {
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001618 /* mute WaveOut (avoid clicking during setup) */
1619 previously_muted =
Andreas Mohrb5dc20c2011-02-19 00:49:32 +01001620 snd_azf3328_mixer_mute_control_pcm(
1621 chip, 1
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001622 );
1623 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001624
Andreas Mohrda237f32010-12-27 21:17:26 +01001625 spin_lock(codec->lock);
Andreas Mohr02330fb2008-05-16 12:18:29 +02001626 /* first, remember current value: */
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001627 flags1 = snd_azf3328_codec_inw(codec, IDX_IO_CODEC_DMA_FLAGS);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001628
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001629 /* stop transfer */
1630 flags1 &= ~DMA_RESUME;
1631 snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, flags1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001632
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001633 /* hmm, is this really required? we're resetting the same bit
1634 * immediately thereafter... */
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001635 flags1 |= DMA_RUN_SOMETHING1;
1636 snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, flags1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001637
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001638 flags1 &= ~DMA_RUN_SOMETHING1;
1639 snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, flags1);
Andreas Mohrda237f32010-12-27 21:17:26 +01001640 spin_unlock(codec->lock);
1641 snd_azf3328_ctrl_codec_activity(chip, codec->type, 0);
Andreas Mohr02330fb2008-05-16 12:18:29 +02001642
Andreas Mohrda237f32010-12-27 21:17:26 +01001643 if (is_main_mixer_playback_codec) {
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001644 /* now unmute WaveOut */
1645 if (!previously_muted)
Andreas Mohrb5dc20c2011-02-19 00:49:32 +01001646 snd_azf3328_mixer_mute_control_pcm(
1647 chip, 0
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001648 );
1649 }
Andreas Mohr02330fb2008-05-16 12:18:29 +02001650
Andreas Mohr78df6172009-07-12 22:17:54 +02001651 snd_azf3328_dbgcodec("STOPPED %s\n", codec->name);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001652 break;
Andreas Mohrca54bde2006-05-17 11:02:24 +02001653 case SNDRV_PCM_TRIGGER_SUSPEND:
Andreas Mohr78df6172009-07-12 22:17:54 +02001654 snd_azf3328_dbgcodec("SUSPEND %s\n", codec->name);
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001655 /* make sure codec is stopped */
1656 snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS,
1657 snd_azf3328_codec_inw(
1658 codec, IDX_IO_CODEC_DMA_FLAGS
1659 ) & ~DMA_RESUME
1660 );
Andreas Mohrca54bde2006-05-17 11:02:24 +02001661 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001662 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
Takashi Iwai99b359b2005-10-20 18:26:44 +02001663 snd_printk(KERN_ERR "FIXME: SNDRV_PCM_TRIGGER_PAUSE_PUSH NIY!\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001664 break;
1665 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
Takashi Iwai99b359b2005-10-20 18:26:44 +02001666 snd_printk(KERN_ERR "FIXME: SNDRV_PCM_TRIGGER_PAUSE_RELEASE NIY!\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001667 break;
1668 default:
Andreas Mohr78df6172009-07-12 22:17:54 +02001669 snd_printk(KERN_ERR "FIXME: unknown trigger mode!\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001670 return -EINVAL;
1671 }
Andreas Mohr02330fb2008-05-16 12:18:29 +02001672
Linus Torvalds1da177e2005-04-16 15:20:36 -07001673 return result;
1674}
1675
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001676static snd_pcm_uframes_t
Andreas Mohrda237f32010-12-27 21:17:26 +01001677snd_azf3328_pcm_pointer(struct snd_pcm_substream *substream
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001678)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001679{
Andreas Mohrda237f32010-12-27 21:17:26 +01001680 const struct snd_azf3328_codec_data *codec =
1681 substream->runtime->private_data;
Andreas Mohr34585592010-12-27 21:17:11 +01001682 unsigned long result;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001683 snd_pcm_uframes_t frmres;
1684
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001685 result = snd_azf3328_codec_inl(codec, IDX_IO_CODEC_DMA_CURRPOS);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001686
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001687 /* calculate offset */
Andreas Mohr34585592010-12-27 21:17:11 +01001688#ifdef QUERY_HARDWARE
1689 result -= snd_azf3328_codec_inl(codec, IDX_IO_CODEC_DMA_START_1);
1690#else
1691 result -= codec->dma_base;
1692#endif
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001693 frmres = bytes_to_frames( substream->runtime, result);
Andreas Mohradf59312010-12-27 21:16:43 +01001694 snd_azf3328_dbgcodec("%08li %s @ 0x%8lx, frames %8ld\n",
1695 jiffies, codec->name, result, frmres);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001696 return frmres;
1697}
1698
Andreas Mohr02330fb2008-05-16 12:18:29 +02001699/******************************************************************/
1700
1701#ifdef SUPPORT_GAMEPORT
1702static inline void
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001703snd_azf3328_gameport_irq_enable(struct snd_azf3328 *chip,
1704 bool enable
1705)
Andreas Mohr02330fb2008-05-16 12:18:29 +02001706{
1707 snd_azf3328_io_reg_setb(
1708 chip->game_io+IDX_GAME_HWCONFIG,
1709 GAME_HWCFG_IRQ_ENABLE,
1710 enable
1711 );
1712}
1713
1714static inline void
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001715snd_azf3328_gameport_legacy_address_enable(struct snd_azf3328 *chip,
1716 bool enable
1717)
Andreas Mohr02330fb2008-05-16 12:18:29 +02001718{
1719 snd_azf3328_io_reg_setb(
1720 chip->game_io+IDX_GAME_HWCONFIG,
1721 GAME_HWCFG_LEGACY_ADDRESS_ENABLE,
1722 enable
1723 );
1724}
1725
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001726static void
1727snd_azf3328_gameport_set_counter_frequency(struct snd_azf3328 *chip,
1728 unsigned int freq_cfg
1729)
Andreas Mohr02330fb2008-05-16 12:18:29 +02001730{
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001731 snd_azf3328_io_reg_setb(
1732 chip->game_io+IDX_GAME_HWCONFIG,
1733 0x02,
1734 (freq_cfg & 1) != 0
1735 );
1736 snd_azf3328_io_reg_setb(
1737 chip->game_io+IDX_GAME_HWCONFIG,
1738 0x04,
1739 (freq_cfg & 2) != 0
1740 );
1741}
1742
1743static inline void
1744snd_azf3328_gameport_axis_circuit_enable(struct snd_azf3328 *chip, bool enable)
1745{
1746 snd_azf3328_ctrl_reg_6AH_update(
Andreas Mohr627d3e72008-06-23 11:50:47 +02001747 chip, IO_6A_SOMETHING2_GAMEPORT, enable
Andreas Mohr02330fb2008-05-16 12:18:29 +02001748 );
1749}
1750
1751static inline void
1752snd_azf3328_gameport_interrupt(struct snd_azf3328 *chip)
1753{
1754 /*
1755 * skeleton handler only
1756 * (we do not want axis reading in interrupt handler - too much load!)
1757 */
1758 snd_azf3328_dbggame("gameport irq\n");
1759
1760 /* this should ACK the gameport IRQ properly, hopefully. */
1761 snd_azf3328_game_inw(chip, IDX_GAME_AXIS_VALUE);
1762}
1763
1764static int
1765snd_azf3328_gameport_open(struct gameport *gameport, int mode)
1766{
1767 struct snd_azf3328 *chip = gameport_get_port_data(gameport);
1768 int res;
1769
1770 snd_azf3328_dbggame("gameport_open, mode %d\n", mode);
1771 switch (mode) {
1772 case GAMEPORT_MODE_COOKED:
1773 case GAMEPORT_MODE_RAW:
1774 res = 0;
1775 break;
1776 default:
1777 res = -1;
1778 break;
1779 }
1780
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001781 snd_azf3328_gameport_set_counter_frequency(chip,
1782 GAME_HWCFG_ADC_COUNTER_FREQ_STD);
Andreas Mohr02330fb2008-05-16 12:18:29 +02001783 snd_azf3328_gameport_axis_circuit_enable(chip, (res == 0));
1784
1785 return res;
1786}
1787
1788static void
1789snd_azf3328_gameport_close(struct gameport *gameport)
1790{
1791 struct snd_azf3328 *chip = gameport_get_port_data(gameport);
1792
1793 snd_azf3328_dbggame("gameport_close\n");
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001794 snd_azf3328_gameport_set_counter_frequency(chip,
1795 GAME_HWCFG_ADC_COUNTER_FREQ_1_200);
Andreas Mohr02330fb2008-05-16 12:18:29 +02001796 snd_azf3328_gameport_axis_circuit_enable(chip, 0);
1797}
1798
1799static int
1800snd_azf3328_gameport_cooked_read(struct gameport *gameport,
1801 int *axes,
1802 int *buttons
1803)
1804{
1805 struct snd_azf3328 *chip = gameport_get_port_data(gameport);
1806 int i;
1807 u8 val;
1808 unsigned long flags;
1809
Takashi Iwaida3cec32008-08-08 17:12:14 +02001810 if (snd_BUG_ON(!chip))
1811 return 0;
Andreas Mohr02330fb2008-05-16 12:18:29 +02001812
1813 spin_lock_irqsave(&chip->reg_lock, flags);
1814 val = snd_azf3328_game_inb(chip, IDX_GAME_LEGACY_COMPATIBLE);
1815 *buttons = (~(val) >> 4) & 0xf;
1816
1817 /* ok, this one is a bit dirty: cooked_read is being polled by a timer,
1818 * thus we're atomic and cannot actively wait in here
1819 * (which would be useful for us since it probably would be better
1820 * to trigger a measurement in here, then wait a short amount of
1821 * time until it's finished, then read values of _this_ measurement).
1822 *
1823 * Thus we simply resort to reading values if they're available already
1824 * and trigger the next measurement.
1825 */
1826
1827 val = snd_azf3328_game_inb(chip, IDX_GAME_AXES_CONFIG);
1828 if (val & GAME_AXES_SAMPLING_READY) {
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001829 for (i = 0; i < ARRAY_SIZE(chip->axes); ++i) {
Andreas Mohr02330fb2008-05-16 12:18:29 +02001830 /* configure the axis to read */
1831 val = (i << 4) | 0x0f;
1832 snd_azf3328_game_outb(chip, IDX_GAME_AXES_CONFIG, val);
1833
1834 chip->axes[i] = snd_azf3328_game_inw(
1835 chip, IDX_GAME_AXIS_VALUE
1836 );
1837 }
1838 }
1839
Andreas Mohradf59312010-12-27 21:16:43 +01001840 /* trigger next sampling of axes, to be evaluated the next time we
Andreas Mohr02330fb2008-05-16 12:18:29 +02001841 * enter this function */
1842
1843 /* for some very, very strange reason we cannot enable
1844 * Measurement Ready monitoring for all axes here,
1845 * at least not when only one joystick connected */
1846 val = 0x03; /* we're able to monitor axes 1 and 2 only */
1847 snd_azf3328_game_outb(chip, IDX_GAME_AXES_CONFIG, val);
1848
1849 snd_azf3328_game_outw(chip, IDX_GAME_AXIS_VALUE, 0xffff);
1850 spin_unlock_irqrestore(&chip->reg_lock, flags);
1851
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001852 for (i = 0; i < ARRAY_SIZE(chip->axes); i++) {
Andreas Mohr02330fb2008-05-16 12:18:29 +02001853 axes[i] = chip->axes[i];
1854 if (axes[i] == 0xffff)
1855 axes[i] = -1;
1856 }
1857
1858 snd_azf3328_dbggame("cooked_read: axes %d %d %d %d buttons %d\n",
1859 axes[0], axes[1], axes[2], axes[3], *buttons
1860 );
1861
1862 return 0;
1863}
1864
Bill Pembertone23e7a12012-12-06 12:35:10 -05001865static int
Andreas Mohr02330fb2008-05-16 12:18:29 +02001866snd_azf3328_gameport(struct snd_azf3328 *chip, int dev)
1867{
1868 struct gameport *gp;
1869
Andreas Mohr02330fb2008-05-16 12:18:29 +02001870 chip->gameport = gp = gameport_allocate_port();
1871 if (!gp) {
1872 printk(KERN_ERR "azt3328: cannot alloc memory for gameport\n");
1873 return -ENOMEM;
1874 }
1875
1876 gameport_set_name(gp, "AZF3328 Gameport");
1877 gameport_set_phys(gp, "pci%s/gameport0", pci_name(chip->pci));
1878 gameport_set_dev_parent(gp, &chip->pci->dev);
Andreas Mohr627d3e72008-06-23 11:50:47 +02001879 gp->io = chip->game_io;
Andreas Mohr02330fb2008-05-16 12:18:29 +02001880 gameport_set_port_data(gp, chip);
1881
1882 gp->open = snd_azf3328_gameport_open;
1883 gp->close = snd_azf3328_gameport_close;
1884 gp->fuzz = 16; /* seems ok */
1885 gp->cooked_read = snd_azf3328_gameport_cooked_read;
1886
1887 /* DISABLE legacy address: we don't need it! */
1888 snd_azf3328_gameport_legacy_address_enable(chip, 0);
1889
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001890 snd_azf3328_gameport_set_counter_frequency(chip,
1891 GAME_HWCFG_ADC_COUNTER_FREQ_1_200);
Andreas Mohr02330fb2008-05-16 12:18:29 +02001892 snd_azf3328_gameport_axis_circuit_enable(chip, 0);
1893
1894 gameport_register_port(chip->gameport);
1895
1896 return 0;
1897}
1898
1899static void
1900snd_azf3328_gameport_free(struct snd_azf3328 *chip)
1901{
1902 if (chip->gameport) {
1903 gameport_unregister_port(chip->gameport);
1904 chip->gameport = NULL;
1905 }
1906 snd_azf3328_gameport_irq_enable(chip, 0);
1907}
1908#else
1909static inline int
1910snd_azf3328_gameport(struct snd_azf3328 *chip, int dev) { return -ENOSYS; }
1911static inline void
1912snd_azf3328_gameport_free(struct snd_azf3328 *chip) { }
1913static inline void
1914snd_azf3328_gameport_interrupt(struct snd_azf3328 *chip)
1915{
1916 printk(KERN_WARNING "huh, game port IRQ occurred!?\n");
1917}
1918#endif /* SUPPORT_GAMEPORT */
1919
1920/******************************************************************/
1921
Andreas Mohr627d3e72008-06-23 11:50:47 +02001922static inline void
1923snd_azf3328_irq_log_unknown_type(u8 which)
1924{
Andreas Mohr78df6172009-07-12 22:17:54 +02001925 snd_azf3328_dbgcodec(
Andreas Mohr627d3e72008-06-23 11:50:47 +02001926 "azt3328: unknown IRQ type (%x) occurred, please report!\n",
1927 which
1928 );
1929}
1930
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001931static inline void
Andreas Mohrda237f32010-12-27 21:17:26 +01001932snd_azf3328_pcm_interrupt(const struct snd_azf3328_codec_data *first_codec,
1933 u8 status
1934)
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001935{
1936 u8 which;
1937 enum snd_azf3328_codec_type codec_type;
Andreas Mohrda237f32010-12-27 21:17:26 +01001938 const struct snd_azf3328_codec_data *codec = first_codec;
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001939
1940 for (codec_type = AZF_CODEC_PLAYBACK;
1941 codec_type <= AZF_CODEC_I2S_OUT;
Andreas Mohrda237f32010-12-27 21:17:26 +01001942 ++codec_type, ++codec) {
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001943
1944 /* skip codec if there's no interrupt for it */
1945 if (!(status & (1 << codec_type)))
1946 continue;
1947
Andreas Mohrda237f32010-12-27 21:17:26 +01001948 spin_lock(codec->lock);
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001949 which = snd_azf3328_codec_inb(codec, IDX_IO_CODEC_IRQTYPE);
1950 /* ack all IRQ types immediately */
1951 snd_azf3328_codec_outb(codec, IDX_IO_CODEC_IRQTYPE, which);
Andreas Mohrda237f32010-12-27 21:17:26 +01001952 spin_unlock(codec->lock);
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001953
Andreas Mohrda237f32010-12-27 21:17:26 +01001954 if (codec->substream) {
Andreas Mohr78df6172009-07-12 22:17:54 +02001955 snd_pcm_period_elapsed(codec->substream);
1956 snd_azf3328_dbgcodec("%s period done (#%x), @ %x\n",
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001957 codec->name,
1958 which,
1959 snd_azf3328_codec_inl(
1960 codec, IDX_IO_CODEC_DMA_CURRPOS
1961 )
1962 );
1963 } else
1964 printk(KERN_WARNING "azt3328: irq handler problem!\n");
1965 if (which & IRQ_SOMETHING)
1966 snd_azf3328_irq_log_unknown_type(which);
1967 }
1968}
1969
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001970static irqreturn_t
David Howells7d12e782006-10-05 14:55:46 +01001971snd_azf3328_interrupt(int irq, void *dev_id)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001972{
Takashi Iwai95de7762005-11-17 15:02:42 +01001973 struct snd_azf3328 *chip = dev_id;
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001974 u8 status;
Andreas Mohr78df6172009-07-12 22:17:54 +02001975#if DEBUG_CODEC
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001976 static unsigned long irq_count;
Andreas Mohr02330fb2008-05-16 12:18:29 +02001977#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001978
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001979 status = snd_azf3328_ctrl_inb(chip, IDX_IO_IRQSTATUS);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001980
1981 /* fast path out, to ease interrupt sharing */
Andreas Mohr02330fb2008-05-16 12:18:29 +02001982 if (!(status &
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001983 (IRQ_PLAYBACK|IRQ_RECORDING|IRQ_I2S_OUT
1984 |IRQ_GAMEPORT|IRQ_MPU401|IRQ_TIMER)
Andreas Mohr02330fb2008-05-16 12:18:29 +02001985 ))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001986 return IRQ_NONE; /* must be interrupt for another device */
1987
Andreas Mohr78df6172009-07-12 22:17:54 +02001988 snd_azf3328_dbgcodec(
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001989 "irq_count %ld! IDX_IO_IRQSTATUS %04x\n",
Andreas Mohr627d3e72008-06-23 11:50:47 +02001990 irq_count++ /* debug-only */,
Andreas Mohr627d3e72008-06-23 11:50:47 +02001991 status
1992 );
Andreas Mohr02330fb2008-05-16 12:18:29 +02001993
Andreas Mohre2f87262006-05-17 11:04:19 +02001994 if (status & IRQ_TIMER) {
Andreas Mohr78df6172009-07-12 22:17:54 +02001995 /* snd_azf3328_dbgcodec("timer %ld\n",
Andreas Mohr02330fb2008-05-16 12:18:29 +02001996 snd_azf3328_codec_inl(chip, IDX_IO_TIMER_VALUE)
1997 & TIMER_VALUE_MASK
1998 ); */
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001999 if (chip->timer)
2000 snd_timer_interrupt(chip->timer, chip->timer->sticks);
2001 /* ACK timer */
2002 spin_lock(&chip->reg_lock);
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002003 snd_azf3328_ctrl_outb(chip, IDX_IO_TIMER_VALUE + 3, 0x07);
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002004 spin_unlock(&chip->reg_lock);
Andreas Mohr78df6172009-07-12 22:17:54 +02002005 snd_azf3328_dbgcodec("azt3328: timer IRQ\n");
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002006 }
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002007
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002008 if (status & (IRQ_PLAYBACK|IRQ_RECORDING|IRQ_I2S_OUT))
Andreas Mohrda237f32010-12-27 21:17:26 +01002009 snd_azf3328_pcm_interrupt(chip->codecs, status);
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002010
Andreas Mohr02330fb2008-05-16 12:18:29 +02002011 if (status & IRQ_GAMEPORT)
2012 snd_azf3328_gameport_interrupt(chip);
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002013
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002014 /* MPU401 has less critical IRQ requirements
2015 * than timer and playback/recording, right? */
Andreas Mohre2f87262006-05-17 11:04:19 +02002016 if (status & IRQ_MPU401) {
David Howells7d12e782006-10-05 14:55:46 +01002017 snd_mpu401_uart_interrupt(irq, chip->rmidi->private_data);
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002018
2019 /* hmm, do we have to ack the IRQ here somehow?
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002020 * If so, then I don't know how yet... */
Andreas Mohr78df6172009-07-12 22:17:54 +02002021 snd_azf3328_dbgcodec("azt3328: MPU401 IRQ\n");
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002022 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002023 return IRQ_HANDLED;
2024}
2025
2026/*****************************************************************/
2027
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002028/* as long as we think we have identical snd_pcm_hardware parameters
2029 for playback, capture and i2s out, we can use the same physical struct
2030 since the struct is simply being copied into a member.
2031*/
2032static const struct snd_pcm_hardware snd_azf3328_hardware =
Linus Torvalds1da177e2005-04-16 15:20:36 -07002033{
2034 /* FIXME!! Correct? */
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002035 .info = SNDRV_PCM_INFO_MMAP |
2036 SNDRV_PCM_INFO_INTERLEAVED |
2037 SNDRV_PCM_INFO_MMAP_VALID,
2038 .formats = SNDRV_PCM_FMTBIT_S8 |
2039 SNDRV_PCM_FMTBIT_U8 |
2040 SNDRV_PCM_FMTBIT_S16_LE |
2041 SNDRV_PCM_FMTBIT_U16_LE,
2042 .rates = SNDRV_PCM_RATE_5512 |
2043 SNDRV_PCM_RATE_8000_48000 |
2044 SNDRV_PCM_RATE_KNOT,
Andreas Mohr02330fb2008-05-16 12:18:29 +02002045 .rate_min = AZF_FREQ_4000,
2046 .rate_max = AZF_FREQ_66200,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002047 .channels_min = 1,
2048 .channels_max = 2,
Andreas Mohr79741502010-11-21 12:09:32 +01002049 .buffer_bytes_max = (64*1024),
2050 .period_bytes_min = 1024,
2051 .period_bytes_max = (32*1024),
2052 /* We simply have two DMA areas (instead of a list of descriptors
2053 such as other cards); I believe that this is a fixed hardware
2054 attribute and there isn't much driver magic to be done to expand it.
2055 Thus indicate that we have at least and at most 2 periods. */
2056 .periods_min = 2,
2057 .periods_max = 2,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002058 /* FIXME: maybe that card actually has a FIFO?
2059 * Hmm, it seems newer revisions do have one, but we still don't know
2060 * its size... */
2061 .fifo_size = 0,
2062};
2063
Linus Torvalds1da177e2005-04-16 15:20:36 -07002064
2065static unsigned int snd_azf3328_fixed_rates[] = {
Andreas Mohr02330fb2008-05-16 12:18:29 +02002066 AZF_FREQ_4000,
2067 AZF_FREQ_4800,
2068 AZF_FREQ_5512,
2069 AZF_FREQ_6620,
2070 AZF_FREQ_8000,
2071 AZF_FREQ_9600,
2072 AZF_FREQ_11025,
2073 AZF_FREQ_13240,
2074 AZF_FREQ_16000,
2075 AZF_FREQ_22050,
2076 AZF_FREQ_32000,
2077 AZF_FREQ_44100,
2078 AZF_FREQ_48000,
2079 AZF_FREQ_66200
2080};
2081
Takashi Iwai95de7762005-11-17 15:02:42 +01002082static struct snd_pcm_hw_constraint_list snd_azf3328_hw_constraints_rates = {
Andreas Mohr02330fb2008-05-16 12:18:29 +02002083 .count = ARRAY_SIZE(snd_azf3328_fixed_rates),
Linus Torvalds1da177e2005-04-16 15:20:36 -07002084 .list = snd_azf3328_fixed_rates,
2085 .mask = 0,
2086};
2087
2088/*****************************************************************/
2089
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002090static int
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002091snd_azf3328_pcm_open(struct snd_pcm_substream *substream,
2092 enum snd_azf3328_codec_type codec_type
2093)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002094{
Takashi Iwai95de7762005-11-17 15:02:42 +01002095 struct snd_azf3328 *chip = snd_pcm_substream_chip(substream);
2096 struct snd_pcm_runtime *runtime = substream->runtime;
Andreas Mohrda237f32010-12-27 21:17:26 +01002097 struct snd_azf3328_codec_data *codec = &chip->codecs[codec_type];
Linus Torvalds1da177e2005-04-16 15:20:36 -07002098
Andreas Mohrda237f32010-12-27 21:17:26 +01002099 codec->substream = substream;
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002100
2101 /* same parameters for all our codecs - at least we think so... */
2102 runtime->hw = snd_azf3328_hardware;
2103
Linus Torvalds1da177e2005-04-16 15:20:36 -07002104 snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
2105 &snd_azf3328_hw_constraints_rates);
Andreas Mohrda237f32010-12-27 21:17:26 +01002106 runtime->private_data = codec;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002107 return 0;
2108}
2109
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002110static int
Andreas Mohrda237f32010-12-27 21:17:26 +01002111snd_azf3328_pcm_playback_open(struct snd_pcm_substream *substream)
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002112{
2113 return snd_azf3328_pcm_open(substream, AZF_CODEC_PLAYBACK);
2114}
2115
2116static int
Andreas Mohrda237f32010-12-27 21:17:26 +01002117snd_azf3328_pcm_capture_open(struct snd_pcm_substream *substream)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002118{
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002119 return snd_azf3328_pcm_open(substream, AZF_CODEC_CAPTURE);
2120}
2121
2122static int
Andreas Mohrda237f32010-12-27 21:17:26 +01002123snd_azf3328_pcm_i2s_out_open(struct snd_pcm_substream *substream)
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002124{
2125 return snd_azf3328_pcm_open(substream, AZF_CODEC_I2S_OUT);
2126}
2127
2128static int
Andreas Mohrda237f32010-12-27 21:17:26 +01002129snd_azf3328_pcm_close(struct snd_pcm_substream *substream
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002130)
2131{
Andreas Mohrda237f32010-12-27 21:17:26 +01002132 struct snd_azf3328_codec_data *codec =
2133 substream->runtime->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002134
Andreas Mohrda237f32010-12-27 21:17:26 +01002135 codec->substream = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002136 return 0;
2137}
2138
Linus Torvalds1da177e2005-04-16 15:20:36 -07002139/******************************************************************/
2140
Takashi Iwai95de7762005-11-17 15:02:42 +01002141static struct snd_pcm_ops snd_azf3328_playback_ops = {
Andreas Mohrda237f32010-12-27 21:17:26 +01002142 .open = snd_azf3328_pcm_playback_open,
2143 .close = snd_azf3328_pcm_close,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002144 .ioctl = snd_pcm_lib_ioctl,
2145 .hw_params = snd_azf3328_hw_params,
2146 .hw_free = snd_azf3328_hw_free,
Andreas Mohrda237f32010-12-27 21:17:26 +01002147 .prepare = snd_azf3328_pcm_prepare,
2148 .trigger = snd_azf3328_pcm_trigger,
2149 .pointer = snd_azf3328_pcm_pointer
Linus Torvalds1da177e2005-04-16 15:20:36 -07002150};
2151
Takashi Iwai95de7762005-11-17 15:02:42 +01002152static struct snd_pcm_ops snd_azf3328_capture_ops = {
Andreas Mohrda237f32010-12-27 21:17:26 +01002153 .open = snd_azf3328_pcm_capture_open,
2154 .close = snd_azf3328_pcm_close,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002155 .ioctl = snd_pcm_lib_ioctl,
2156 .hw_params = snd_azf3328_hw_params,
2157 .hw_free = snd_azf3328_hw_free,
Andreas Mohrda237f32010-12-27 21:17:26 +01002158 .prepare = snd_azf3328_pcm_prepare,
2159 .trigger = snd_azf3328_pcm_trigger,
2160 .pointer = snd_azf3328_pcm_pointer
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002161};
2162
2163static struct snd_pcm_ops snd_azf3328_i2s_out_ops = {
Andreas Mohrda237f32010-12-27 21:17:26 +01002164 .open = snd_azf3328_pcm_i2s_out_open,
2165 .close = snd_azf3328_pcm_close,
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002166 .ioctl = snd_pcm_lib_ioctl,
2167 .hw_params = snd_azf3328_hw_params,
2168 .hw_free = snd_azf3328_hw_free,
Andreas Mohrda237f32010-12-27 21:17:26 +01002169 .prepare = snd_azf3328_pcm_prepare,
2170 .trigger = snd_azf3328_pcm_trigger,
2171 .pointer = snd_azf3328_pcm_pointer
Linus Torvalds1da177e2005-04-16 15:20:36 -07002172};
2173
Bill Pembertone23e7a12012-12-06 12:35:10 -05002174static int
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002175snd_azf3328_pcm(struct snd_azf3328 *chip)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002176{
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002177enum { AZF_PCMDEV_STD, AZF_PCMDEV_I2S_OUT, NUM_AZF_PCMDEVS }; /* pcm devices */
2178
Takashi Iwai95de7762005-11-17 15:02:42 +01002179 struct snd_pcm *pcm;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002180 int err;
2181
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002182 err = snd_pcm_new(chip->card, "AZF3328 DSP", AZF_PCMDEV_STD,
2183 1, 1, &pcm);
2184 if (err < 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002185 return err;
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002186 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
2187 &snd_azf3328_playback_ops);
2188 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
2189 &snd_azf3328_capture_ops);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002190
2191 pcm->private_data = chip;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002192 pcm->info_flags = 0;
2193 strcpy(pcm->name, chip->card->shortname);
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002194 /* same pcm object for playback/capture (see snd_pcm_new() above) */
2195 chip->pcm[AZF_CODEC_PLAYBACK] = pcm;
2196 chip->pcm[AZF_CODEC_CAPTURE] = pcm;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002197
2198 snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002199 snd_dma_pci_data(chip->pci),
2200 64*1024, 64*1024);
2201
2202 err = snd_pcm_new(chip->card, "AZF3328 I2S OUT", AZF_PCMDEV_I2S_OUT,
2203 1, 0, &pcm);
2204 if (err < 0)
2205 return err;
2206 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
2207 &snd_azf3328_i2s_out_ops);
2208
2209 pcm->private_data = chip;
2210 pcm->info_flags = 0;
2211 strcpy(pcm->name, chip->card->shortname);
2212 chip->pcm[AZF_CODEC_I2S_OUT] = pcm;
2213
2214 snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
2215 snd_dma_pci_data(chip->pci),
2216 64*1024, 64*1024);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002217
Linus Torvalds1da177e2005-04-16 15:20:36 -07002218 return 0;
2219}
2220
2221/******************************************************************/
2222
Andreas Mohr02330fb2008-05-16 12:18:29 +02002223/*** NOTE: the physical timer resolution actually is 1024000 ticks per second
2224 *** (probably derived from main crystal via a divider of 24),
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002225 *** but announcing those attributes to user-space would make programs
2226 *** configure the timer to a 1 tick value, resulting in an absolutely fatal
2227 *** timer IRQ storm.
2228 *** Thus I chose to announce a down-scaled virtual timer to the outside and
2229 *** calculate real timer countdown values internally.
2230 *** (the scale factor can be set via module parameter "seqtimer_scaling").
2231 ***/
2232
2233static int
Takashi Iwai95de7762005-11-17 15:02:42 +01002234snd_azf3328_timer_start(struct snd_timer *timer)
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002235{
Takashi Iwai95de7762005-11-17 15:02:42 +01002236 struct snd_azf3328 *chip;
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002237 unsigned long flags;
2238 unsigned int delay;
2239
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002240 chip = snd_timer_chip(timer);
2241 delay = ((timer->sticks * seqtimer_scaling) - 1) & TIMER_VALUE_MASK;
Andreas Mohre2f87262006-05-17 11:04:19 +02002242 if (delay < 49) {
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002243 /* uhoh, that's not good, since user-space won't know about
2244 * this timing tweak
2245 * (we need to do it to avoid a lockup, though) */
2246
2247 snd_azf3328_dbgtimer("delay was too low (%d)!\n", delay);
2248 delay = 49; /* minimum time is 49 ticks */
2249 }
Andreas Mohradf59312010-12-27 21:16:43 +01002250 snd_azf3328_dbgtimer("setting timer countdown value %d\n", delay);
Andreas Mohr02330fb2008-05-16 12:18:29 +02002251 delay |= TIMER_COUNTDOWN_ENABLE | TIMER_IRQ_ENABLE;
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002252 spin_lock_irqsave(&chip->reg_lock, flags);
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002253 snd_azf3328_ctrl_outl(chip, IDX_IO_TIMER_VALUE, delay);
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002254 spin_unlock_irqrestore(&chip->reg_lock, flags);
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002255 return 0;
2256}
2257
2258static int
Takashi Iwai95de7762005-11-17 15:02:42 +01002259snd_azf3328_timer_stop(struct snd_timer *timer)
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002260{
Takashi Iwai95de7762005-11-17 15:02:42 +01002261 struct snd_azf3328 *chip;
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002262 unsigned long flags;
2263
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002264 chip = snd_timer_chip(timer);
2265 spin_lock_irqsave(&chip->reg_lock, flags);
2266 /* disable timer countdown and interrupt */
Andreas Mohr79741502010-11-21 12:09:32 +01002267 /* Hmm, should we write TIMER_IRQ_ACK here?
2268 YES indeed, otherwise a rogue timer operation - which prompts
2269 ALSA(?) to call repeated stop() in vain, but NOT start() -
2270 will never end (value 0x03 is kept shown in control byte).
2271 Simply manually poking 0x04 _once_ immediately successfully stops
2272 the hardware/ALSA interrupt activity. */
2273 snd_azf3328_ctrl_outb(chip, IDX_IO_TIMER_VALUE + 3, 0x04);
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002274 spin_unlock_irqrestore(&chip->reg_lock, flags);
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002275 return 0;
2276}
2277
2278
2279static int
Takashi Iwai95de7762005-11-17 15:02:42 +01002280snd_azf3328_timer_precise_resolution(struct snd_timer *timer,
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002281 unsigned long *num, unsigned long *den)
2282{
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002283 *num = 1;
2284 *den = 1024000 / seqtimer_scaling;
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002285 return 0;
2286}
2287
Takashi Iwai95de7762005-11-17 15:02:42 +01002288static struct snd_timer_hardware snd_azf3328_timer_hw = {
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002289 .flags = SNDRV_TIMER_HW_AUTO,
2290 .resolution = 977, /* 1000000/1024000 = 0.9765625us */
2291 .ticks = 1024000, /* max tick count, defined by the value register; actually it's not 1024000, but 1048576, but we don't care */
2292 .start = snd_azf3328_timer_start,
2293 .stop = snd_azf3328_timer_stop,
2294 .precise_resolution = snd_azf3328_timer_precise_resolution,
2295};
2296
Bill Pembertone23e7a12012-12-06 12:35:10 -05002297static int
Takashi Iwai95de7762005-11-17 15:02:42 +01002298snd_azf3328_timer(struct snd_azf3328 *chip, int device)
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002299{
Takashi Iwai95de7762005-11-17 15:02:42 +01002300 struct snd_timer *timer = NULL;
2301 struct snd_timer_id tid;
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002302 int err;
2303
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002304 tid.dev_class = SNDRV_TIMER_CLASS_CARD;
2305 tid.dev_sclass = SNDRV_TIMER_SCLASS_NONE;
2306 tid.card = chip->card->number;
2307 tid.device = device;
2308 tid.subdevice = 0;
2309
2310 snd_azf3328_timer_hw.resolution *= seqtimer_scaling;
2311 snd_azf3328_timer_hw.ticks /= seqtimer_scaling;
Andreas Mohr02330fb2008-05-16 12:18:29 +02002312
2313 err = snd_timer_new(chip->card, "AZF3328", &tid, &timer);
2314 if (err < 0)
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002315 goto out;
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002316
2317 strcpy(timer->name, "AZF3328 timer");
2318 timer->private_data = chip;
2319 timer->hw = snd_azf3328_timer_hw;
2320
2321 chip->timer = timer;
2322
Andreas Mohr02330fb2008-05-16 12:18:29 +02002323 snd_azf3328_timer_stop(timer);
2324
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002325 err = 0;
2326
2327out:
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002328 return err;
2329}
2330
2331/******************************************************************/
2332
Andreas Mohr02330fb2008-05-16 12:18:29 +02002333static int
2334snd_azf3328_free(struct snd_azf3328 *chip)
2335{
2336 if (chip->irq < 0)
2337 goto __end_hw;
2338
Andreas Mohrb5dc20c2011-02-19 00:49:32 +01002339 snd_azf3328_mixer_reset(chip);
Andreas Mohr02330fb2008-05-16 12:18:29 +02002340
2341 snd_azf3328_timer_stop(chip->timer);
2342 snd_azf3328_gameport_free(chip);
2343
2344 if (chip->irq >= 0)
2345 synchronize_irq(chip->irq);
2346__end_hw:
2347 if (chip->irq >= 0)
2348 free_irq(chip->irq, chip);
2349 pci_release_regions(chip->pci);
2350 pci_disable_device(chip->pci);
2351
2352 kfree(chip);
2353 return 0;
2354}
2355
2356static int
2357snd_azf3328_dev_free(struct snd_device *device)
2358{
2359 struct snd_azf3328 *chip = device->device_data;
2360 return snd_azf3328_free(chip);
2361}
2362
Linus Torvalds1da177e2005-04-16 15:20:36 -07002363#if 0
2364/* check whether a bit can be modified */
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002365static void
Andreas Mohr02330fb2008-05-16 12:18:29 +02002366snd_azf3328_test_bit(unsigned unsigned reg, int bit)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002367{
2368 unsigned char val, valoff, valon;
2369
2370 val = inb(reg);
2371
2372 outb(val & ~(1 << bit), reg);
2373 valoff = inb(reg);
2374
2375 outb(val|(1 << bit), reg);
2376 valon = inb(reg);
Andreas Mohr02330fb2008-05-16 12:18:29 +02002377
Linus Torvalds1da177e2005-04-16 15:20:36 -07002378 outb(val, reg);
2379
Andreas Mohr78df6172009-07-12 22:17:54 +02002380 printk(KERN_DEBUG "reg %04x bit %d: %02x %02x %02x\n",
Andreas Mohr02330fb2008-05-16 12:18:29 +02002381 reg, bit, val, valoff, valon
2382 );
Linus Torvalds1da177e2005-04-16 15:20:36 -07002383}
2384#endif
2385
Andreas Mohr02330fb2008-05-16 12:18:29 +02002386static inline void
Takashi Iwai95de7762005-11-17 15:02:42 +01002387snd_azf3328_debug_show_ports(const struct snd_azf3328 *chip)
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002388{
Andreas Mohr02330fb2008-05-16 12:18:29 +02002389#if DEBUG_MISC
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002390 u16 tmp;
2391
Andreas Mohr02330fb2008-05-16 12:18:29 +02002392 snd_azf3328_dbgmisc(
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002393 "ctrl_io 0x%lx, game_io 0x%lx, mpu_io 0x%lx, "
Andreas Mohr02330fb2008-05-16 12:18:29 +02002394 "opl3_io 0x%lx, mixer_io 0x%lx, irq %d\n",
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002395 chip->ctrl_io, chip->game_io, chip->mpu_io,
Andreas Mohr02330fb2008-05-16 12:18:29 +02002396 chip->opl3_io, chip->mixer_io, chip->irq
2397 );
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002398
Andreas Mohr02330fb2008-05-16 12:18:29 +02002399 snd_azf3328_dbgmisc("game %02x %02x %02x %02x %02x %02x\n",
2400 snd_azf3328_game_inb(chip, 0),
2401 snd_azf3328_game_inb(chip, 1),
2402 snd_azf3328_game_inb(chip, 2),
2403 snd_azf3328_game_inb(chip, 3),
2404 snd_azf3328_game_inb(chip, 4),
2405 snd_azf3328_game_inb(chip, 5)
2406 );
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002407
Andreas Mohr02330fb2008-05-16 12:18:29 +02002408 for (tmp = 0; tmp < 0x07; tmp += 1)
2409 snd_azf3328_dbgmisc("mpu_io 0x%04x\n", inb(chip->mpu_io + tmp));
2410
2411 for (tmp = 0; tmp <= 0x07; tmp += 1)
2412 snd_azf3328_dbgmisc("0x%02x: game200 0x%04x, game208 0x%04x\n",
2413 tmp, inb(0x200 + tmp), inb(0x208 + tmp));
2414
2415 for (tmp = 0; tmp <= 0x01; tmp += 1)
2416 snd_azf3328_dbgmisc(
2417 "0x%02x: mpu300 0x%04x, mpu310 0x%04x, mpu320 0x%04x, "
2418 "mpu330 0x%04x opl388 0x%04x opl38c 0x%04x\n",
2419 tmp,
2420 inb(0x300 + tmp),
2421 inb(0x310 + tmp),
2422 inb(0x320 + tmp),
2423 inb(0x330 + tmp),
2424 inb(0x388 + tmp),
2425 inb(0x38c + tmp)
2426 );
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002427
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002428 for (tmp = 0; tmp < AZF_IO_SIZE_CTRL; tmp += 2)
2429 snd_azf3328_dbgmisc("ctrl 0x%02x: 0x%04x\n",
2430 tmp, snd_azf3328_ctrl_inw(chip, tmp)
Andreas Mohr02330fb2008-05-16 12:18:29 +02002431 );
Andreas Mohre24a1212007-03-26 12:49:45 +02002432
2433 for (tmp = 0; tmp < AZF_IO_SIZE_MIXER; tmp += 2)
Andreas Mohr02330fb2008-05-16 12:18:29 +02002434 snd_azf3328_dbgmisc("mixer 0x%02x: 0x%04x\n",
2435 tmp, snd_azf3328_mixer_inw(chip, tmp)
2436 );
2437#endif /* DEBUG_MISC */
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002438}
2439
Bill Pembertone23e7a12012-12-06 12:35:10 -05002440static int
Takashi Iwai95de7762005-11-17 15:02:42 +01002441snd_azf3328_create(struct snd_card *card,
Andreas Mohr02330fb2008-05-16 12:18:29 +02002442 struct pci_dev *pci,
2443 unsigned long device_type,
2444 struct snd_azf3328 **rchip)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002445{
Takashi Iwai95de7762005-11-17 15:02:42 +01002446 struct snd_azf3328 *chip;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002447 int err;
Takashi Iwai95de7762005-11-17 15:02:42 +01002448 static struct snd_device_ops ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002449 .dev_free = snd_azf3328_dev_free,
2450 };
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002451 u8 dma_init;
2452 enum snd_azf3328_codec_type codec_type;
Andreas Mohrda237f32010-12-27 21:17:26 +01002453 struct snd_azf3328_codec_data *codec_setup;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002454
2455 *rchip = NULL;
2456
Andreas Mohr02330fb2008-05-16 12:18:29 +02002457 err = pci_enable_device(pci);
2458 if (err < 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002459 return err;
2460
Takashi Iwaie560d8d2005-09-09 14:21:46 +02002461 chip = kzalloc(sizeof(*chip), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002462 if (chip == NULL) {
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002463 err = -ENOMEM;
2464 goto out_err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002465 }
2466 spin_lock_init(&chip->reg_lock);
2467 chip->card = card;
2468 chip->pci = pci;
2469 chip->irq = -1;
2470
2471 /* check if we can restrict PCI DMA transfers to 24 bits */
Yang Hongyang2f4f27d2009-04-06 19:01:18 -07002472 if (pci_set_dma_mask(pci, DMA_BIT_MASK(24)) < 0 ||
2473 pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(24)) < 0) {
Andreas Mohr02330fb2008-05-16 12:18:29 +02002474 snd_printk(KERN_ERR "architecture does not support "
2475 "24bit PCI busmaster DMA\n"
2476 );
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002477 err = -ENXIO;
2478 goto out_err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002479 }
2480
Andreas Mohr02330fb2008-05-16 12:18:29 +02002481 err = pci_request_regions(pci, "Aztech AZF3328");
2482 if (err < 0)
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002483 goto out_err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002484
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002485 chip->ctrl_io = pci_resource_start(pci, 0);
Andreas Mohr02330fb2008-05-16 12:18:29 +02002486 chip->game_io = pci_resource_start(pci, 1);
2487 chip->mpu_io = pci_resource_start(pci, 2);
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002488 chip->opl3_io = pci_resource_start(pci, 3);
Andreas Mohr02330fb2008-05-16 12:18:29 +02002489 chip->mixer_io = pci_resource_start(pci, 4);
2490
Andreas Mohr9fd8d362010-12-27 21:17:00 +01002491 codec_setup = &chip->codecs[AZF_CODEC_PLAYBACK];
2492 codec_setup->io_base = chip->ctrl_io + AZF_IO_OFFS_CODEC_PLAYBACK;
Andreas Mohrda237f32010-12-27 21:17:26 +01002493 codec_setup->lock = &chip->reg_lock;
2494 codec_setup->type = AZF_CODEC_PLAYBACK;
Andreas Mohr9fd8d362010-12-27 21:17:00 +01002495 codec_setup->name = "PLAYBACK";
2496
2497 codec_setup = &chip->codecs[AZF_CODEC_CAPTURE];
2498 codec_setup->io_base = chip->ctrl_io + AZF_IO_OFFS_CODEC_CAPTURE;
Andreas Mohrda237f32010-12-27 21:17:26 +01002499 codec_setup->lock = &chip->reg_lock;
2500 codec_setup->type = AZF_CODEC_CAPTURE;
Andreas Mohr9fd8d362010-12-27 21:17:00 +01002501 codec_setup->name = "CAPTURE";
2502
2503 codec_setup = &chip->codecs[AZF_CODEC_I2S_OUT];
2504 codec_setup->io_base = chip->ctrl_io + AZF_IO_OFFS_CODEC_I2S_OUT;
Andreas Mohrda237f32010-12-27 21:17:26 +01002505 codec_setup->lock = &chip->reg_lock;
2506 codec_setup->type = AZF_CODEC_I2S_OUT;
Andreas Mohr9fd8d362010-12-27 21:17:00 +01002507 codec_setup->name = "I2S_OUT";
Linus Torvalds1da177e2005-04-16 15:20:36 -07002508
Takashi Iwai437a5a42006-11-21 12:14:23 +01002509 if (request_irq(pci->irq, snd_azf3328_interrupt,
Takashi Iwai934c2b62011-06-10 16:36:37 +02002510 IRQF_SHARED, KBUILD_MODNAME, chip)) {
Takashi Iwai99b359b2005-10-20 18:26:44 +02002511 snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002512 err = -EBUSY;
2513 goto out_err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002514 }
2515 chip->irq = pci->irq;
2516 pci_set_master(pci);
2517 synchronize_irq(chip->irq);
2518
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002519 snd_azf3328_debug_show_ports(chip);
Andreas Mohr02330fb2008-05-16 12:18:29 +02002520
2521 err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
2522 if (err < 0)
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002523 goto out_err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002524
2525 /* create mixer interface & switches */
Andreas Mohr02330fb2008-05-16 12:18:29 +02002526 err = snd_azf3328_mixer_new(chip);
2527 if (err < 0)
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002528 goto out_err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002529
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002530 /* standard codec init stuff */
2531 /* default DMA init value */
2532 dma_init = DMA_RUN_SOMETHING2|DMA_EPILOGUE_SOMETHING|DMA_SOMETHING_ELSE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002533
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002534 for (codec_type = AZF_CODEC_PLAYBACK;
2535 codec_type <= AZF_CODEC_I2S_OUT; ++codec_type) {
2536 struct snd_azf3328_codec_data *codec =
2537 &chip->codecs[codec_type];
Linus Torvalds1da177e2005-04-16 15:20:36 -07002538
Andreas Mohradf59312010-12-27 21:16:43 +01002539 /* shutdown codecs to reduce power / noise */
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002540 /* have ...ctrl_codec_activity() act properly */
2541 codec->running = 1;
2542 snd_azf3328_ctrl_codec_activity(chip, codec_type, 0);
2543
Andreas Mohrda237f32010-12-27 21:17:26 +01002544 spin_lock_irq(codec->lock);
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002545 snd_azf3328_codec_outb(codec, IDX_IO_CODEC_DMA_FLAGS,
2546 dma_init);
Andreas Mohrda237f32010-12-27 21:17:26 +01002547 spin_unlock_irq(codec->lock);
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002548 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002549
Linus Torvalds1da177e2005-04-16 15:20:36 -07002550 *rchip = chip;
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002551
2552 err = 0;
2553 goto out;
2554
2555out_err:
2556 if (chip)
2557 snd_azf3328_free(chip);
2558 pci_disable_device(pci);
2559
2560out:
2561 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002562}
2563
Bill Pembertone23e7a12012-12-06 12:35:10 -05002564static int
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002565snd_azf3328_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002566{
2567 static int dev;
Takashi Iwai95de7762005-11-17 15:02:42 +01002568 struct snd_card *card;
2569 struct snd_azf3328 *chip;
2570 struct snd_opl3 *opl3;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002571 int err;
2572
Julia Lawalla5a39732011-08-10 11:49:04 +02002573 if (dev >= SNDRV_CARDS) {
2574 err = -ENODEV;
2575 goto out;
2576 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002577 if (!enable[dev]) {
2578 dev++;
Julia Lawalla5a39732011-08-10 11:49:04 +02002579 err = -ENOENT;
2580 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002581 }
2582
Takashi Iwai60c57722014-01-29 14:20:19 +01002583 err = snd_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE,
2584 0, &card);
Takashi Iwaie58de7b2008-12-28 16:44:30 +01002585 if (err < 0)
Julia Lawalla5a39732011-08-10 11:49:04 +02002586 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002587
2588 strcpy(card->driver, "AZF3328");
2589 strcpy(card->shortname, "Aztech AZF3328 (PCI168)");
2590
Andreas Mohr02330fb2008-05-16 12:18:29 +02002591 err = snd_azf3328_create(card, pci, pci_id->driver_data, &chip);
2592 if (err < 0)
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002593 goto out_err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002594
Andreas Mohrca54bde2006-05-17 11:02:24 +02002595 card->private_data = chip;
2596
Andreas Mohr78df6172009-07-12 22:17:54 +02002597 /* chose to use MPU401_HW_AZT2320 ID instead of MPU401_HW_MPU401,
2598 since our hardware ought to be similar, thus use same ID. */
Andreas Mohr02330fb2008-05-16 12:18:29 +02002599 err = snd_mpu401_uart_new(
Andreas Mohr78df6172009-07-12 22:17:54 +02002600 card, 0,
Clemens Ladischdba8b462011-09-13 11:24:41 +02002601 MPU401_HW_AZT2320, chip->mpu_io,
2602 MPU401_INFO_INTEGRATED | MPU401_INFO_IRQ_HOOK,
2603 -1, &chip->rmidi
Andreas Mohr02330fb2008-05-16 12:18:29 +02002604 );
2605 if (err < 0) {
2606 snd_printk(KERN_ERR "azf3328: no MPU-401 device at 0x%lx?\n",
2607 chip->mpu_io
2608 );
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002609 goto out_err;
2610 }
2611
Andreas Mohr02330fb2008-05-16 12:18:29 +02002612 err = snd_azf3328_timer(chip, 0);
2613 if (err < 0)
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002614 goto out_err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002615
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002616 err = snd_azf3328_pcm(chip);
Andreas Mohr02330fb2008-05-16 12:18:29 +02002617 if (err < 0)
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002618 goto out_err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002619
Andreas Mohr02330fb2008-05-16 12:18:29 +02002620 if (snd_opl3_create(card, chip->opl3_io, chip->opl3_io+2,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002621 OPL3_HW_AUTO, 1, &opl3) < 0) {
Takashi Iwai99b359b2005-10-20 18:26:44 +02002622 snd_printk(KERN_ERR "azf3328: no OPL3 device at 0x%lx-0x%lx?\n",
Andreas Mohr02330fb2008-05-16 12:18:29 +02002623 chip->opl3_io, chip->opl3_io+2
2624 );
Linus Torvalds1da177e2005-04-16 15:20:36 -07002625 } else {
Andreas Mohr02330fb2008-05-16 12:18:29 +02002626 /* need to use IDs 1, 2 since ID 0 is snd_azf3328_timer above */
2627 err = snd_opl3_timer_new(opl3, 1, 2);
2628 if (err < 0)
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002629 goto out_err;
Andreas Mohr02330fb2008-05-16 12:18:29 +02002630 err = snd_opl3_hwdep_new(opl3, 0, 1, NULL);
2631 if (err < 0)
2632 goto out_err;
Alban Bedel87c9e7d2012-02-25 16:15:57 +01002633 opl3->private_data = chip;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002634 }
2635
Linus Torvalds1da177e2005-04-16 15:20:36 -07002636 sprintf(card->longname, "%s at 0x%lx, irq %i",
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002637 card->shortname, chip->ctrl_io, chip->irq);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002638
Andreas Mohr02330fb2008-05-16 12:18:29 +02002639 err = snd_card_register(card);
2640 if (err < 0)
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002641 goto out_err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002642
2643#ifdef MODULE
Andreas Mohr78df6172009-07-12 22:17:54 +02002644 printk(KERN_INFO
Andreas Mohre24a1212007-03-26 12:49:45 +02002645"azt3328: Sound driver for Aztech AZF3328-based soundcards such as PCI168.\n"
2646"azt3328: Hardware was completely undocumented, unfortunately.\n"
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002647"azt3328: Feel free to contact andi AT lisas.de for bug reports etc.!\n"
2648"azt3328: User-scalable sequencer timer set to %dHz (1024000Hz / %d).\n",
2649 1024000 / seqtimer_scaling, seqtimer_scaling);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002650#endif
2651
Andreas Mohr02330fb2008-05-16 12:18:29 +02002652 snd_azf3328_gameport(chip, dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002653
2654 pci_set_drvdata(pci, card);
2655 dev++;
2656
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002657 err = 0;
2658 goto out;
Andreas Mohr02330fb2008-05-16 12:18:29 +02002659
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002660out_err:
Andreas Mohr02330fb2008-05-16 12:18:29 +02002661 snd_printk(KERN_ERR "azf3328: something failed, exiting\n");
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002662 snd_card_free(card);
Andreas Mohr02330fb2008-05-16 12:18:29 +02002663
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002664out:
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002665 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002666}
2667
Bill Pembertone23e7a12012-12-06 12:35:10 -05002668static void
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002669snd_azf3328_remove(struct pci_dev *pci)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002670{
Linus Torvalds1da177e2005-04-16 15:20:36 -07002671 snd_card_free(pci_get_drvdata(pci));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002672}
2673
Takashi Iwaic7561cd2012-08-14 18:12:04 +02002674#ifdef CONFIG_PM_SLEEP
Andreas Mohr78df6172009-07-12 22:17:54 +02002675static inline void
2676snd_azf3328_suspend_regs(unsigned long io_addr, unsigned count, u32 *saved_regs)
2677{
2678 unsigned reg;
2679
2680 for (reg = 0; reg < count; ++reg) {
2681 *saved_regs = inl(io_addr);
2682 snd_azf3328_dbgpm("suspend: io 0x%04lx: 0x%08x\n",
2683 io_addr, *saved_regs);
2684 ++saved_regs;
2685 io_addr += sizeof(*saved_regs);
2686 }
2687}
2688
Andreas Mohrb5dc20c2011-02-19 00:49:32 +01002689static inline void
2690snd_azf3328_resume_regs(const u32 *saved_regs,
2691 unsigned long io_addr,
2692 unsigned count
2693)
2694{
2695 unsigned reg;
2696
2697 for (reg = 0; reg < count; ++reg) {
2698 outl(*saved_regs, io_addr);
2699 snd_azf3328_dbgpm("resume: io 0x%04lx: 0x%08x --> 0x%08x\n",
2700 io_addr, *saved_regs, inl(io_addr));
2701 ++saved_regs;
2702 io_addr += sizeof(*saved_regs);
2703 }
2704}
2705
2706static inline void
2707snd_azf3328_suspend_ac97(struct snd_azf3328 *chip)
2708{
2709#ifdef AZF_USE_AC97_LAYER
2710 snd_ac97_suspend(chip->ac97);
2711#else
2712 snd_azf3328_suspend_regs(chip->mixer_io,
2713 ARRAY_SIZE(chip->saved_regs_mixer), chip->saved_regs_mixer);
2714
2715 /* make sure to disable master volume etc. to prevent looping sound */
2716 snd_azf3328_mixer_mute_control_master(chip, 1);
2717 snd_azf3328_mixer_mute_control_pcm(chip, 1);
2718#endif /* AZF_USE_AC97_LAYER */
2719}
2720
2721static inline void
2722snd_azf3328_resume_ac97(const struct snd_azf3328 *chip)
2723{
2724#ifdef AZF_USE_AC97_LAYER
2725 snd_ac97_resume(chip->ac97);
2726#else
2727 snd_azf3328_resume_regs(chip->saved_regs_mixer, chip->mixer_io,
2728 ARRAY_SIZE(chip->saved_regs_mixer));
2729
2730 /* unfortunately with 32bit transfers, IDX_MIXER_PLAY_MASTER (0x02)
2731 and IDX_MIXER_RESET (offset 0x00) get touched at the same time,
2732 resulting in a mixer reset condition persisting until _after_
2733 master vol was restored. Thus master vol needs an extra restore. */
2734 outw(((u16 *)chip->saved_regs_mixer)[1], chip->mixer_io + 2);
2735#endif /* AZF_USE_AC97_LAYER */
2736}
2737
Andreas Mohrca54bde2006-05-17 11:02:24 +02002738static int
Takashi Iwai68cb2b52012-07-02 15:20:37 +02002739snd_azf3328_suspend(struct device *dev)
Andreas Mohrca54bde2006-05-17 11:02:24 +02002740{
Takashi Iwai68cb2b52012-07-02 15:20:37 +02002741 struct pci_dev *pci = to_pci_dev(dev);
2742 struct snd_card *card = dev_get_drvdata(dev);
Andreas Mohrca54bde2006-05-17 11:02:24 +02002743 struct snd_azf3328 *chip = card->private_data;
Andreas Mohr78df6172009-07-12 22:17:54 +02002744 u16 *saved_regs_ctrl_u16;
Andreas Mohrca54bde2006-05-17 11:02:24 +02002745
2746 snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
Andreas Mohr02330fb2008-05-16 12:18:29 +02002747
Andreas Mohradf59312010-12-27 21:16:43 +01002748 /* same pcm object for playback/capture */
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002749 snd_pcm_suspend_all(chip->pcm[AZF_CODEC_PLAYBACK]);
2750 snd_pcm_suspend_all(chip->pcm[AZF_CODEC_I2S_OUT]);
Andreas Mohrca54bde2006-05-17 11:02:24 +02002751
Andreas Mohrb5dc20c2011-02-19 00:49:32 +01002752 snd_azf3328_suspend_ac97(chip);
Andreas Mohr02330fb2008-05-16 12:18:29 +02002753
Andreas Mohr78df6172009-07-12 22:17:54 +02002754 snd_azf3328_suspend_regs(chip->ctrl_io,
2755 ARRAY_SIZE(chip->saved_regs_ctrl), chip->saved_regs_ctrl);
Andreas Mohr627d3e72008-06-23 11:50:47 +02002756
2757 /* manually store the one currently relevant write-only reg, too */
Andreas Mohr78df6172009-07-12 22:17:54 +02002758 saved_regs_ctrl_u16 = (u16 *)chip->saved_regs_ctrl;
2759 saved_regs_ctrl_u16[IDX_IO_6AH / 2] = chip->shadow_reg_ctrl_6AH;
Andreas Mohr627d3e72008-06-23 11:50:47 +02002760
Andreas Mohr78df6172009-07-12 22:17:54 +02002761 snd_azf3328_suspend_regs(chip->game_io,
2762 ARRAY_SIZE(chip->saved_regs_game), chip->saved_regs_game);
2763 snd_azf3328_suspend_regs(chip->mpu_io,
2764 ARRAY_SIZE(chip->saved_regs_mpu), chip->saved_regs_mpu);
2765 snd_azf3328_suspend_regs(chip->opl3_io,
2766 ARRAY_SIZE(chip->saved_regs_opl3), chip->saved_regs_opl3);
Andreas Mohrca54bde2006-05-17 11:02:24 +02002767
Andreas Mohrca54bde2006-05-17 11:02:24 +02002768 pci_disable_device(pci);
2769 pci_save_state(pci);
Takashi Iwai68cb2b52012-07-02 15:20:37 +02002770 pci_set_power_state(pci, PCI_D3hot);
Andreas Mohrca54bde2006-05-17 11:02:24 +02002771 return 0;
2772}
2773
2774static int
Takashi Iwai68cb2b52012-07-02 15:20:37 +02002775snd_azf3328_resume(struct device *dev)
Andreas Mohrca54bde2006-05-17 11:02:24 +02002776{
Takashi Iwai68cb2b52012-07-02 15:20:37 +02002777 struct pci_dev *pci = to_pci_dev(dev);
2778 struct snd_card *card = dev_get_drvdata(dev);
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002779 const struct snd_azf3328 *chip = card->private_data;
Andreas Mohrca54bde2006-05-17 11:02:24 +02002780
Andreas Mohrca54bde2006-05-17 11:02:24 +02002781 pci_set_power_state(pci, PCI_D0);
Takashi Iwai30b35392006-10-11 18:52:53 +02002782 pci_restore_state(pci);
2783 if (pci_enable_device(pci) < 0) {
2784 printk(KERN_ERR "azt3328: pci_enable_device failed, "
2785 "disabling device\n");
2786 snd_card_disconnect(card);
2787 return -EIO;
2788 }
Andreas Mohrca54bde2006-05-17 11:02:24 +02002789 pci_set_master(pci);
2790
Andreas Mohr78df6172009-07-12 22:17:54 +02002791 snd_azf3328_resume_regs(chip->saved_regs_game, chip->game_io,
2792 ARRAY_SIZE(chip->saved_regs_game));
2793 snd_azf3328_resume_regs(chip->saved_regs_mpu, chip->mpu_io,
2794 ARRAY_SIZE(chip->saved_regs_mpu));
2795 snd_azf3328_resume_regs(chip->saved_regs_opl3, chip->opl3_io,
2796 ARRAY_SIZE(chip->saved_regs_opl3));
2797
Andreas Mohrb5dc20c2011-02-19 00:49:32 +01002798 snd_azf3328_resume_ac97(chip);
Andreas Mohr78df6172009-07-12 22:17:54 +02002799
2800 snd_azf3328_resume_regs(chip->saved_regs_ctrl, chip->ctrl_io,
2801 ARRAY_SIZE(chip->saved_regs_ctrl));
Andreas Mohrca54bde2006-05-17 11:02:24 +02002802
2803 snd_power_change_state(card, SNDRV_CTL_POWER_D0);
2804 return 0;
2805}
Andreas Mohrca54bde2006-05-17 11:02:24 +02002806
Takashi Iwai68cb2b52012-07-02 15:20:37 +02002807static SIMPLE_DEV_PM_OPS(snd_azf3328_pm, snd_azf3328_suspend, snd_azf3328_resume);
2808#define SND_AZF3328_PM_OPS &snd_azf3328_pm
2809#else
2810#define SND_AZF3328_PM_OPS NULL
Takashi Iwaic7561cd2012-08-14 18:12:04 +02002811#endif /* CONFIG_PM_SLEEP */
Andreas Mohrca54bde2006-05-17 11:02:24 +02002812
Takashi Iwaie9f66d92012-04-24 12:25:00 +02002813static struct pci_driver azf3328_driver = {
Takashi Iwai3733e422011-06-10 16:20:20 +02002814 .name = KBUILD_MODNAME,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002815 .id_table = snd_azf3328_ids,
2816 .probe = snd_azf3328_probe,
Bill Pembertone23e7a12012-12-06 12:35:10 -05002817 .remove = snd_azf3328_remove,
Takashi Iwai68cb2b52012-07-02 15:20:37 +02002818 .driver = {
2819 .pm = SND_AZF3328_PM_OPS,
2820 },
Linus Torvalds1da177e2005-04-16 15:20:36 -07002821};
2822
Takashi Iwaie9f66d92012-04-24 12:25:00 +02002823module_pci_driver(azf3328_driver);