blob: 39dfdaa6a56fb0c63b7ef05eb58dcc7f74bbbb2a [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * azt3328.c - driver for Aztech AZF3328 based soundcards (e.g. PCI168).
Andreas Mohrdfbf9512009-07-05 13:55:46 +02003 * Copyright (C) 2002, 2005 - 2009 by Andreas Mohr <andi AT lisas.de>
Linus Torvalds1da177e2005-04-16 15:20:36 -07004 *
5 * Framework borrowed from Bart Hartgers's als4000.c.
6 * Driver developed on PCI168 AP(W) version (PCI rev. 10, subsystem ID 1801),
7 * found in a Fujitsu-Siemens PC ("Cordant", aluminum case).
8 * Other versions are:
9 * PCI168 A(W), sub ID 1800
10 * PCI168 A/AP, sub ID 8000
11 * Please give me feedback in case you try my driver with one of these!!
12 *
Andreas Mohrdfbf9512009-07-05 13:55:46 +020013 * Keywords: Windows XP Vista 168nt4-125.zip 168win95-125.zip PCI 168 download
14 * (XP/Vista do not support this card at all but every Linux distribution
15 * has very good support out of the box;
16 * just to make sure that the right people hit this and get to know that,
17 * despite the high level of Internet ignorance - as usual :-P -
18 * about Linux support for this card)
19 *
Linus Torvalds1da177e2005-04-16 15:20:36 -070020 * GPL LICENSE
21 * This program is free software; you can redistribute it and/or modify
22 * it under the terms of the GNU General Public License as published by
23 * the Free Software Foundation; either version 2 of the License, or
24 * (at your option) any later version.
25 *
26 * This program is distributed in the hope that it will be useful,
27 * but WITHOUT ANY WARRANTY; without even the implied warranty of
28 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
29 * GNU General Public License for more details.
30
31 * You should have received a copy of the GNU General Public License
32 * along with this program; if not, write to the Free Software
33 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
34 *
35 * NOTES
36 * Since Aztech does not provide any chipset documentation,
37 * even on repeated request to various addresses,
38 * and the answer that was finally given was negative
39 * (and I was stupid enough to manage to get hold of a PCI168 soundcard
40 * in the first place >:-P}),
41 * I was forced to base this driver on reverse engineering
42 * (3 weeks' worth of evenings filled with driver work).
Andreas Mohre2f87262006-05-17 11:04:19 +020043 * (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 -070044 *
Andreas Mohr02330fb2008-05-16 12:18:29 +020045 * It is quite likely that the AZF3328 chip is the PCI cousin of the
46 * AZF3318 ("azt1020 pnp", "MM Pro 16") ISA chip, given very similar specs.
Linus Torvalds1da177e2005-04-16 15:20:36 -070047 *
Andreas Mohr02330fb2008-05-16 12:18:29 +020048 * The AZF3328 chip (note: AZF3328, *not* AZT3328, that's just the driver name
49 * for compatibility reasons) from Azfin (joint-venture of Aztech and Fincitec,
50 * Fincitec acquired by National Semiconductor in 2002, together with the
51 * Fincitec-related company ARSmikro) has the following features:
52 *
53 * - compatibility & compliance:
54 * - Microsoft PC 97 ("PC 97 Hardware Design Guide",
55 * http://www.microsoft.com/whdc/archive/pcguides.mspx)
56 * - Microsoft PC 98 Baseline Audio
57 * - MPU401 UART
58 * - Sound Blaster Emulation (DOS Box)
Linus Torvalds1da177e2005-04-16 15:20:36 -070059 * - builtin AC97 conformant codec (SNR over 80dB)
Andreas Mohr13769e32006-05-17 11:03:16 +020060 * Note that "conformant" != "compliant"!! this chip's mixer register layout
61 * *differs* from the standard AC97 layout:
62 * they chose to not implement the headphone register (which is not a
63 * problem since it's merely optional), yet when doing this, they committed
64 * the grave sin of letting other registers follow immediately instead of
65 * keeping a headphone dummy register, thereby shifting the mixer register
66 * addresses illegally. So far unfortunately it looks like the very flexible
67 * ALSA AC97 support is still not enough to easily compensate for such a
68 * grave layout violation despite all tweaks and quirks mechanisms it offers.
Andreas Mohr02330fb2008-05-16 12:18:29 +020069 * - builtin genuine OPL3 - verified to work fine, 20080506
Linus Torvalds1da177e2005-04-16 15:20:36 -070070 * - full duplex 16bit playback/record at independent sampling rate
Andreas Mohr02330fb2008-05-16 12:18:29 +020071 * - MPU401 (+ legacy address support, claimed by one official spec sheet)
72 * FIXME: how to enable legacy addr??
Linus Torvalds1da177e2005-04-16 15:20:36 -070073 * - game port (legacy address support)
Andreas Mohre24a1212007-03-26 12:49:45 +020074 * - builtin DirectInput support, helps reduce CPU overhead (interrupt-driven
Andreas Mohr02330fb2008-05-16 12:18:29 +020075 * features supported). - See common term "Digital Enhanced Game Port"...
76 * (probably DirectInput 3.0 spec - confirm)
77 * - builtin 3D enhancement (said to be YAMAHA Ymersion)
Linus Torvalds1da177e2005-04-16 15:20:36 -070078 * - built-in General DirectX timer having a 20 bits counter
Andreas Mohrd91c64c2005-10-25 11:17:45 +020079 * with 1us resolution (see below!)
Andreas Mohr02330fb2008-05-16 12:18:29 +020080 * - I2S serial output port for external DAC
Andreas Mohrdfbf9512009-07-05 13:55:46 +020081 * [FIXME: 3.3V or 5V level? maximum rate is 66.2kHz right?]
Linus Torvalds1da177e2005-04-16 15:20:36 -070082 * - supports 33MHz PCI spec 2.1, PCI power management 1.0, compliant with ACPI
83 * - supports hardware volume control
84 * - single chip low cost solution (128 pin QFP)
Andreas Mohrdfbf9512009-07-05 13:55:46 +020085 * - supports programmable Sub-vendor and Sub-system ID [24C02 SEEPROM chip]
Linus Torvalds1da177e2005-04-16 15:20:36 -070086 * required for Microsoft's logo compliance (FIXME: where?)
Andreas Mohr02330fb2008-05-16 12:18:29 +020087 * At least the Trident 4D Wave DX has one bit somewhere
88 * to enable writes to PCI subsystem VID registers, that should be it.
89 * This might easily be in extended PCI reg space, since PCI168 also has
90 * some custom data starting at 0x80. What kind of config settings
91 * are located in our extended PCI space anyway??
Linus Torvalds1da177e2005-04-16 15:20:36 -070092 * - PCI168 AP(W) card: power amplifier with 4 Watts/channel at 4 Ohms
Andreas Mohrdfbf9512009-07-05 13:55:46 +020093 * [TDA1517P chip]
Linus Torvalds1da177e2005-04-16 15:20:36 -070094 *
Andreas Mohrd91c64c2005-10-25 11:17:45 +020095 * Note that this driver now is actually *better* than the Windows driver,
96 * since it additionally supports the card's 1MHz DirectX timer - just try
97 * the following snd-seq module parameters etc.:
98 * - options snd-seq seq_default_timer_class=2 seq_default_timer_sclass=0
99 * seq_default_timer_card=0 seq_client_load=1 seq_default_timer_device=0
100 * seq_default_timer_subdevice=0 seq_default_timer_resolution=1000000
101 * - "timidity -iAv -B2,8 -Os -EFreverb=0"
102 * - "pmidi -p 128:0 jazz.mid"
103 *
Andreas Mohr02330fb2008-05-16 12:18:29 +0200104 * OPL3 hardware playback testing, try something like:
105 * cat /proc/asound/hwdep
106 * and
107 * aconnect -o
108 * Then use
109 * sbiload -Dhw:x,y --opl3 /usr/share/sounds/opl3/std.o3 ......./drums.o3
110 * where x,y is the xx-yy number as given in hwdep.
111 * Then try
112 * pmidi -p a:b jazz.mid
113 * where a:b is the client number plus 0 usually, as given by aconnect above.
114 * Oh, and make sure to unmute the FM mixer control (doh!)
115 * NOTE: power use during OPL3 playback is _VERY_ high (70W --> 90W!)
116 * despite no CPU activity, possibly due to hindering ACPI idling somehow.
117 * Shouldn't be a problem of the AZF3328 chip itself, I'd hope.
118 * Higher PCM / FM mixer levels seem to conflict (causes crackling),
119 * at least sometimes. Maybe even use with hardware sequencer timer above :)
120 * adplay/adplug-utils might soon offer hardware-based OPL3 playback, too.
121 *
Linus Torvalds1da177e2005-04-16 15:20:36 -0700122 * Certain PCI versions of this card are susceptible to DMA traffic underruns
123 * in some systems (resulting in sound crackling/clicking/popping),
124 * probably because they don't have a DMA FIFO buffer or so.
125 * Overview (PCI ID/PCI subID/PCI rev.):
126 * - no DMA crackling on SiS735: 0x50DC/0x1801/16
127 * - unknown performance: 0x50DC/0x1801/10
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200128 * (well, it's not bad on an Athlon 1800 with now very optimized IRQ handler)
129 *
Linus Torvalds1da177e2005-04-16 15:20:36 -0700130 * Crackling happens with VIA chipsets or, in my case, an SiS735, which is
131 * supposed to be very fast and supposed to get rid of crackling much
132 * better than a VIA, yet ironically I still get crackling, like many other
133 * people with the same chipset.
134 * Possible remedies:
Andreas Mohr02330fb2008-05-16 12:18:29 +0200135 * - use speaker (amplifier) output instead of headphone output
136 * (in case crackling is due to overloaded output clipping)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700137 * - plug card into a different PCI slot, preferrably one that isn't shared
138 * too much (this helps a lot, but not completely!)
139 * - get rid of PCI VGA card, use AGP instead
140 * - upgrade or downgrade BIOS
141 * - fiddle with PCI latency settings (setpci -v -s BUSID latency_timer=XX)
142 * Not too helpful.
143 * - Disable ACPI/power management/"Auto Detect RAM/PCI Clk" in BIOS
Andreas Mohr02330fb2008-05-16 12:18:29 +0200144 *
Linus Torvalds1da177e2005-04-16 15:20:36 -0700145 * BUGS
Andreas Mohr02330fb2008-05-16 12:18:29 +0200146 * - full-duplex might *still* be problematic, however a recent test was fine
Andreas Mohre24a1212007-03-26 12:49:45 +0200147 * - (non-bug) "Bass/Treble or 3D settings don't work" - they do get evaluated
148 * if you set PCM output switch to "pre 3D" instead of "post 3D".
149 * If this can't be set, then get a mixer application that Isn't Stupid (tm)
150 * (e.g. kmix, gamix) - unfortunately several are!!
Andreas Mohr02330fb2008-05-16 12:18:29 +0200151 * - locking is not entirely clean, especially the audio stream activity
152 * ints --> may be racy
153 * - an _unconnected_ secondary joystick at the gameport will be reported
154 * to be "active" (floating values, not precisely -1) due to the way we need
155 * to read the Digital Enhanced Game Port. Not sure whether it is fixable.
156 *
Linus Torvalds1da177e2005-04-16 15:20:36 -0700157 * TODO
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200158 * - use PCI_VDEVICE
159 * - verify driver status on x86_64
160 * - test multi-card driver operation
161 * - (ab)use 1MHz DirectX timer as kernel clocksource
Linus Torvalds1da177e2005-04-16 15:20:36 -0700162 * - test MPU401 MIDI playback etc.
Andreas Mohr02330fb2008-05-16 12:18:29 +0200163 * - add more power micro-management (disable various units of the card
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200164 * as long as they're unused, to improve audio quality and save power).
165 * However this requires more I/O ports which I haven't figured out yet
166 * and which thus might not even exist...
Andreas Mohrca54bde2006-05-17 11:02:24 +0200167 * The standard suspend/resume functionality could probably make use of
168 * some improvement, too...
Linus Torvalds1da177e2005-04-16 15:20:36 -0700169 * - figure out what all unknown port bits are responsible for
Andreas Mohr13769e32006-05-17 11:03:16 +0200170 * - figure out some cleverly evil scheme to possibly make ALSA AC97 code
171 * fully accept our quite incompatible ""AC97"" mixer and thus save some
172 * code (but I'm not too optimistic that doing this is possible at all)
Andreas Mohr02330fb2008-05-16 12:18:29 +0200173 * - use MMIO (memory-mapped I/O)? Slightly faster access, e.g. for gameport.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700174 */
175
Linus Torvalds1da177e2005-04-16 15:20:36 -0700176#include <asm/io.h>
177#include <linux/init.h>
178#include <linux/pci.h>
179#include <linux/delay.h>
180#include <linux/slab.h>
181#include <linux/gameport.h>
182#include <linux/moduleparam.h>
Matthias Gehre910638a2006-03-28 01:56:48 -0800183#include <linux/dma-mapping.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -0700184#include <sound/core.h>
185#include <sound/control.h>
186#include <sound/pcm.h>
187#include <sound/rawmidi.h>
188#include <sound/mpu401.h>
189#include <sound/opl3.h>
190#include <sound/initval.h>
191#include "azt3328.h"
192
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200193MODULE_AUTHOR("Andreas Mohr <andi AT lisas.de>");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700194MODULE_DESCRIPTION("Aztech AZF3328 (PCI168)");
195MODULE_LICENSE("GPL");
196MODULE_SUPPORTED_DEVICE("{{Aztech,AZF3328}}");
197
198#if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE))
Andreas Mohr02330fb2008-05-16 12:18:29 +0200199#define SUPPORT_GAMEPORT 1
Linus Torvalds1da177e2005-04-16 15:20:36 -0700200#endif
201
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200202/* === Debug settings ===
203 Further diagnostic functionality than the settings below
204 does not need to be provided, since one can easily write a bash script
205 to dump the card's I/O ports (those listed in lspci -v -v):
206 function dump()
207 {
208 local descr=$1; local addr=$2; local count=$3
209
210 echo "${descr}: ${count} @ ${addr}:"
211 dd if=/dev/port skip=$[${addr}] count=${count} bs=1 2>/dev/null| hexdump -C
212 }
213 and then use something like
214 "dump joy200 0x200 8", "dump mpu388 0x388 4", "dump joy 0xb400 8",
215 "dump codec00 0xa800 32", "dump mixer 0xb800 64", "dump synth 0xbc00 8",
216 possibly within a "while true; do ... sleep 1; done" loop.
217 Tweaking ports could be done using
218 VALSTRING="`printf "%02x" $value`"
219 printf "\x""$VALSTRING"|dd of=/dev/port seek=$[${addr}] bs=1 2>/dev/null
220*/
221
Linus Torvalds1da177e2005-04-16 15:20:36 -0700222#define DEBUG_MISC 0
223#define DEBUG_CALLS 0
224#define DEBUG_MIXER 0
225#define DEBUG_PLAY_REC 0
226#define DEBUG_IO 0
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200227#define DEBUG_TIMER 0
Andreas Mohr02330fb2008-05-16 12:18:29 +0200228#define DEBUG_GAME 0
Linus Torvalds1da177e2005-04-16 15:20:36 -0700229#define MIXER_TESTING 0
230
231#if DEBUG_MISC
232#define snd_azf3328_dbgmisc(format, args...) printk(KERN_ERR format, ##args)
233#else
234#define snd_azf3328_dbgmisc(format, args...)
Andreas Mohr02330fb2008-05-16 12:18:29 +0200235#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700236
237#if DEBUG_CALLS
238#define snd_azf3328_dbgcalls(format, args...) printk(format, ##args)
Harvey Harrison9bf8e7d2008-03-03 15:32:18 -0800239#define snd_azf3328_dbgcallenter() printk(KERN_ERR "--> %s\n", __func__)
240#define snd_azf3328_dbgcallleave() printk(KERN_ERR "<-- %s\n", __func__)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700241#else
242#define snd_azf3328_dbgcalls(format, args...)
243#define snd_azf3328_dbgcallenter()
244#define snd_azf3328_dbgcallleave()
Andreas Mohr02330fb2008-05-16 12:18:29 +0200245#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700246
247#if DEBUG_MIXER
Takashi Iwaiee419652009-02-05 16:11:31 +0100248#define snd_azf3328_dbgmixer(format, args...) printk(KERN_DEBUG format, ##args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700249#else
250#define snd_azf3328_dbgmixer(format, args...)
Andreas Mohr02330fb2008-05-16 12:18:29 +0200251#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700252
253#if DEBUG_PLAY_REC
Takashi Iwaiee419652009-02-05 16:11:31 +0100254#define snd_azf3328_dbgplay(format, args...) printk(KERN_DEBUG format, ##args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700255#else
256#define snd_azf3328_dbgplay(format, args...)
Andreas Mohr02330fb2008-05-16 12:18:29 +0200257#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700258
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200259#if DEBUG_MISC
Takashi Iwaiee419652009-02-05 16:11:31 +0100260#define snd_azf3328_dbgtimer(format, args...) printk(KERN_DEBUG format, ##args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700261#else
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200262#define snd_azf3328_dbgtimer(format, args...)
Andreas Mohr02330fb2008-05-16 12:18:29 +0200263#endif
264
265#if DEBUG_GAME
Takashi Iwaiee419652009-02-05 16:11:31 +0100266#define snd_azf3328_dbggame(format, args...) printk(KERN_DEBUG format, ##args)
Andreas Mohr02330fb2008-05-16 12:18:29 +0200267#else
268#define snd_azf3328_dbggame(format, args...)
269#endif
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200270
Linus Torvalds1da177e2005-04-16 15:20:36 -0700271static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
272module_param_array(index, int, NULL, 0444);
273MODULE_PARM_DESC(index, "Index value for AZF3328 soundcard.");
274
275static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
276module_param_array(id, charp, NULL, 0444);
277MODULE_PARM_DESC(id, "ID string for AZF3328 soundcard.");
278
279static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
280module_param_array(enable, bool, NULL, 0444);
281MODULE_PARM_DESC(enable, "Enable AZF3328 soundcard.");
282
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200283static int seqtimer_scaling = 128;
284module_param(seqtimer_scaling, int, 0444);
285MODULE_PARM_DESC(seqtimer_scaling, "Set 1024000Hz sequencer timer scale factor (lockup danger!). Default 128.");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700286
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200287struct snd_azf3328_codec_data {
288 unsigned long io_base;
Andreas Mohr02330fb2008-05-16 12:18:29 +0200289 struct snd_pcm_substream *substream;
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200290 bool running;
291 const char *name;
Andreas Mohr02330fb2008-05-16 12:18:29 +0200292};
293
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200294enum snd_azf3328_codec_type {
295 AZF_CODEC_PLAYBACK = 0,
296 AZF_CODEC_CAPTURE = 1,
297 AZF_CODEC_I2S_OUT = 2,
Andreas Mohr02330fb2008-05-16 12:18:29 +0200298};
299
Takashi Iwai95de7762005-11-17 15:02:42 +0100300struct snd_azf3328 {
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200301 /* often-used fields towards beginning, then grouped */
Andreas Mohr02330fb2008-05-16 12:18:29 +0200302
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200303 unsigned long ctrl_io; /* usually 0xb000, size 128 */
Andreas Mohr02330fb2008-05-16 12:18:29 +0200304 unsigned long game_io; /* usually 0xb400, size 8 */
305 unsigned long mpu_io; /* usually 0xb800, size 4 */
306 unsigned long opl3_io; /* usually 0xbc00, size 8 */
307 unsigned long mixer_io; /* usually 0xc000, size 64 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700308
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200309 spinlock_t reg_lock;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700310
Takashi Iwai95de7762005-11-17 15:02:42 +0100311 struct snd_timer *timer;
Andreas Mohr02330fb2008-05-16 12:18:29 +0200312
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200313 struct snd_pcm *pcm[3];
314
315 /* playback, recording and I2S out codecs */
316 struct snd_azf3328_codec_data codecs[3];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700317
Takashi Iwai95de7762005-11-17 15:02:42 +0100318 struct snd_card *card;
319 struct snd_rawmidi *rmidi;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700320
Andreas Mohr02330fb2008-05-16 12:18:29 +0200321#ifdef SUPPORT_GAMEPORT
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200322 struct gameport *gameport;
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200323 u16 axes[4];
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200324#endif
325
326 struct pci_dev *pci;
327 int irq;
Andreas Mohrca54bde2006-05-17 11:02:24 +0200328
Andreas Mohr627d3e72008-06-23 11:50:47 +0200329 /* register 0x6a is write-only, thus need to remember setting.
330 * If we need to add more registers here, then we might try to fold this
331 * into some transparent combined shadow register handling with
332 * CONFIG_PM register storage below, but that's slightly difficult. */
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200333 u16 shadow_reg_ctrl_6AH;
Andreas Mohr627d3e72008-06-23 11:50:47 +0200334
Andreas Mohrca54bde2006-05-17 11:02:24 +0200335#ifdef CONFIG_PM
336 /* register value containers for power management
337 * Note: not always full I/O range preserved (just like Win driver!) */
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200338 u16 saved_regs_ctrl[AZF_IO_SIZE_CTRL_PM / 2];
Andreas Mohr02330fb2008-05-16 12:18:29 +0200339 u16 saved_regs_game [AZF_IO_SIZE_GAME_PM / 2];
340 u16 saved_regs_mpu [AZF_IO_SIZE_MPU_PM / 2];
341 u16 saved_regs_opl3 [AZF_IO_SIZE_OPL3_PM / 2];
Andreas Mohrca54bde2006-05-17 11:02:24 +0200342 u16 saved_regs_mixer[AZF_IO_SIZE_MIXER_PM / 2];
343#endif
Takashi Iwai95de7762005-11-17 15:02:42 +0100344};
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200345
Takashi Iwaif40b6892006-07-05 16:51:05 +0200346static const struct pci_device_id snd_azf3328_ids[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700347 { 0x122D, 0x50DC, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* PCI168/3328 */
348 { 0x122D, 0x80DA, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* 3328 */
349 { 0, }
350};
351
352MODULE_DEVICE_TABLE(pci, snd_azf3328_ids);
353
Andreas Mohr02330fb2008-05-16 12:18:29 +0200354
355static int
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200356snd_azf3328_io_reg_setb(unsigned reg, u8 mask, bool do_set)
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200357{
Andreas Mohr02330fb2008-05-16 12:18:29 +0200358 u8 prev = inb(reg), new;
359
360 new = (do_set) ? (prev|mask) : (prev & ~mask);
361 /* we need to always write the new value no matter whether it differs
362 * or not, since some register bits don't indicate their setting */
363 outb(new, reg);
364 if (new != prev)
365 return 1;
366
367 return 0;
368}
369
Andreas Mohr02330fb2008-05-16 12:18:29 +0200370static inline void
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200371snd_azf3328_codec_outb(const struct snd_azf3328_codec_data *codec,
372 unsigned reg,
373 u8 value
374)
Andreas Mohr02330fb2008-05-16 12:18:29 +0200375{
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200376 outb(value, codec->io_base + reg);
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200377}
378
379static inline u8
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200380snd_azf3328_codec_inb(const struct snd_azf3328_codec_data *codec, unsigned reg)
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200381{
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200382 return inb(codec->io_base + reg);
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200383}
384
385static inline void
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200386snd_azf3328_codec_outw(const struct snd_azf3328_codec_data *codec,
387 unsigned reg,
388 u16 value
389)
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200390{
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200391 outw(value, codec->io_base + reg);
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200392}
393
394static inline u16
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200395snd_azf3328_codec_inw(const struct snd_azf3328_codec_data *codec, unsigned reg)
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200396{
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200397 return inw(codec->io_base + reg);
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200398}
399
400static inline void
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200401snd_azf3328_codec_outl(const struct snd_azf3328_codec_data *codec,
402 unsigned reg,
403 u32 value
404)
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200405{
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200406 outl(value, codec->io_base + reg);
Andreas Mohr02330fb2008-05-16 12:18:29 +0200407}
408
409static inline u32
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200410snd_azf3328_codec_inl(const struct snd_azf3328_codec_data *codec, unsigned reg)
Andreas Mohr02330fb2008-05-16 12:18:29 +0200411{
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200412 return inl(codec->io_base + reg);
413}
414
415static inline void
416snd_azf3328_ctrl_outb(const struct snd_azf3328 *chip, unsigned reg, u8 value)
417{
418 outb(value, chip->ctrl_io + reg);
419}
420
421static inline u8
422snd_azf3328_ctrl_inb(const struct snd_azf3328 *chip, unsigned reg)
423{
424 return inb(chip->ctrl_io + reg);
425}
426
427static inline void
428snd_azf3328_ctrl_outw(const struct snd_azf3328 *chip, unsigned reg, u16 value)
429{
430 outw(value, chip->ctrl_io + reg);
431}
432
433static inline void
434snd_azf3328_ctrl_outl(const struct snd_azf3328 *chip, unsigned reg, u32 value)
435{
436 outl(value, chip->ctrl_io + reg);
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200437}
438
439static inline void
Andreas Mohr02330fb2008-05-16 12:18:29 +0200440snd_azf3328_game_outb(const struct snd_azf3328 *chip, unsigned reg, u8 value)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700441{
Andreas Mohr02330fb2008-05-16 12:18:29 +0200442 outb(value, chip->game_io + reg);
443}
444
445static inline void
446snd_azf3328_game_outw(const struct snd_azf3328 *chip, unsigned reg, u16 value)
447{
448 outw(value, chip->game_io + reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700449}
450
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200451static inline u8
Andreas Mohr02330fb2008-05-16 12:18:29 +0200452snd_azf3328_game_inb(const struct snd_azf3328 *chip, unsigned reg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700453{
Andreas Mohr02330fb2008-05-16 12:18:29 +0200454 return inb(chip->game_io + reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700455}
456
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200457static inline u16
Andreas Mohr02330fb2008-05-16 12:18:29 +0200458snd_azf3328_game_inw(const struct snd_azf3328 *chip, unsigned reg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700459{
Andreas Mohr02330fb2008-05-16 12:18:29 +0200460 return inw(chip->game_io + reg);
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200461}
462
Andreas Mohr02330fb2008-05-16 12:18:29 +0200463static inline void
464snd_azf3328_mixer_outw(const struct snd_azf3328 *chip, unsigned reg, u16 value)
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200465{
Andreas Mohr02330fb2008-05-16 12:18:29 +0200466 outw(value, chip->mixer_io + reg);
467}
468
469static inline u16
470snd_azf3328_mixer_inw(const struct snd_azf3328 *chip, unsigned reg)
471{
472 return inw(chip->mixer_io + reg);
473}
474
475#define AZF_MUTE_BIT 0x80
476
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200477static bool
Andreas Mohr02330fb2008-05-16 12:18:29 +0200478snd_azf3328_mixer_set_mute(const struct snd_azf3328 *chip,
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200479 unsigned reg, bool do_mute
Andreas Mohr02330fb2008-05-16 12:18:29 +0200480)
481{
482 unsigned long portbase = chip->mixer_io + reg + 1;
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200483 bool updated;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700484
485 /* the mute bit is on the *second* (i.e. right) register of a
486 * left/right channel setting */
Andreas Mohr02330fb2008-05-16 12:18:29 +0200487 updated = snd_azf3328_io_reg_setb(portbase, AZF_MUTE_BIT, do_mute);
488
489 /* indicate whether it was muted before */
490 return (do_mute) ? !updated : updated;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700491}
492
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200493static void
Andreas Mohr02330fb2008-05-16 12:18:29 +0200494snd_azf3328_mixer_write_volume_gradually(const struct snd_azf3328 *chip,
495 unsigned reg,
496 unsigned char dst_vol_left,
497 unsigned char dst_vol_right,
498 int chan_sel, int delay
499)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700500{
Andreas Mohr02330fb2008-05-16 12:18:29 +0200501 unsigned long portbase = chip->mixer_io + reg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700502 unsigned char curr_vol_left = 0, curr_vol_right = 0;
Andreas Mohr02330fb2008-05-16 12:18:29 +0200503 int left_change = 0, right_change = 0;
504
Linus Torvalds1da177e2005-04-16 15:20:36 -0700505 snd_azf3328_dbgcallenter();
Andreas Mohr02330fb2008-05-16 12:18:29 +0200506
507 if (chan_sel & SET_CHAN_LEFT) {
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200508 curr_vol_left = inb(portbase + 1);
Andreas Mohr02330fb2008-05-16 12:18:29 +0200509
510 /* take care of muting flag contained in left channel */
511 if (curr_vol_left & AZF_MUTE_BIT)
512 dst_vol_left |= AZF_MUTE_BIT;
513 else
514 dst_vol_left &= ~AZF_MUTE_BIT;
515
516 left_change = (curr_vol_left > dst_vol_left) ? -1 : 1;
517 }
518
519 if (chan_sel & SET_CHAN_RIGHT) {
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200520 curr_vol_right = inb(portbase + 0);
Andreas Mohr02330fb2008-05-16 12:18:29 +0200521
522 right_change = (curr_vol_right > dst_vol_right) ? -1 : 1;
523 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700524
Andreas Mohre2f87262006-05-17 11:04:19 +0200525 do {
Andreas Mohr02330fb2008-05-16 12:18:29 +0200526 if (left_change) {
527 if (curr_vol_left != dst_vol_left) {
528 curr_vol_left += left_change;
529 outb(curr_vol_left, portbase + 1);
530 } else
531 left_change = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700532 }
Andreas Mohr02330fb2008-05-16 12:18:29 +0200533 if (right_change) {
534 if (curr_vol_right != dst_vol_right) {
535 curr_vol_right += right_change;
536
Linus Torvalds1da177e2005-04-16 15:20:36 -0700537 /* during volume change, the right channel is crackling
538 * somewhat more than the left channel, unfortunately.
539 * This seems to be a hardware issue. */
Andreas Mohr02330fb2008-05-16 12:18:29 +0200540 outb(curr_vol_right, portbase + 0);
541 } else
542 right_change = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700543 }
544 if (delay)
545 mdelay(delay);
Andreas Mohr02330fb2008-05-16 12:18:29 +0200546 } while ((left_change) || (right_change));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700547 snd_azf3328_dbgcallleave();
548}
549
550/*
551 * general mixer element
552 */
Takashi Iwai95de7762005-11-17 15:02:42 +0100553struct azf3328_mixer_reg {
Andreas Mohr02330fb2008-05-16 12:18:29 +0200554 unsigned reg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700555 unsigned int lchan_shift, rchan_shift;
556 unsigned int mask;
557 unsigned int invert: 1;
558 unsigned int stereo: 1;
559 unsigned int enum_c: 4;
Takashi Iwai95de7762005-11-17 15:02:42 +0100560};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700561
562#define COMPOSE_MIXER_REG(reg,lchan_shift,rchan_shift,mask,invert,stereo,enum_c) \
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200563 ((reg) | (lchan_shift << 8) | (rchan_shift << 12) | \
564 (mask << 16) | \
565 (invert << 24) | \
566 (stereo << 25) | \
567 (enum_c << 26))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700568
Takashi Iwai95de7762005-11-17 15:02:42 +0100569static void snd_azf3328_mixer_reg_decode(struct azf3328_mixer_reg *r, unsigned long val)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700570{
571 r->reg = val & 0xff;
572 r->lchan_shift = (val >> 8) & 0x0f;
573 r->rchan_shift = (val >> 12) & 0x0f;
574 r->mask = (val >> 16) & 0xff;
575 r->invert = (val >> 24) & 1;
576 r->stereo = (val >> 25) & 1;
577 r->enum_c = (val >> 26) & 0x0f;
578}
579
580/*
581 * mixer switches/volumes
582 */
583
584#define AZF3328_MIXER_SWITCH(xname, reg, shift, invert) \
585{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
586 .info = snd_azf3328_info_mixer, \
587 .get = snd_azf3328_get_mixer, .put = snd_azf3328_put_mixer, \
588 .private_value = COMPOSE_MIXER_REG(reg, shift, 0, 0x1, invert, 0, 0), \
589}
590
591#define AZF3328_MIXER_VOL_STEREO(xname, reg, mask, invert) \
592{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
593 .info = snd_azf3328_info_mixer, \
594 .get = snd_azf3328_get_mixer, .put = snd_azf3328_put_mixer, \
595 .private_value = COMPOSE_MIXER_REG(reg, 8, 0, mask, invert, 1, 0), \
596}
597
598#define AZF3328_MIXER_VOL_MONO(xname, reg, mask, is_right_chan) \
599{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
600 .info = snd_azf3328_info_mixer, \
601 .get = snd_azf3328_get_mixer, .put = snd_azf3328_put_mixer, \
602 .private_value = COMPOSE_MIXER_REG(reg, is_right_chan ? 0 : 8, 0, mask, 1, 0, 0), \
603}
604
605#define AZF3328_MIXER_VOL_SPECIAL(xname, reg, mask, shift, invert) \
606{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
607 .info = snd_azf3328_info_mixer, \
608 .get = snd_azf3328_get_mixer, .put = snd_azf3328_put_mixer, \
609 .private_value = COMPOSE_MIXER_REG(reg, shift, 0, mask, invert, 0, 0), \
610}
611
612#define AZF3328_MIXER_ENUM(xname, reg, enum_c, shift) \
613{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
614 .info = snd_azf3328_info_mixer_enum, \
615 .get = snd_azf3328_get_mixer_enum, .put = snd_azf3328_put_mixer_enum, \
616 .private_value = COMPOSE_MIXER_REG(reg, shift, 0, 0, 0, 0, enum_c), \
617}
618
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200619static int
Takashi Iwai95de7762005-11-17 15:02:42 +0100620snd_azf3328_info_mixer(struct snd_kcontrol *kcontrol,
621 struct snd_ctl_elem_info *uinfo)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700622{
Takashi Iwai95de7762005-11-17 15:02:42 +0100623 struct azf3328_mixer_reg reg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700624
625 snd_azf3328_dbgcallenter();
626 snd_azf3328_mixer_reg_decode(&reg, kcontrol->private_value);
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200627 uinfo->type = reg.mask == 1 ?
628 SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700629 uinfo->count = reg.stereo + 1;
630 uinfo->value.integer.min = 0;
631 uinfo->value.integer.max = reg.mask;
632 snd_azf3328_dbgcallleave();
633 return 0;
634}
635
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200636static int
Takashi Iwai95de7762005-11-17 15:02:42 +0100637snd_azf3328_get_mixer(struct snd_kcontrol *kcontrol,
638 struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700639{
Takashi Iwai95de7762005-11-17 15:02:42 +0100640 struct snd_azf3328 *chip = snd_kcontrol_chip(kcontrol);
641 struct azf3328_mixer_reg reg;
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200642 u16 oreg, val;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700643
644 snd_azf3328_dbgcallenter();
645 snd_azf3328_mixer_reg_decode(&reg, kcontrol->private_value);
646
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200647 oreg = snd_azf3328_mixer_inw(chip, reg.reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700648 val = (oreg >> reg.lchan_shift) & reg.mask;
649 if (reg.invert)
650 val = reg.mask - val;
651 ucontrol->value.integer.value[0] = val;
652 if (reg.stereo) {
653 val = (oreg >> reg.rchan_shift) & reg.mask;
654 if (reg.invert)
655 val = reg.mask - val;
656 ucontrol->value.integer.value[1] = val;
657 }
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200658 snd_azf3328_dbgmixer("get: %02x is %04x -> vol %02lx|%02lx "
659 "(shift %02d|%02d, mask %02x, inv. %d, stereo %d)\n",
660 reg.reg, oreg,
661 ucontrol->value.integer.value[0], ucontrol->value.integer.value[1],
662 reg.lchan_shift, reg.rchan_shift, reg.mask, reg.invert, reg.stereo);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700663 snd_azf3328_dbgcallleave();
664 return 0;
665}
666
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200667static int
Takashi Iwai95de7762005-11-17 15:02:42 +0100668snd_azf3328_put_mixer(struct snd_kcontrol *kcontrol,
669 struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700670{
Takashi Iwai95de7762005-11-17 15:02:42 +0100671 struct snd_azf3328 *chip = snd_kcontrol_chip(kcontrol);
672 struct azf3328_mixer_reg reg;
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200673 u16 oreg, nreg, val;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700674
675 snd_azf3328_dbgcallenter();
676 snd_azf3328_mixer_reg_decode(&reg, kcontrol->private_value);
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200677 oreg = snd_azf3328_mixer_inw(chip, reg.reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700678 val = ucontrol->value.integer.value[0] & reg.mask;
679 if (reg.invert)
680 val = reg.mask - val;
681 nreg = oreg & ~(reg.mask << reg.lchan_shift);
682 nreg |= (val << reg.lchan_shift);
683 if (reg.stereo) {
684 val = ucontrol->value.integer.value[1] & reg.mask;
685 if (reg.invert)
686 val = reg.mask - val;
687 nreg &= ~(reg.mask << reg.rchan_shift);
688 nreg |= (val << reg.rchan_shift);
689 }
690 if (reg.mask >= 0x07) /* it's a volume control, so better take care */
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200691 snd_azf3328_mixer_write_volume_gradually(
692 chip, reg.reg, nreg >> 8, nreg & 0xff,
693 /* just set both channels, doesn't matter */
694 SET_CHAN_LEFT|SET_CHAN_RIGHT,
695 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700696 else
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200697 snd_azf3328_mixer_outw(chip, reg.reg, nreg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700698
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200699 snd_azf3328_dbgmixer("put: %02x to %02lx|%02lx, "
700 "oreg %04x; shift %02d|%02d -> nreg %04x; after: %04x\n",
701 reg.reg, ucontrol->value.integer.value[0], ucontrol->value.integer.value[1],
702 oreg, reg.lchan_shift, reg.rchan_shift,
703 nreg, snd_azf3328_mixer_inw(chip, reg.reg));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700704 snd_azf3328_dbgcallleave();
705 return (nreg != oreg);
706}
707
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200708static int
Takashi Iwai95de7762005-11-17 15:02:42 +0100709snd_azf3328_info_mixer_enum(struct snd_kcontrol *kcontrol,
710 struct snd_ctl_elem_info *uinfo)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700711{
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200712 static const char * const texts1[] = {
Andreas Mohr13769e32006-05-17 11:03:16 +0200713 "Mic1", "Mic2"
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200714 };
715 static const char * const texts2[] = {
Andreas Mohr13769e32006-05-17 11:03:16 +0200716 "Mix", "Mic"
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200717 };
718 static const char * const texts3[] = {
Andreas Mohr02330fb2008-05-16 12:18:29 +0200719 "Mic", "CD", "Video", "Aux",
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200720 "Line", "Mix", "Mix Mono", "Phone"
Linus Torvalds1da177e2005-04-16 15:20:36 -0700721 };
Andreas Mohr13769e32006-05-17 11:03:16 +0200722 static const char * const texts4[] = {
723 "pre 3D", "post 3D"
724 };
Takashi Iwai95de7762005-11-17 15:02:42 +0100725 struct azf3328_mixer_reg reg;
Andreas Mohr627d3e72008-06-23 11:50:47 +0200726 const char * const *p = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700727
728 snd_azf3328_mixer_reg_decode(&reg, kcontrol->private_value);
729 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
730 uinfo->count = (reg.reg == IDX_MIXER_REC_SELECT) ? 2 : 1;
731 uinfo->value.enumerated.items = reg.enum_c;
732 if (uinfo->value.enumerated.item > reg.enum_c - 1U)
733 uinfo->value.enumerated.item = reg.enum_c - 1U;
Andreas Mohre2f87262006-05-17 11:04:19 +0200734 if (reg.reg == IDX_MIXER_ADVCTL2) {
Andreas Mohr13769e32006-05-17 11:03:16 +0200735 switch(reg.lchan_shift) {
736 case 8: /* modem out sel */
Andreas Mohr627d3e72008-06-23 11:50:47 +0200737 p = texts1;
Andreas Mohr13769e32006-05-17 11:03:16 +0200738 break;
739 case 9: /* mono sel source */
Andreas Mohr627d3e72008-06-23 11:50:47 +0200740 p = texts2;
Andreas Mohr13769e32006-05-17 11:03:16 +0200741 break;
742 case 15: /* PCM Out Path */
Andreas Mohr627d3e72008-06-23 11:50:47 +0200743 p = texts4;
Andreas Mohr13769e32006-05-17 11:03:16 +0200744 break;
745 }
Andreas Mohre2f87262006-05-17 11:04:19 +0200746 } else
Andreas Mohr02330fb2008-05-16 12:18:29 +0200747 if (reg.reg == IDX_MIXER_REC_SELECT)
Andreas Mohr627d3e72008-06-23 11:50:47 +0200748 p = texts3;
Andreas Mohr02330fb2008-05-16 12:18:29 +0200749
Andreas Mohr627d3e72008-06-23 11:50:47 +0200750 strcpy(uinfo->value.enumerated.name, p[uinfo->value.enumerated.item]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700751 return 0;
752}
753
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200754static int
Takashi Iwai95de7762005-11-17 15:02:42 +0100755snd_azf3328_get_mixer_enum(struct snd_kcontrol *kcontrol,
756 struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700757{
Takashi Iwai95de7762005-11-17 15:02:42 +0100758 struct snd_azf3328 *chip = snd_kcontrol_chip(kcontrol);
759 struct azf3328_mixer_reg reg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700760 unsigned short val;
Andreas Mohr02330fb2008-05-16 12:18:29 +0200761
Linus Torvalds1da177e2005-04-16 15:20:36 -0700762 snd_azf3328_mixer_reg_decode(&reg, kcontrol->private_value);
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200763 val = snd_azf3328_mixer_inw(chip, reg.reg);
Andreas Mohre2f87262006-05-17 11:04:19 +0200764 if (reg.reg == IDX_MIXER_REC_SELECT) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700765 ucontrol->value.enumerated.item[0] = (val >> 8) & (reg.enum_c - 1);
766 ucontrol->value.enumerated.item[1] = (val >> 0) & (reg.enum_c - 1);
Andreas Mohre2f87262006-05-17 11:04:19 +0200767 } else
Linus Torvalds1da177e2005-04-16 15:20:36 -0700768 ucontrol->value.enumerated.item[0] = (val >> reg.lchan_shift) & (reg.enum_c - 1);
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200769
770 snd_azf3328_dbgmixer("get_enum: %02x is %04x -> %d|%d (shift %02d, enum_c %d)\n",
771 reg.reg, val, ucontrol->value.enumerated.item[0], ucontrol->value.enumerated.item[1],
772 reg.lchan_shift, reg.enum_c);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700773 return 0;
774}
775
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200776static int
Takashi Iwai95de7762005-11-17 15:02:42 +0100777snd_azf3328_put_mixer_enum(struct snd_kcontrol *kcontrol,
778 struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700779{
Takashi Iwai95de7762005-11-17 15:02:42 +0100780 struct snd_azf3328 *chip = snd_kcontrol_chip(kcontrol);
781 struct azf3328_mixer_reg reg;
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200782 u16 oreg, nreg, val;
Andreas Mohr02330fb2008-05-16 12:18:29 +0200783
Linus Torvalds1da177e2005-04-16 15:20:36 -0700784 snd_azf3328_mixer_reg_decode(&reg, kcontrol->private_value);
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200785 oreg = snd_azf3328_mixer_inw(chip, reg.reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700786 val = oreg;
Andreas Mohre2f87262006-05-17 11:04:19 +0200787 if (reg.reg == IDX_MIXER_REC_SELECT) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700788 if (ucontrol->value.enumerated.item[0] > reg.enum_c - 1U ||
789 ucontrol->value.enumerated.item[1] > reg.enum_c - 1U)
790 return -EINVAL;
791 val = (ucontrol->value.enumerated.item[0] << 8) |
792 (ucontrol->value.enumerated.item[1] << 0);
Andreas Mohre2f87262006-05-17 11:04:19 +0200793 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700794 if (ucontrol->value.enumerated.item[0] > reg.enum_c - 1U)
795 return -EINVAL;
796 val &= ~((reg.enum_c - 1) << reg.lchan_shift);
797 val |= (ucontrol->value.enumerated.item[0] << reg.lchan_shift);
798 }
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200799 snd_azf3328_mixer_outw(chip, reg.reg, val);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700800 nreg = val;
801
802 snd_azf3328_dbgmixer("put_enum: %02x to %04x, oreg %04x\n", reg.reg, val, oreg);
803 return (nreg != oreg);
804}
805
Takashi Iwai1b60f6b2007-03-13 22:13:47 +0100806static struct snd_kcontrol_new snd_azf3328_mixer_controls[] __devinitdata = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700807 AZF3328_MIXER_SWITCH("Master Playback Switch", IDX_MIXER_PLAY_MASTER, 15, 1),
808 AZF3328_MIXER_VOL_STEREO("Master Playback Volume", IDX_MIXER_PLAY_MASTER, 0x1f, 1),
Andreas Mohr627d3e72008-06-23 11:50:47 +0200809 AZF3328_MIXER_SWITCH("PCM Playback Switch", IDX_MIXER_WAVEOUT, 15, 1),
810 AZF3328_MIXER_VOL_STEREO("PCM Playback Volume",
811 IDX_MIXER_WAVEOUT, 0x1f, 1),
812 AZF3328_MIXER_SWITCH("PCM 3D Bypass Playback Switch",
813 IDX_MIXER_ADVCTL2, 7, 1),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700814 AZF3328_MIXER_SWITCH("FM Playback Switch", IDX_MIXER_FMSYNTH, 15, 1),
815 AZF3328_MIXER_VOL_STEREO("FM Playback Volume", IDX_MIXER_FMSYNTH, 0x1f, 1),
816 AZF3328_MIXER_SWITCH("CD Playback Switch", IDX_MIXER_CDAUDIO, 15, 1),
817 AZF3328_MIXER_VOL_STEREO("CD Playback Volume", IDX_MIXER_CDAUDIO, 0x1f, 1),
818 AZF3328_MIXER_SWITCH("Capture Switch", IDX_MIXER_REC_VOLUME, 15, 1),
819 AZF3328_MIXER_VOL_STEREO("Capture Volume", IDX_MIXER_REC_VOLUME, 0x0f, 0),
820 AZF3328_MIXER_ENUM("Capture Source", IDX_MIXER_REC_SELECT, 8, 0),
821 AZF3328_MIXER_SWITCH("Mic Playback Switch", IDX_MIXER_MIC, 15, 1),
822 AZF3328_MIXER_VOL_MONO("Mic Playback Volume", IDX_MIXER_MIC, 0x1f, 1),
823 AZF3328_MIXER_SWITCH("Mic Boost (+20dB)", IDX_MIXER_MIC, 6, 0),
824 AZF3328_MIXER_SWITCH("Line Playback Switch", IDX_MIXER_LINEIN, 15, 1),
825 AZF3328_MIXER_VOL_STEREO("Line Playback Volume", IDX_MIXER_LINEIN, 0x1f, 1),
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200826 AZF3328_MIXER_SWITCH("PC Speaker Playback Switch", IDX_MIXER_PCBEEP, 15, 1),
827 AZF3328_MIXER_VOL_SPECIAL("PC Speaker Playback Volume", IDX_MIXER_PCBEEP, 0x0f, 1, 1),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700828 AZF3328_MIXER_SWITCH("Video Playback Switch", IDX_MIXER_VIDEO, 15, 1),
829 AZF3328_MIXER_VOL_STEREO("Video Playback Volume", IDX_MIXER_VIDEO, 0x1f, 1),
830 AZF3328_MIXER_SWITCH("Aux Playback Switch", IDX_MIXER_AUX, 15, 1),
831 AZF3328_MIXER_VOL_STEREO("Aux Playback Volume", IDX_MIXER_AUX, 0x1f, 1),
832 AZF3328_MIXER_SWITCH("Modem Playback Switch", IDX_MIXER_MODEMOUT, 15, 1),
833 AZF3328_MIXER_VOL_MONO("Modem Playback Volume", IDX_MIXER_MODEMOUT, 0x1f, 1),
834 AZF3328_MIXER_SWITCH("Modem Capture Switch", IDX_MIXER_MODEMIN, 15, 1),
835 AZF3328_MIXER_VOL_MONO("Modem Capture Volume", IDX_MIXER_MODEMIN, 0x1f, 1),
Andreas Mohr13769e32006-05-17 11:03:16 +0200836 AZF3328_MIXER_ENUM("Mic Select", IDX_MIXER_ADVCTL2, 2, 8),
837 AZF3328_MIXER_ENUM("Mono Output Select", IDX_MIXER_ADVCTL2, 2, 9),
Andreas Mohre24a1212007-03-26 12:49:45 +0200838 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 -0700839 AZF3328_MIXER_VOL_SPECIAL("Tone Control - Treble", IDX_MIXER_BASSTREBLE, 0x07, 1, 0),
840 AZF3328_MIXER_VOL_SPECIAL("Tone Control - Bass", IDX_MIXER_BASSTREBLE, 0x07, 9, 0),
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200841 AZF3328_MIXER_SWITCH("3D Control - Switch", IDX_MIXER_ADVCTL2, 13, 0),
Andreas Mohr13769e32006-05-17 11:03:16 +0200842 AZF3328_MIXER_VOL_SPECIAL("3D Control - Width", IDX_MIXER_ADVCTL1, 0x07, 1, 0), /* "3D Width" */
843 AZF3328_MIXER_VOL_SPECIAL("3D Control - Depth", IDX_MIXER_ADVCTL1, 0x03, 8, 0), /* "Hifi 3D" */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700844#if MIXER_TESTING
845 AZF3328_MIXER_SWITCH("0", IDX_MIXER_ADVCTL2, 0, 0),
846 AZF3328_MIXER_SWITCH("1", IDX_MIXER_ADVCTL2, 1, 0),
847 AZF3328_MIXER_SWITCH("2", IDX_MIXER_ADVCTL2, 2, 0),
848 AZF3328_MIXER_SWITCH("3", IDX_MIXER_ADVCTL2, 3, 0),
849 AZF3328_MIXER_SWITCH("4", IDX_MIXER_ADVCTL2, 4, 0),
850 AZF3328_MIXER_SWITCH("5", IDX_MIXER_ADVCTL2, 5, 0),
851 AZF3328_MIXER_SWITCH("6", IDX_MIXER_ADVCTL2, 6, 0),
852 AZF3328_MIXER_SWITCH("7", IDX_MIXER_ADVCTL2, 7, 0),
853 AZF3328_MIXER_SWITCH("8", IDX_MIXER_ADVCTL2, 8, 0),
854 AZF3328_MIXER_SWITCH("9", IDX_MIXER_ADVCTL2, 9, 0),
855 AZF3328_MIXER_SWITCH("10", IDX_MIXER_ADVCTL2, 10, 0),
856 AZF3328_MIXER_SWITCH("11", IDX_MIXER_ADVCTL2, 11, 0),
857 AZF3328_MIXER_SWITCH("12", IDX_MIXER_ADVCTL2, 12, 0),
858 AZF3328_MIXER_SWITCH("13", IDX_MIXER_ADVCTL2, 13, 0),
859 AZF3328_MIXER_SWITCH("14", IDX_MIXER_ADVCTL2, 14, 0),
860 AZF3328_MIXER_SWITCH("15", IDX_MIXER_ADVCTL2, 15, 0),
861#endif
862};
863
Takashi Iwai1b60f6b2007-03-13 22:13:47 +0100864static u16 __devinitdata snd_azf3328_init_values[][2] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700865 { IDX_MIXER_PLAY_MASTER, MIXER_MUTE_MASK|0x1f1f },
866 { IDX_MIXER_MODEMOUT, MIXER_MUTE_MASK|0x1f1f },
867 { IDX_MIXER_BASSTREBLE, 0x0000 },
868 { IDX_MIXER_PCBEEP, MIXER_MUTE_MASK|0x1f1f },
869 { IDX_MIXER_MODEMIN, MIXER_MUTE_MASK|0x1f1f },
870 { IDX_MIXER_MIC, MIXER_MUTE_MASK|0x001f },
871 { IDX_MIXER_LINEIN, MIXER_MUTE_MASK|0x1f1f },
872 { IDX_MIXER_CDAUDIO, MIXER_MUTE_MASK|0x1f1f },
873 { IDX_MIXER_VIDEO, MIXER_MUTE_MASK|0x1f1f },
874 { IDX_MIXER_AUX, MIXER_MUTE_MASK|0x1f1f },
875 { IDX_MIXER_WAVEOUT, MIXER_MUTE_MASK|0x1f1f },
876 { IDX_MIXER_FMSYNTH, MIXER_MUTE_MASK|0x1f1f },
877 { IDX_MIXER_REC_VOLUME, MIXER_MUTE_MASK|0x0707 },
878};
879
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200880static int __devinit
Takashi Iwai95de7762005-11-17 15:02:42 +0100881snd_azf3328_mixer_new(struct snd_azf3328 *chip)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700882{
Takashi Iwai95de7762005-11-17 15:02:42 +0100883 struct snd_card *card;
884 const struct snd_kcontrol_new *sw;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700885 unsigned int idx;
886 int err;
887
888 snd_azf3328_dbgcallenter();
Takashi Iwaida3cec32008-08-08 17:12:14 +0200889 if (snd_BUG_ON(!chip || !chip->card))
890 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700891
892 card = chip->card;
893
894 /* mixer reset */
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200895 snd_azf3328_mixer_outw(chip, IDX_MIXER_RESET, 0x0000);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700896
897 /* mute and zero volume channels */
Andreas Mohr02330fb2008-05-16 12:18:29 +0200898 for (idx = 0; idx < ARRAY_SIZE(snd_azf3328_init_values); ++idx) {
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200899 snd_azf3328_mixer_outw(chip,
900 snd_azf3328_init_values[idx][0],
901 snd_azf3328_init_values[idx][1]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700902 }
Andreas Mohr02330fb2008-05-16 12:18:29 +0200903
Linus Torvalds1da177e2005-04-16 15:20:36 -0700904 /* add mixer controls */
905 sw = snd_azf3328_mixer_controls;
Andreas Mohr02330fb2008-05-16 12:18:29 +0200906 for (idx = 0; idx < ARRAY_SIZE(snd_azf3328_mixer_controls);
907 ++idx, ++sw) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700908 if ((err = snd_ctl_add(chip->card, snd_ctl_new1(sw, chip))) < 0)
909 return err;
910 }
911 snd_component_add(card, "AZF3328 mixer");
912 strcpy(card->mixername, "AZF3328 mixer");
913
914 snd_azf3328_dbgcallleave();
915 return 0;
916}
917
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200918static int
Takashi Iwai95de7762005-11-17 15:02:42 +0100919snd_azf3328_hw_params(struct snd_pcm_substream *substream,
920 struct snd_pcm_hw_params *hw_params)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700921{
922 int res;
923 snd_azf3328_dbgcallenter();
924 res = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
925 snd_azf3328_dbgcallleave();
926 return res;
927}
928
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200929static int
Takashi Iwai95de7762005-11-17 15:02:42 +0100930snd_azf3328_hw_free(struct snd_pcm_substream *substream)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700931{
932 snd_azf3328_dbgcallenter();
933 snd_pcm_lib_free_pages(substream);
934 snd_azf3328_dbgcallleave();
935 return 0;
936}
937
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200938static void
Andreas Mohr02330fb2008-05-16 12:18:29 +0200939snd_azf3328_codec_setfmt(struct snd_azf3328 *chip,
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200940 enum snd_azf3328_codec_type codec_type,
Andreas Mohr627d3e72008-06-23 11:50:47 +0200941 enum azf_freq_t bitrate,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700942 unsigned int format_width,
943 unsigned int channels
944)
945{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700946 unsigned long flags;
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200947 const struct snd_azf3328_codec_data *codec = &chip->codecs[codec_type];
948 u16 val = 0xff00;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700949
950 snd_azf3328_dbgcallenter();
951 switch (bitrate) {
Andreas Mohr02330fb2008-05-16 12:18:29 +0200952 case AZF_FREQ_4000: val |= SOUNDFORMAT_FREQ_SUSPECTED_4000; break;
953 case AZF_FREQ_4800: val |= SOUNDFORMAT_FREQ_SUSPECTED_4800; break;
954 case AZF_FREQ_5512:
955 /* the AZF3328 names it "5510" for some strange reason */
956 val |= SOUNDFORMAT_FREQ_5510; break;
957 case AZF_FREQ_6620: val |= SOUNDFORMAT_FREQ_6620; break;
958 case AZF_FREQ_8000: val |= SOUNDFORMAT_FREQ_8000; break;
959 case AZF_FREQ_9600: val |= SOUNDFORMAT_FREQ_9600; break;
960 case AZF_FREQ_11025: val |= SOUNDFORMAT_FREQ_11025; break;
961 case AZF_FREQ_13240: val |= SOUNDFORMAT_FREQ_SUSPECTED_13240; break;
962 case AZF_FREQ_16000: val |= SOUNDFORMAT_FREQ_16000; break;
963 case AZF_FREQ_22050: val |= SOUNDFORMAT_FREQ_22050; break;
964 case AZF_FREQ_32000: val |= SOUNDFORMAT_FREQ_32000; break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700965 default:
Takashi Iwai99b359b2005-10-20 18:26:44 +0200966 snd_printk(KERN_WARNING "unknown bitrate %d, assuming 44.1kHz!\n", bitrate);
Andreas Mohr02330fb2008-05-16 12:18:29 +0200967 /* fall-through */
968 case AZF_FREQ_44100: val |= SOUNDFORMAT_FREQ_44100; break;
969 case AZF_FREQ_48000: val |= SOUNDFORMAT_FREQ_48000; break;
970 case AZF_FREQ_66200: val |= SOUNDFORMAT_FREQ_SUSPECTED_66200; break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700971 }
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200972 /* val = 0xff07; 3m27.993s (65301Hz; -> 64000Hz???) hmm, 66120, 65967, 66123 */
973 /* val = 0xff09; 17m15.098s (13123,478Hz; -> 12000Hz???) hmm, 13237.2Hz? */
974 /* val = 0xff0a; 47m30.599s (4764,891Hz; -> 4800Hz???) yup, 4803Hz */
975 /* val = 0xff0c; 57m0.510s (4010,263Hz; -> 4000Hz???) yup, 4003Hz */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700976 /* val = 0xff05; 5m11.556s (... -> 44100Hz) */
977 /* val = 0xff03; 10m21.529s (21872,463Hz; -> 22050Hz???) */
978 /* val = 0xff0f; 20m41.883s (10937,993Hz; -> 11025Hz???) */
979 /* val = 0xff0d; 41m23.135s (5523,600Hz; -> 5512Hz???) */
980 /* val = 0xff0e; 28m30.777s (8017Hz; -> 8000Hz???) */
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200981
Linus Torvalds1da177e2005-04-16 15:20:36 -0700982 if (channels == 2)
983 val |= SOUNDFORMAT_FLAG_2CHANNELS;
984
985 if (format_width == 16)
986 val |= SOUNDFORMAT_FLAG_16BIT;
987
988 spin_lock_irqsave(&chip->reg_lock, flags);
Andreas Mohr02330fb2008-05-16 12:18:29 +0200989
Linus Torvalds1da177e2005-04-16 15:20:36 -0700990 /* set bitrate/format */
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200991 snd_azf3328_codec_outw(codec, IDX_IO_CODEC_SOUNDFORMAT, val);
Andreas Mohr02330fb2008-05-16 12:18:29 +0200992
Linus Torvalds1da177e2005-04-16 15:20:36 -0700993 /* changing the bitrate/format settings switches off the
994 * audio output with an annoying click in case of 8/16bit format change
995 * (maybe shutting down DAC/ADC?), thus immediately
996 * do some tweaking to reenable it and get rid of the clicking
997 * (FIXME: yes, it works, but what exactly am I doing here?? :)
998 * FIXME: does this have some side effects for full-duplex
999 * or other dramatic side effects? */
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001000 if (codec_type == AZF_CODEC_PLAYBACK) /* only do it for playback */
1001 snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS,
1002 snd_azf3328_codec_inw(codec, IDX_IO_CODEC_DMA_FLAGS) |
1003 DMA_RUN_SOMETHING1 |
1004 DMA_RUN_SOMETHING2 |
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001005 SOMETHING_ALMOST_ALWAYS_SET |
1006 DMA_EPILOGUE_SOMETHING |
1007 DMA_SOMETHING_ELSE
1008 );
Linus Torvalds1da177e2005-04-16 15:20:36 -07001009
1010 spin_unlock_irqrestore(&chip->reg_lock, flags);
1011 snd_azf3328_dbgcallleave();
1012}
1013
Andreas Mohr02330fb2008-05-16 12:18:29 +02001014static inline void
1015snd_azf3328_codec_setfmt_lowpower(struct snd_azf3328 *chip,
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001016 enum snd_azf3328_codec_type codec_type
Andreas Mohr02330fb2008-05-16 12:18:29 +02001017)
1018{
1019 /* choose lowest frequency for low power consumption.
1020 * While this will cause louder noise due to rather coarse frequency,
1021 * it should never matter since output should always
1022 * get disabled properly when idle anyway. */
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001023 snd_azf3328_codec_setfmt(chip, codec_type, AZF_FREQ_4000, 8, 1);
Andreas Mohr02330fb2008-05-16 12:18:29 +02001024}
1025
Andreas Mohr627d3e72008-06-23 11:50:47 +02001026static void
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001027snd_azf3328_ctrl_reg_6AH_update(struct snd_azf3328 *chip,
Andreas Mohr627d3e72008-06-23 11:50:47 +02001028 unsigned bitmask,
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001029 bool enable
Andreas Mohr627d3e72008-06-23 11:50:47 +02001030)
1031{
1032 if (enable)
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001033 chip->shadow_reg_ctrl_6AH &= ~bitmask;
Andreas Mohr627d3e72008-06-23 11:50:47 +02001034 else
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001035 chip->shadow_reg_ctrl_6AH |= bitmask;
Andreas Mohr627d3e72008-06-23 11:50:47 +02001036 snd_azf3328_dbgplay("6AH_update mask 0x%04x enable %d: val 0x%04x\n",
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001037 bitmask, enable, chip->shadow_reg_ctrl_6AH);
1038 snd_azf3328_ctrl_outw(chip, IDX_IO_6AH, chip->shadow_reg_ctrl_6AH);
Andreas Mohr627d3e72008-06-23 11:50:47 +02001039}
1040
Andreas Mohr02330fb2008-05-16 12:18:29 +02001041static inline void
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001042snd_azf3328_ctrl_enable_codecs(struct snd_azf3328 *chip, bool enable)
Andreas Mohr02330fb2008-05-16 12:18:29 +02001043{
Andreas Mohr627d3e72008-06-23 11:50:47 +02001044 snd_azf3328_dbgplay("codec_enable %d\n", enable);
Andreas Mohr02330fb2008-05-16 12:18:29 +02001045 /* no idea what exactly is being done here, but I strongly assume it's
1046 * PM related */
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001047 snd_azf3328_ctrl_reg_6AH_update(
Andreas Mohr627d3e72008-06-23 11:50:47 +02001048 chip, IO_6A_PAUSE_PLAYBACK_BIT8, enable
Andreas Mohr02330fb2008-05-16 12:18:29 +02001049 );
1050}
1051
1052static void
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001053snd_azf3328_ctrl_codec_activity(struct snd_azf3328 *chip,
1054 enum snd_azf3328_codec_type codec_type,
1055 bool enable
Andreas Mohr02330fb2008-05-16 12:18:29 +02001056)
1057{
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001058 struct snd_azf3328_codec_data *codec = &chip->codecs[codec_type];
1059 bool need_change = (codec->running != enable);
Andreas Mohr02330fb2008-05-16 12:18:29 +02001060
1061 snd_azf3328_dbgplay(
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001062 "codec_activity: %s codec, enable %d, need_change %d\n",
1063 codec->name, enable, need_change
Andreas Mohr02330fb2008-05-16 12:18:29 +02001064 );
1065 if (need_change) {
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001066 static const struct {
1067 enum snd_azf3328_codec_type other1;
1068 enum snd_azf3328_codec_type other2;
1069 } peer_codecs[3] =
1070 { { AZF_CODEC_CAPTURE, AZF_CODEC_I2S_OUT },
1071 { AZF_CODEC_PLAYBACK, AZF_CODEC_I2S_OUT },
1072 { AZF_CODEC_PLAYBACK, AZF_CODEC_CAPTURE } };
1073 bool call_function;
1074
1075 if (enable)
1076 /* if enable codec, call enable_codecs func
1077 to enable codec supply... */
1078 call_function = 1;
1079 else {
1080 /* ...otherwise call enable_codecs func
1081 (which globally shuts down operation of codecs)
1082 only in case the other codecs are currently
1083 not active either! */
1084 if ((!chip->codecs[peer_codecs[codec_type].other1]
1085 .running)
1086 && (!chip->codecs[peer_codecs[codec_type].other2]
1087 .running))
1088 call_function = 1;
1089 }
1090 if (call_function)
1091 snd_azf3328_ctrl_enable_codecs(chip, enable);
Andreas Mohr02330fb2008-05-16 12:18:29 +02001092
1093 /* ...and adjust clock, too
1094 * (reduce noise and power consumption) */
1095 if (!enable)
1096 snd_azf3328_codec_setfmt_lowpower(
1097 chip,
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001098 codec_type
Andreas Mohr02330fb2008-05-16 12:18:29 +02001099 );
1100 }
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001101 codec->running = enable;
Andreas Mohr02330fb2008-05-16 12:18:29 +02001102}
1103
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001104static void
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001105snd_azf3328_codec_setdmaa(struct snd_azf3328 *chip,
1106 enum snd_azf3328_codec_type codec_type,
1107 unsigned long addr,
1108 unsigned int count,
1109 unsigned int size
Andreas Mohr02330fb2008-05-16 12:18:29 +02001110)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001111{
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001112 const struct snd_azf3328_codec_data *codec = &chip->codecs[codec_type];
Linus Torvalds1da177e2005-04-16 15:20:36 -07001113 snd_azf3328_dbgcallenter();
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001114 if (!codec->running) {
1115 /* AZF3328 uses a two buffer pointer DMA transfer approach */
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001116
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001117 unsigned long flags;
Andreas Mohr02330fb2008-05-16 12:18:29 +02001118
1119 /* width 32bit (prevent overflow): */
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001120 u32 addr_area2, count_areas, lengths;
Andreas Mohr02330fb2008-05-16 12:18:29 +02001121
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001122 count_areas = size/2;
1123 addr_area2 = addr+count_areas;
1124 count_areas--; /* max. index */
1125 snd_azf3328_dbgplay("set DMA: buf1 %08lx[%lu], buf2 %08lx[%lu]\n", addr, count_areas, addr_area2, count_areas);
1126
1127 /* build combined I/O buffer length word */
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001128 lengths = (count_areas << 16) | (count_areas);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001129 spin_lock_irqsave(&chip->reg_lock, flags);
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001130 snd_azf3328_codec_outl(codec, IDX_IO_CODEC_DMA_START_1, addr);
1131 snd_azf3328_codec_outl(codec, IDX_IO_CODEC_DMA_START_2,
1132 addr_area2);
1133 snd_azf3328_codec_outl(codec, IDX_IO_CODEC_DMA_LENGTHS,
1134 lengths);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001135 spin_unlock_irqrestore(&chip->reg_lock, flags);
1136 }
1137 snd_azf3328_dbgcallleave();
1138}
1139
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001140static int
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001141snd_azf3328_codec_prepare(struct snd_pcm_substream *substream)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001142{
1143#if 0
Takashi Iwai95de7762005-11-17 15:02:42 +01001144 struct snd_azf3328 *chip = snd_pcm_substream_chip(substream);
1145 struct snd_pcm_runtime *runtime = substream->runtime;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001146 unsigned int size = snd_pcm_lib_buffer_bytes(substream);
1147 unsigned int count = snd_pcm_lib_period_bytes(substream);
1148#endif
1149
1150 snd_azf3328_dbgcallenter();
1151#if 0
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001152 snd_azf3328_codec_setfmt(chip, AZF_CODEC_...,
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001153 runtime->rate,
1154 snd_pcm_format_width(runtime->format),
1155 runtime->channels);
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001156 snd_azf3328_codec_setdmaa(chip, AZF_CODEC_...,
1157 runtime->dma_addr, count, size);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001158#endif
1159 snd_azf3328_dbgcallleave();
1160 return 0;
1161}
1162
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001163static int
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001164snd_azf3328_codec_trigger(enum snd_azf3328_codec_type codec_type,
1165 struct snd_pcm_substream *substream, int cmd)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001166{
Takashi Iwai95de7762005-11-17 15:02:42 +01001167 struct snd_azf3328 *chip = snd_pcm_substream_chip(substream);
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001168 const struct snd_azf3328_codec_data *codec = &chip->codecs[codec_type];
Takashi Iwai95de7762005-11-17 15:02:42 +01001169 struct snd_pcm_runtime *runtime = substream->runtime;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001170 int result = 0;
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001171 u16 flags1;
1172 bool previously_muted = 0;
1173 bool is_playback_codec = (AZF_CODEC_PLAYBACK == codec_type);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001174
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001175 snd_azf3328_dbgcalls("snd_azf3328_codec_trigger cmd %d\n", cmd);
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001176
Linus Torvalds1da177e2005-04-16 15:20:36 -07001177 switch (cmd) {
1178 case SNDRV_PCM_TRIGGER_START:
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001179 snd_azf3328_dbgplay("START %s\n", codec->name);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001180
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001181 if (is_playback_codec) {
1182 /* mute WaveOut (avoid clicking during setup) */
1183 previously_muted =
1184 snd_azf3328_mixer_set_mute(
1185 chip, IDX_MIXER_WAVEOUT, 1
1186 );
1187 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001188
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001189 snd_azf3328_codec_setfmt(chip, codec_type,
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001190 runtime->rate,
1191 snd_pcm_format_width(runtime->format),
1192 runtime->channels);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001193
1194 spin_lock(&chip->reg_lock);
Andreas Mohr02330fb2008-05-16 12:18:29 +02001195 /* first, remember current value: */
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001196 flags1 = snd_azf3328_codec_inw(codec, IDX_IO_CODEC_DMA_FLAGS);
Andreas Mohr02330fb2008-05-16 12:18:29 +02001197
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001198 /* stop transfer */
1199 flags1 &= ~DMA_RESUME;
1200 snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, flags1);
Andreas Mohr02330fb2008-05-16 12:18:29 +02001201
Linus Torvalds1da177e2005-04-16 15:20:36 -07001202 /* FIXME: clear interrupts or what??? */
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001203 snd_azf3328_codec_outw(codec, IDX_IO_CODEC_IRQTYPE, 0xffff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001204 spin_unlock(&chip->reg_lock);
1205
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001206 snd_azf3328_codec_setdmaa(chip, codec_type, runtime->dma_addr,
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001207 snd_pcm_lib_period_bytes(substream),
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001208 snd_pcm_lib_buffer_bytes(substream)
1209 );
Linus Torvalds1da177e2005-04-16 15:20:36 -07001210
1211 spin_lock(&chip->reg_lock);
1212#ifdef WIN9X
1213 /* FIXME: enable playback/recording??? */
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001214 flags1 |= DMA_RUN_SOMETHING1 | DMA_RUN_SOMETHING2;
1215 snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, flags1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001216
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001217 /* start transfer again */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001218 /* FIXME: what is this value (0x0010)??? */
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001219 flags1 |= DMA_RESUME | DMA_EPILOGUE_SOMETHING;
1220 snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, flags1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001221#else /* NT4 */
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001222 snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS,
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001223 0x0000);
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001224 snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS,
1225 DMA_RUN_SOMETHING1);
1226 snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS,
1227 DMA_RUN_SOMETHING1 |
1228 DMA_RUN_SOMETHING2);
1229 snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS,
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001230 DMA_RESUME |
1231 SOMETHING_ALMOST_ALWAYS_SET |
1232 DMA_EPILOGUE_SOMETHING |
1233 DMA_SOMETHING_ELSE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001234#endif
1235 spin_unlock(&chip->reg_lock);
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001236 snd_azf3328_ctrl_codec_activity(chip, codec_type, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001237
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001238 if (is_playback_codec) {
1239 /* now unmute WaveOut */
1240 if (!previously_muted)
1241 snd_azf3328_mixer_set_mute(
1242 chip, IDX_MIXER_WAVEOUT, 0
1243 );
1244 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001245
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001246 snd_azf3328_dbgplay("STARTED %s\n", codec->name);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001247 break;
Andreas Mohrca54bde2006-05-17 11:02:24 +02001248 case SNDRV_PCM_TRIGGER_RESUME:
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001249 snd_azf3328_dbgplay("RESUME %s\n", codec->name);
1250 /* resume codec if we were active */
Andreas Mohr02330fb2008-05-16 12:18:29 +02001251 spin_lock(&chip->reg_lock);
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001252 if (codec->running)
1253 snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS,
1254 snd_azf3328_codec_inw(
1255 codec, IDX_IO_CODEC_DMA_FLAGS
1256 ) | DMA_RESUME
1257 );
Andreas Mohr02330fb2008-05-16 12:18:29 +02001258 spin_unlock(&chip->reg_lock);
Andreas Mohrca54bde2006-05-17 11:02:24 +02001259 break;
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001260 case SNDRV_PCM_TRIGGER_STOP:
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001261 snd_azf3328_dbgplay("STOP %s\n", codec->name);
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001262
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001263 if (is_playback_codec) {
1264 /* mute WaveOut (avoid clicking during setup) */
1265 previously_muted =
1266 snd_azf3328_mixer_set_mute(
1267 chip, IDX_MIXER_WAVEOUT, 1
1268 );
1269 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001270
1271 spin_lock(&chip->reg_lock);
Andreas Mohr02330fb2008-05-16 12:18:29 +02001272 /* first, remember current value: */
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001273 flags1 = snd_azf3328_codec_inw(codec, IDX_IO_CODEC_DMA_FLAGS);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001274
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001275 /* stop transfer */
1276 flags1 &= ~DMA_RESUME;
1277 snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, flags1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001278
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001279 /* hmm, is this really required? we're resetting the same bit
1280 * immediately thereafter... */
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001281 flags1 |= DMA_RUN_SOMETHING1;
1282 snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, flags1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001283
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001284 flags1 &= ~DMA_RUN_SOMETHING1;
1285 snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, flags1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001286 spin_unlock(&chip->reg_lock);
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001287 snd_azf3328_ctrl_codec_activity(chip, codec_type, 0);
Andreas Mohr02330fb2008-05-16 12:18:29 +02001288
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001289 if (is_playback_codec) {
1290 /* now unmute WaveOut */
1291 if (!previously_muted)
1292 snd_azf3328_mixer_set_mute(
1293 chip, IDX_MIXER_WAVEOUT, 0
1294 );
1295 }
Andreas Mohr02330fb2008-05-16 12:18:29 +02001296
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001297 snd_azf3328_dbgplay("STOPPED %s\n", codec->name);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001298 break;
Andreas Mohrca54bde2006-05-17 11:02:24 +02001299 case SNDRV_PCM_TRIGGER_SUSPEND:
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001300 snd_azf3328_dbgplay("SUSPEND %s\n", codec->name);
1301 /* make sure codec is stopped */
1302 snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS,
1303 snd_azf3328_codec_inw(
1304 codec, IDX_IO_CODEC_DMA_FLAGS
1305 ) & ~DMA_RESUME
1306 );
Andreas Mohrca54bde2006-05-17 11:02:24 +02001307 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001308 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
Takashi Iwai99b359b2005-10-20 18:26:44 +02001309 snd_printk(KERN_ERR "FIXME: SNDRV_PCM_TRIGGER_PAUSE_PUSH NIY!\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001310 break;
1311 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
Takashi Iwai99b359b2005-10-20 18:26:44 +02001312 snd_printk(KERN_ERR "FIXME: SNDRV_PCM_TRIGGER_PAUSE_RELEASE NIY!\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001313 break;
1314 default:
Andreas Mohrca54bde2006-05-17 11:02:24 +02001315 printk(KERN_ERR "FIXME: unknown trigger mode!\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001316 return -EINVAL;
1317 }
Andreas Mohr02330fb2008-05-16 12:18:29 +02001318
Linus Torvalds1da177e2005-04-16 15:20:36 -07001319 snd_azf3328_dbgcallleave();
1320 return result;
1321}
1322
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001323static int
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001324snd_azf3328_codec_playback_trigger(struct snd_pcm_substream *substream, int cmd)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001325{
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001326 return snd_azf3328_codec_trigger(AZF_CODEC_PLAYBACK, substream, cmd);
1327}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001328
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001329static int
1330snd_azf3328_codec_capture_trigger(struct snd_pcm_substream *substream, int cmd)
1331{
1332 return snd_azf3328_codec_trigger(AZF_CODEC_CAPTURE, substream, cmd);
1333}
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001334
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001335static int
1336snd_azf3328_codec_i2s_out_trigger(struct snd_pcm_substream *substream, int cmd)
1337{
1338 return snd_azf3328_codec_trigger(AZF_CODEC_I2S_OUT, substream, cmd);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001339}
1340
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001341static snd_pcm_uframes_t
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001342snd_azf3328_codec_pointer(struct snd_pcm_substream *substream,
1343 enum snd_azf3328_codec_type codec_type
1344)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001345{
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001346 const struct snd_azf3328 *chip = snd_pcm_substream_chip(substream);
1347 const struct snd_azf3328_codec_data *codec = &chip->codecs[codec_type];
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001348 unsigned long bufptr, result;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001349 snd_pcm_uframes_t frmres;
1350
1351#ifdef QUERY_HARDWARE
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001352 bufptr = snd_azf3328_codec_inl(codec, IDX_IO_CODEC_DMA_START_1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001353#else
1354 bufptr = substream->runtime->dma_addr;
1355#endif
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001356 result = snd_azf3328_codec_inl(codec, IDX_IO_CODEC_DMA_CURRPOS);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001357
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001358 /* calculate offset */
1359 result -= bufptr;
1360 frmres = bytes_to_frames( substream->runtime, result);
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001361 snd_azf3328_dbgplay("%s @ 0x%8lx, frames %8ld\n",
1362 codec->name, result, frmres);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001363 return frmres;
1364}
1365
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001366static snd_pcm_uframes_t
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001367snd_azf3328_codec_playback_pointer(struct snd_pcm_substream *substream)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001368{
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001369 return snd_azf3328_codec_pointer(substream, AZF_CODEC_PLAYBACK);
1370}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001371
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001372static snd_pcm_uframes_t
1373snd_azf3328_codec_capture_pointer(struct snd_pcm_substream *substream)
1374{
1375 return snd_azf3328_codec_pointer(substream, AZF_CODEC_CAPTURE);
1376}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001377
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001378static snd_pcm_uframes_t
1379snd_azf3328_codec_i2s_out_pointer(struct snd_pcm_substream *substream)
1380{
1381 return snd_azf3328_codec_pointer(substream, AZF_CODEC_I2S_OUT);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001382}
1383
Andreas Mohr02330fb2008-05-16 12:18:29 +02001384/******************************************************************/
1385
1386#ifdef SUPPORT_GAMEPORT
1387static inline void
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001388snd_azf3328_gameport_irq_enable(struct snd_azf3328 *chip,
1389 bool enable
1390)
Andreas Mohr02330fb2008-05-16 12:18:29 +02001391{
1392 snd_azf3328_io_reg_setb(
1393 chip->game_io+IDX_GAME_HWCONFIG,
1394 GAME_HWCFG_IRQ_ENABLE,
1395 enable
1396 );
1397}
1398
1399static inline void
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001400snd_azf3328_gameport_legacy_address_enable(struct snd_azf3328 *chip,
1401 bool enable
1402)
Andreas Mohr02330fb2008-05-16 12:18:29 +02001403{
1404 snd_azf3328_io_reg_setb(
1405 chip->game_io+IDX_GAME_HWCONFIG,
1406 GAME_HWCFG_LEGACY_ADDRESS_ENABLE,
1407 enable
1408 );
1409}
1410
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001411static void
1412snd_azf3328_gameport_set_counter_frequency(struct snd_azf3328 *chip,
1413 unsigned int freq_cfg
1414)
Andreas Mohr02330fb2008-05-16 12:18:29 +02001415{
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001416 snd_azf3328_io_reg_setb(
1417 chip->game_io+IDX_GAME_HWCONFIG,
1418 0x02,
1419 (freq_cfg & 1) != 0
1420 );
1421 snd_azf3328_io_reg_setb(
1422 chip->game_io+IDX_GAME_HWCONFIG,
1423 0x04,
1424 (freq_cfg & 2) != 0
1425 );
1426}
1427
1428static inline void
1429snd_azf3328_gameport_axis_circuit_enable(struct snd_azf3328 *chip, bool enable)
1430{
1431 snd_azf3328_ctrl_reg_6AH_update(
Andreas Mohr627d3e72008-06-23 11:50:47 +02001432 chip, IO_6A_SOMETHING2_GAMEPORT, enable
Andreas Mohr02330fb2008-05-16 12:18:29 +02001433 );
1434}
1435
1436static inline void
1437snd_azf3328_gameport_interrupt(struct snd_azf3328 *chip)
1438{
1439 /*
1440 * skeleton handler only
1441 * (we do not want axis reading in interrupt handler - too much load!)
1442 */
1443 snd_azf3328_dbggame("gameport irq\n");
1444
1445 /* this should ACK the gameport IRQ properly, hopefully. */
1446 snd_azf3328_game_inw(chip, IDX_GAME_AXIS_VALUE);
1447}
1448
1449static int
1450snd_azf3328_gameport_open(struct gameport *gameport, int mode)
1451{
1452 struct snd_azf3328 *chip = gameport_get_port_data(gameport);
1453 int res;
1454
1455 snd_azf3328_dbggame("gameport_open, mode %d\n", mode);
1456 switch (mode) {
1457 case GAMEPORT_MODE_COOKED:
1458 case GAMEPORT_MODE_RAW:
1459 res = 0;
1460 break;
1461 default:
1462 res = -1;
1463 break;
1464 }
1465
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001466 snd_azf3328_gameport_set_counter_frequency(chip,
1467 GAME_HWCFG_ADC_COUNTER_FREQ_STD);
Andreas Mohr02330fb2008-05-16 12:18:29 +02001468 snd_azf3328_gameport_axis_circuit_enable(chip, (res == 0));
1469
1470 return res;
1471}
1472
1473static void
1474snd_azf3328_gameport_close(struct gameport *gameport)
1475{
1476 struct snd_azf3328 *chip = gameport_get_port_data(gameport);
1477
1478 snd_azf3328_dbggame("gameport_close\n");
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001479 snd_azf3328_gameport_set_counter_frequency(chip,
1480 GAME_HWCFG_ADC_COUNTER_FREQ_1_200);
Andreas Mohr02330fb2008-05-16 12:18:29 +02001481 snd_azf3328_gameport_axis_circuit_enable(chip, 0);
1482}
1483
1484static int
1485snd_azf3328_gameport_cooked_read(struct gameport *gameport,
1486 int *axes,
1487 int *buttons
1488)
1489{
1490 struct snd_azf3328 *chip = gameport_get_port_data(gameport);
1491 int i;
1492 u8 val;
1493 unsigned long flags;
1494
Takashi Iwaida3cec32008-08-08 17:12:14 +02001495 if (snd_BUG_ON(!chip))
1496 return 0;
Andreas Mohr02330fb2008-05-16 12:18:29 +02001497
1498 spin_lock_irqsave(&chip->reg_lock, flags);
1499 val = snd_azf3328_game_inb(chip, IDX_GAME_LEGACY_COMPATIBLE);
1500 *buttons = (~(val) >> 4) & 0xf;
1501
1502 /* ok, this one is a bit dirty: cooked_read is being polled by a timer,
1503 * thus we're atomic and cannot actively wait in here
1504 * (which would be useful for us since it probably would be better
1505 * to trigger a measurement in here, then wait a short amount of
1506 * time until it's finished, then read values of _this_ measurement).
1507 *
1508 * Thus we simply resort to reading values if they're available already
1509 * and trigger the next measurement.
1510 */
1511
1512 val = snd_azf3328_game_inb(chip, IDX_GAME_AXES_CONFIG);
1513 if (val & GAME_AXES_SAMPLING_READY) {
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001514 for (i = 0; i < ARRAY_SIZE(chip->axes); ++i) {
Andreas Mohr02330fb2008-05-16 12:18:29 +02001515 /* configure the axis to read */
1516 val = (i << 4) | 0x0f;
1517 snd_azf3328_game_outb(chip, IDX_GAME_AXES_CONFIG, val);
1518
1519 chip->axes[i] = snd_azf3328_game_inw(
1520 chip, IDX_GAME_AXIS_VALUE
1521 );
1522 }
1523 }
1524
1525 /* trigger next axes sampling, to be evaluated the next time we
1526 * enter this function */
1527
1528 /* for some very, very strange reason we cannot enable
1529 * Measurement Ready monitoring for all axes here,
1530 * at least not when only one joystick connected */
1531 val = 0x03; /* we're able to monitor axes 1 and 2 only */
1532 snd_azf3328_game_outb(chip, IDX_GAME_AXES_CONFIG, val);
1533
1534 snd_azf3328_game_outw(chip, IDX_GAME_AXIS_VALUE, 0xffff);
1535 spin_unlock_irqrestore(&chip->reg_lock, flags);
1536
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001537 for (i = 0; i < ARRAY_SIZE(chip->axes); i++) {
Andreas Mohr02330fb2008-05-16 12:18:29 +02001538 axes[i] = chip->axes[i];
1539 if (axes[i] == 0xffff)
1540 axes[i] = -1;
1541 }
1542
1543 snd_azf3328_dbggame("cooked_read: axes %d %d %d %d buttons %d\n",
1544 axes[0], axes[1], axes[2], axes[3], *buttons
1545 );
1546
1547 return 0;
1548}
1549
1550static int __devinit
1551snd_azf3328_gameport(struct snd_azf3328 *chip, int dev)
1552{
1553 struct gameport *gp;
1554
Andreas Mohr02330fb2008-05-16 12:18:29 +02001555 chip->gameport = gp = gameport_allocate_port();
1556 if (!gp) {
1557 printk(KERN_ERR "azt3328: cannot alloc memory for gameport\n");
1558 return -ENOMEM;
1559 }
1560
1561 gameport_set_name(gp, "AZF3328 Gameport");
1562 gameport_set_phys(gp, "pci%s/gameport0", pci_name(chip->pci));
1563 gameport_set_dev_parent(gp, &chip->pci->dev);
Andreas Mohr627d3e72008-06-23 11:50:47 +02001564 gp->io = chip->game_io;
Andreas Mohr02330fb2008-05-16 12:18:29 +02001565 gameport_set_port_data(gp, chip);
1566
1567 gp->open = snd_azf3328_gameport_open;
1568 gp->close = snd_azf3328_gameport_close;
1569 gp->fuzz = 16; /* seems ok */
1570 gp->cooked_read = snd_azf3328_gameport_cooked_read;
1571
1572 /* DISABLE legacy address: we don't need it! */
1573 snd_azf3328_gameport_legacy_address_enable(chip, 0);
1574
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001575 snd_azf3328_gameport_set_counter_frequency(chip,
1576 GAME_HWCFG_ADC_COUNTER_FREQ_1_200);
Andreas Mohr02330fb2008-05-16 12:18:29 +02001577 snd_azf3328_gameport_axis_circuit_enable(chip, 0);
1578
1579 gameport_register_port(chip->gameport);
1580
1581 return 0;
1582}
1583
1584static void
1585snd_azf3328_gameport_free(struct snd_azf3328 *chip)
1586{
1587 if (chip->gameport) {
1588 gameport_unregister_port(chip->gameport);
1589 chip->gameport = NULL;
1590 }
1591 snd_azf3328_gameport_irq_enable(chip, 0);
1592}
1593#else
1594static inline int
1595snd_azf3328_gameport(struct snd_azf3328 *chip, int dev) { return -ENOSYS; }
1596static inline void
1597snd_azf3328_gameport_free(struct snd_azf3328 *chip) { }
1598static inline void
1599snd_azf3328_gameport_interrupt(struct snd_azf3328 *chip)
1600{
1601 printk(KERN_WARNING "huh, game port IRQ occurred!?\n");
1602}
1603#endif /* SUPPORT_GAMEPORT */
1604
1605/******************************************************************/
1606
Andreas Mohr627d3e72008-06-23 11:50:47 +02001607static inline void
1608snd_azf3328_irq_log_unknown_type(u8 which)
1609{
1610 snd_azf3328_dbgplay(
1611 "azt3328: unknown IRQ type (%x) occurred, please report!\n",
1612 which
1613 );
1614}
1615
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001616static inline void
1617snd_azf3328_codec_interrupt(struct snd_azf3328 *chip, u8 status)
1618{
1619 u8 which;
1620 enum snd_azf3328_codec_type codec_type;
1621 const struct snd_azf3328_codec_data *codec;
1622
1623 for (codec_type = AZF_CODEC_PLAYBACK;
1624 codec_type <= AZF_CODEC_I2S_OUT;
1625 ++codec_type) {
1626
1627 /* skip codec if there's no interrupt for it */
1628 if (!(status & (1 << codec_type)))
1629 continue;
1630
1631 codec = &chip->codecs[codec_type];
1632
1633 spin_lock(&chip->reg_lock);
1634 which = snd_azf3328_codec_inb(codec, IDX_IO_CODEC_IRQTYPE);
1635 /* ack all IRQ types immediately */
1636 snd_azf3328_codec_outb(codec, IDX_IO_CODEC_IRQTYPE, which);
1637 spin_unlock(&chip->reg_lock);
1638
1639 if ((chip->pcm[codec_type])
1640 && (chip->codecs[codec_type].substream)) {
1641 snd_pcm_period_elapsed(
1642 chip->codecs[codec_type].substream
1643 );
1644 snd_azf3328_dbgplay("%s period done (#%x), @ %x\n",
1645 codec->name,
1646 which,
1647 snd_azf3328_codec_inl(
1648 codec, IDX_IO_CODEC_DMA_CURRPOS
1649 )
1650 );
1651 } else
1652 printk(KERN_WARNING "azt3328: irq handler problem!\n");
1653 if (which & IRQ_SOMETHING)
1654 snd_azf3328_irq_log_unknown_type(which);
1655 }
1656}
1657
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001658static irqreturn_t
David Howells7d12e782006-10-05 14:55:46 +01001659snd_azf3328_interrupt(int irq, void *dev_id)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001660{
Takashi Iwai95de7762005-11-17 15:02:42 +01001661 struct snd_azf3328 *chip = dev_id;
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001662 u8 status;
Andreas Mohr02330fb2008-05-16 12:18:29 +02001663#if DEBUG_PLAY_REC
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001664 static unsigned long irq_count;
Andreas Mohr02330fb2008-05-16 12:18:29 +02001665#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001666
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001667 status = snd_azf3328_ctrl_inb(chip, IDX_IO_IRQSTATUS);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001668
1669 /* fast path out, to ease interrupt sharing */
Andreas Mohr02330fb2008-05-16 12:18:29 +02001670 if (!(status &
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001671 (IRQ_PLAYBACK|IRQ_RECORDING|IRQ_I2S_OUT
1672 |IRQ_GAMEPORT|IRQ_MPU401|IRQ_TIMER)
Andreas Mohr02330fb2008-05-16 12:18:29 +02001673 ))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001674 return IRQ_NONE; /* must be interrupt for another device */
1675
Andreas Mohr627d3e72008-06-23 11:50:47 +02001676 snd_azf3328_dbgplay(
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001677 "irq_count %ld! IDX_IO_IRQSTATUS %04x\n",
Andreas Mohr627d3e72008-06-23 11:50:47 +02001678 irq_count++ /* debug-only */,
Andreas Mohr627d3e72008-06-23 11:50:47 +02001679 status
1680 );
Andreas Mohr02330fb2008-05-16 12:18:29 +02001681
Andreas Mohre2f87262006-05-17 11:04:19 +02001682 if (status & IRQ_TIMER) {
Andreas Mohr02330fb2008-05-16 12:18:29 +02001683 /* snd_azf3328_dbgplay("timer %ld\n",
1684 snd_azf3328_codec_inl(chip, IDX_IO_TIMER_VALUE)
1685 & TIMER_VALUE_MASK
1686 ); */
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001687 if (chip->timer)
1688 snd_timer_interrupt(chip->timer, chip->timer->sticks);
1689 /* ACK timer */
1690 spin_lock(&chip->reg_lock);
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001691 snd_azf3328_ctrl_outb(chip, IDX_IO_TIMER_VALUE + 3, 0x07);
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001692 spin_unlock(&chip->reg_lock);
1693 snd_azf3328_dbgplay("azt3328: timer IRQ\n");
1694 }
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001695
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001696 if (status & (IRQ_PLAYBACK|IRQ_RECORDING|IRQ_I2S_OUT))
1697 snd_azf3328_codec_interrupt(chip, status);
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001698
Andreas Mohr02330fb2008-05-16 12:18:29 +02001699 if (status & IRQ_GAMEPORT)
1700 snd_azf3328_gameport_interrupt(chip);
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001701
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001702 /* MPU401 has less critical IRQ requirements
1703 * than timer and playback/recording, right? */
Andreas Mohre2f87262006-05-17 11:04:19 +02001704 if (status & IRQ_MPU401) {
David Howells7d12e782006-10-05 14:55:46 +01001705 snd_mpu401_uart_interrupt(irq, chip->rmidi->private_data);
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001706
1707 /* hmm, do we have to ack the IRQ here somehow?
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001708 * If so, then I don't know how yet... */
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001709 snd_azf3328_dbgplay("azt3328: MPU401 IRQ\n");
1710 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001711 return IRQ_HANDLED;
1712}
1713
1714/*****************************************************************/
1715
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001716/* as long as we think we have identical snd_pcm_hardware parameters
1717 for playback, capture and i2s out, we can use the same physical struct
1718 since the struct is simply being copied into a member.
1719*/
1720static const struct snd_pcm_hardware snd_azf3328_hardware =
Linus Torvalds1da177e2005-04-16 15:20:36 -07001721{
1722 /* FIXME!! Correct? */
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001723 .info = SNDRV_PCM_INFO_MMAP |
1724 SNDRV_PCM_INFO_INTERLEAVED |
1725 SNDRV_PCM_INFO_MMAP_VALID,
1726 .formats = SNDRV_PCM_FMTBIT_S8 |
1727 SNDRV_PCM_FMTBIT_U8 |
1728 SNDRV_PCM_FMTBIT_S16_LE |
1729 SNDRV_PCM_FMTBIT_U16_LE,
1730 .rates = SNDRV_PCM_RATE_5512 |
1731 SNDRV_PCM_RATE_8000_48000 |
1732 SNDRV_PCM_RATE_KNOT,
Andreas Mohr02330fb2008-05-16 12:18:29 +02001733 .rate_min = AZF_FREQ_4000,
1734 .rate_max = AZF_FREQ_66200,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001735 .channels_min = 1,
1736 .channels_max = 2,
1737 .buffer_bytes_max = 65536,
1738 .period_bytes_min = 64,
1739 .period_bytes_max = 65536,
1740 .periods_min = 1,
1741 .periods_max = 1024,
1742 /* FIXME: maybe that card actually has a FIFO?
1743 * Hmm, it seems newer revisions do have one, but we still don't know
1744 * its size... */
1745 .fifo_size = 0,
1746};
1747
Linus Torvalds1da177e2005-04-16 15:20:36 -07001748
1749static unsigned int snd_azf3328_fixed_rates[] = {
Andreas Mohr02330fb2008-05-16 12:18:29 +02001750 AZF_FREQ_4000,
1751 AZF_FREQ_4800,
1752 AZF_FREQ_5512,
1753 AZF_FREQ_6620,
1754 AZF_FREQ_8000,
1755 AZF_FREQ_9600,
1756 AZF_FREQ_11025,
1757 AZF_FREQ_13240,
1758 AZF_FREQ_16000,
1759 AZF_FREQ_22050,
1760 AZF_FREQ_32000,
1761 AZF_FREQ_44100,
1762 AZF_FREQ_48000,
1763 AZF_FREQ_66200
1764};
1765
Takashi Iwai95de7762005-11-17 15:02:42 +01001766static struct snd_pcm_hw_constraint_list snd_azf3328_hw_constraints_rates = {
Andreas Mohr02330fb2008-05-16 12:18:29 +02001767 .count = ARRAY_SIZE(snd_azf3328_fixed_rates),
Linus Torvalds1da177e2005-04-16 15:20:36 -07001768 .list = snd_azf3328_fixed_rates,
1769 .mask = 0,
1770};
1771
1772/*****************************************************************/
1773
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001774static int
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001775snd_azf3328_pcm_open(struct snd_pcm_substream *substream,
1776 enum snd_azf3328_codec_type codec_type
1777)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001778{
Takashi Iwai95de7762005-11-17 15:02:42 +01001779 struct snd_azf3328 *chip = snd_pcm_substream_chip(substream);
1780 struct snd_pcm_runtime *runtime = substream->runtime;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001781
1782 snd_azf3328_dbgcallenter();
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001783 chip->codecs[codec_type].substream = substream;
1784
1785 /* same parameters for all our codecs - at least we think so... */
1786 runtime->hw = snd_azf3328_hardware;
1787
Linus Torvalds1da177e2005-04-16 15:20:36 -07001788 snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
1789 &snd_azf3328_hw_constraints_rates);
1790 snd_azf3328_dbgcallleave();
1791 return 0;
1792}
1793
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001794static int
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001795snd_azf3328_playback_open(struct snd_pcm_substream *substream)
1796{
1797 return snd_azf3328_pcm_open(substream, AZF_CODEC_PLAYBACK);
1798}
1799
1800static int
Takashi Iwai95de7762005-11-17 15:02:42 +01001801snd_azf3328_capture_open(struct snd_pcm_substream *substream)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001802{
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001803 return snd_azf3328_pcm_open(substream, AZF_CODEC_CAPTURE);
1804}
1805
1806static int
1807snd_azf3328_i2s_out_open(struct snd_pcm_substream *substream)
1808{
1809 return snd_azf3328_pcm_open(substream, AZF_CODEC_I2S_OUT);
1810}
1811
1812static int
1813snd_azf3328_pcm_close(struct snd_pcm_substream *substream,
1814 enum snd_azf3328_codec_type codec_type
1815)
1816{
Takashi Iwai95de7762005-11-17 15:02:42 +01001817 struct snd_azf3328 *chip = snd_pcm_substream_chip(substream);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001818
1819 snd_azf3328_dbgcallenter();
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001820 chip->codecs[codec_type].substream = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001821 snd_azf3328_dbgcallleave();
1822 return 0;
1823}
1824
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001825static int
Takashi Iwai95de7762005-11-17 15:02:42 +01001826snd_azf3328_playback_close(struct snd_pcm_substream *substream)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001827{
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001828 return snd_azf3328_pcm_close(substream, AZF_CODEC_PLAYBACK);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001829}
1830
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001831static int
Takashi Iwai95de7762005-11-17 15:02:42 +01001832snd_azf3328_capture_close(struct snd_pcm_substream *substream)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001833{
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001834 return snd_azf3328_pcm_close(substream, AZF_CODEC_CAPTURE);
1835}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001836
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001837static int
1838snd_azf3328_i2s_out_close(struct snd_pcm_substream *substream)
1839{
1840 return snd_azf3328_pcm_close(substream, AZF_CODEC_I2S_OUT);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001841}
1842
1843/******************************************************************/
1844
Takashi Iwai95de7762005-11-17 15:02:42 +01001845static struct snd_pcm_ops snd_azf3328_playback_ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001846 .open = snd_azf3328_playback_open,
1847 .close = snd_azf3328_playback_close,
1848 .ioctl = snd_pcm_lib_ioctl,
1849 .hw_params = snd_azf3328_hw_params,
1850 .hw_free = snd_azf3328_hw_free,
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001851 .prepare = snd_azf3328_codec_prepare,
1852 .trigger = snd_azf3328_codec_playback_trigger,
1853 .pointer = snd_azf3328_codec_playback_pointer
Linus Torvalds1da177e2005-04-16 15:20:36 -07001854};
1855
Takashi Iwai95de7762005-11-17 15:02:42 +01001856static struct snd_pcm_ops snd_azf3328_capture_ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001857 .open = snd_azf3328_capture_open,
1858 .close = snd_azf3328_capture_close,
1859 .ioctl = snd_pcm_lib_ioctl,
1860 .hw_params = snd_azf3328_hw_params,
1861 .hw_free = snd_azf3328_hw_free,
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001862 .prepare = snd_azf3328_codec_prepare,
1863 .trigger = snd_azf3328_codec_capture_trigger,
1864 .pointer = snd_azf3328_codec_capture_pointer
1865};
1866
1867static struct snd_pcm_ops snd_azf3328_i2s_out_ops = {
1868 .open = snd_azf3328_i2s_out_open,
1869 .close = snd_azf3328_i2s_out_close,
1870 .ioctl = snd_pcm_lib_ioctl,
1871 .hw_params = snd_azf3328_hw_params,
1872 .hw_free = snd_azf3328_hw_free,
1873 .prepare = snd_azf3328_codec_prepare,
1874 .trigger = snd_azf3328_codec_i2s_out_trigger,
1875 .pointer = snd_azf3328_codec_i2s_out_pointer
Linus Torvalds1da177e2005-04-16 15:20:36 -07001876};
1877
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001878static int __devinit
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001879snd_azf3328_pcm(struct snd_azf3328 *chip)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001880{
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001881enum { AZF_PCMDEV_STD, AZF_PCMDEV_I2S_OUT, NUM_AZF_PCMDEVS }; /* pcm devices */
1882
Takashi Iwai95de7762005-11-17 15:02:42 +01001883 struct snd_pcm *pcm;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001884 int err;
1885
1886 snd_azf3328_dbgcallenter();
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001887
1888 err = snd_pcm_new(chip->card, "AZF3328 DSP", AZF_PCMDEV_STD,
1889 1, 1, &pcm);
1890 if (err < 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001891 return err;
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001892 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
1893 &snd_azf3328_playback_ops);
1894 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
1895 &snd_azf3328_capture_ops);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001896
1897 pcm->private_data = chip;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001898 pcm->info_flags = 0;
1899 strcpy(pcm->name, chip->card->shortname);
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001900 /* same pcm object for playback/capture (see snd_pcm_new() above) */
1901 chip->pcm[AZF_CODEC_PLAYBACK] = pcm;
1902 chip->pcm[AZF_CODEC_CAPTURE] = pcm;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001903
1904 snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001905 snd_dma_pci_data(chip->pci),
1906 64*1024, 64*1024);
1907
1908 err = snd_pcm_new(chip->card, "AZF3328 I2S OUT", AZF_PCMDEV_I2S_OUT,
1909 1, 0, &pcm);
1910 if (err < 0)
1911 return err;
1912 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
1913 &snd_azf3328_i2s_out_ops);
1914
1915 pcm->private_data = chip;
1916 pcm->info_flags = 0;
1917 strcpy(pcm->name, chip->card->shortname);
1918 chip->pcm[AZF_CODEC_I2S_OUT] = pcm;
1919
1920 snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
1921 snd_dma_pci_data(chip->pci),
1922 64*1024, 64*1024);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001923
1924 snd_azf3328_dbgcallleave();
1925 return 0;
1926}
1927
1928/******************************************************************/
1929
Andreas Mohr02330fb2008-05-16 12:18:29 +02001930/*** NOTE: the physical timer resolution actually is 1024000 ticks per second
1931 *** (probably derived from main crystal via a divider of 24),
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001932 *** but announcing those attributes to user-space would make programs
1933 *** configure the timer to a 1 tick value, resulting in an absolutely fatal
1934 *** timer IRQ storm.
1935 *** Thus I chose to announce a down-scaled virtual timer to the outside and
1936 *** calculate real timer countdown values internally.
1937 *** (the scale factor can be set via module parameter "seqtimer_scaling").
1938 ***/
1939
1940static int
Takashi Iwai95de7762005-11-17 15:02:42 +01001941snd_azf3328_timer_start(struct snd_timer *timer)
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001942{
Takashi Iwai95de7762005-11-17 15:02:42 +01001943 struct snd_azf3328 *chip;
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001944 unsigned long flags;
1945 unsigned int delay;
1946
1947 snd_azf3328_dbgcallenter();
1948 chip = snd_timer_chip(timer);
1949 delay = ((timer->sticks * seqtimer_scaling) - 1) & TIMER_VALUE_MASK;
Andreas Mohre2f87262006-05-17 11:04:19 +02001950 if (delay < 49) {
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001951 /* uhoh, that's not good, since user-space won't know about
1952 * this timing tweak
1953 * (we need to do it to avoid a lockup, though) */
1954
1955 snd_azf3328_dbgtimer("delay was too low (%d)!\n", delay);
1956 delay = 49; /* minimum time is 49 ticks */
1957 }
1958 snd_azf3328_dbgtimer("setting timer countdown value %d, add COUNTDOWN|IRQ\n", delay);
Andreas Mohr02330fb2008-05-16 12:18:29 +02001959 delay |= TIMER_COUNTDOWN_ENABLE | TIMER_IRQ_ENABLE;
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001960 spin_lock_irqsave(&chip->reg_lock, flags);
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001961 snd_azf3328_ctrl_outl(chip, IDX_IO_TIMER_VALUE, delay);
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001962 spin_unlock_irqrestore(&chip->reg_lock, flags);
1963 snd_azf3328_dbgcallleave();
1964 return 0;
1965}
1966
1967static int
Takashi Iwai95de7762005-11-17 15:02:42 +01001968snd_azf3328_timer_stop(struct snd_timer *timer)
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001969{
Takashi Iwai95de7762005-11-17 15:02:42 +01001970 struct snd_azf3328 *chip;
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001971 unsigned long flags;
1972
1973 snd_azf3328_dbgcallenter();
1974 chip = snd_timer_chip(timer);
1975 spin_lock_irqsave(&chip->reg_lock, flags);
1976 /* disable timer countdown and interrupt */
Andreas Mohr02330fb2008-05-16 12:18:29 +02001977 /* FIXME: should we write TIMER_IRQ_ACK here? */
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001978 snd_azf3328_ctrl_outb(chip, IDX_IO_TIMER_VALUE + 3, 0);
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001979 spin_unlock_irqrestore(&chip->reg_lock, flags);
1980 snd_azf3328_dbgcallleave();
1981 return 0;
1982}
1983
1984
1985static int
Takashi Iwai95de7762005-11-17 15:02:42 +01001986snd_azf3328_timer_precise_resolution(struct snd_timer *timer,
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001987 unsigned long *num, unsigned long *den)
1988{
1989 snd_azf3328_dbgcallenter();
1990 *num = 1;
1991 *den = 1024000 / seqtimer_scaling;
1992 snd_azf3328_dbgcallleave();
1993 return 0;
1994}
1995
Takashi Iwai95de7762005-11-17 15:02:42 +01001996static struct snd_timer_hardware snd_azf3328_timer_hw = {
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001997 .flags = SNDRV_TIMER_HW_AUTO,
1998 .resolution = 977, /* 1000000/1024000 = 0.9765625us */
1999 .ticks = 1024000, /* max tick count, defined by the value register; actually it's not 1024000, but 1048576, but we don't care */
2000 .start = snd_azf3328_timer_start,
2001 .stop = snd_azf3328_timer_stop,
2002 .precise_resolution = snd_azf3328_timer_precise_resolution,
2003};
2004
2005static int __devinit
Takashi Iwai95de7762005-11-17 15:02:42 +01002006snd_azf3328_timer(struct snd_azf3328 *chip, int device)
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002007{
Takashi Iwai95de7762005-11-17 15:02:42 +01002008 struct snd_timer *timer = NULL;
2009 struct snd_timer_id tid;
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002010 int err;
2011
2012 snd_azf3328_dbgcallenter();
2013 tid.dev_class = SNDRV_TIMER_CLASS_CARD;
2014 tid.dev_sclass = SNDRV_TIMER_SCLASS_NONE;
2015 tid.card = chip->card->number;
2016 tid.device = device;
2017 tid.subdevice = 0;
2018
2019 snd_azf3328_timer_hw.resolution *= seqtimer_scaling;
2020 snd_azf3328_timer_hw.ticks /= seqtimer_scaling;
Andreas Mohr02330fb2008-05-16 12:18:29 +02002021
2022 err = snd_timer_new(chip->card, "AZF3328", &tid, &timer);
2023 if (err < 0)
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002024 goto out;
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002025
2026 strcpy(timer->name, "AZF3328 timer");
2027 timer->private_data = chip;
2028 timer->hw = snd_azf3328_timer_hw;
2029
2030 chip->timer = timer;
2031
Andreas Mohr02330fb2008-05-16 12:18:29 +02002032 snd_azf3328_timer_stop(timer);
2033
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002034 err = 0;
2035
2036out:
2037 snd_azf3328_dbgcallleave();
2038 return err;
2039}
2040
2041/******************************************************************/
2042
Andreas Mohr02330fb2008-05-16 12:18:29 +02002043static int
2044snd_azf3328_free(struct snd_azf3328 *chip)
2045{
2046 if (chip->irq < 0)
2047 goto __end_hw;
2048
2049 /* reset (close) mixer:
2050 * first mute master volume, then reset
2051 */
2052 snd_azf3328_mixer_set_mute(chip, IDX_MIXER_PLAY_MASTER, 1);
2053 snd_azf3328_mixer_outw(chip, IDX_MIXER_RESET, 0x0000);
2054
2055 snd_azf3328_timer_stop(chip->timer);
2056 snd_azf3328_gameport_free(chip);
2057
2058 if (chip->irq >= 0)
2059 synchronize_irq(chip->irq);
2060__end_hw:
2061 if (chip->irq >= 0)
2062 free_irq(chip->irq, chip);
2063 pci_release_regions(chip->pci);
2064 pci_disable_device(chip->pci);
2065
2066 kfree(chip);
2067 return 0;
2068}
2069
2070static int
2071snd_azf3328_dev_free(struct snd_device *device)
2072{
2073 struct snd_azf3328 *chip = device->device_data;
2074 return snd_azf3328_free(chip);
2075}
2076
Linus Torvalds1da177e2005-04-16 15:20:36 -07002077#if 0
2078/* check whether a bit can be modified */
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002079static void
Andreas Mohr02330fb2008-05-16 12:18:29 +02002080snd_azf3328_test_bit(unsigned unsigned reg, int bit)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002081{
2082 unsigned char val, valoff, valon;
2083
2084 val = inb(reg);
2085
2086 outb(val & ~(1 << bit), reg);
2087 valoff = inb(reg);
2088
2089 outb(val|(1 << bit), reg);
2090 valon = inb(reg);
Andreas Mohr02330fb2008-05-16 12:18:29 +02002091
Linus Torvalds1da177e2005-04-16 15:20:36 -07002092 outb(val, reg);
2093
Andreas Mohr02330fb2008-05-16 12:18:29 +02002094 printk(KERN_ERR "reg %04x bit %d: %02x %02x %02x\n",
2095 reg, bit, val, valoff, valon
2096 );
Linus Torvalds1da177e2005-04-16 15:20:36 -07002097}
2098#endif
2099
Andreas Mohr02330fb2008-05-16 12:18:29 +02002100static inline void
Takashi Iwai95de7762005-11-17 15:02:42 +01002101snd_azf3328_debug_show_ports(const struct snd_azf3328 *chip)
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002102{
Andreas Mohr02330fb2008-05-16 12:18:29 +02002103#if DEBUG_MISC
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002104 u16 tmp;
2105
Andreas Mohr02330fb2008-05-16 12:18:29 +02002106 snd_azf3328_dbgmisc(
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002107 "ctrl_io 0x%lx, game_io 0x%lx, mpu_io 0x%lx, "
Andreas Mohr02330fb2008-05-16 12:18:29 +02002108 "opl3_io 0x%lx, mixer_io 0x%lx, irq %d\n",
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002109 chip->ctrl_io, chip->game_io, chip->mpu_io,
Andreas Mohr02330fb2008-05-16 12:18:29 +02002110 chip->opl3_io, chip->mixer_io, chip->irq
2111 );
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002112
Andreas Mohr02330fb2008-05-16 12:18:29 +02002113 snd_azf3328_dbgmisc("game %02x %02x %02x %02x %02x %02x\n",
2114 snd_azf3328_game_inb(chip, 0),
2115 snd_azf3328_game_inb(chip, 1),
2116 snd_azf3328_game_inb(chip, 2),
2117 snd_azf3328_game_inb(chip, 3),
2118 snd_azf3328_game_inb(chip, 4),
2119 snd_azf3328_game_inb(chip, 5)
2120 );
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002121
Andreas Mohr02330fb2008-05-16 12:18:29 +02002122 for (tmp = 0; tmp < 0x07; tmp += 1)
2123 snd_azf3328_dbgmisc("mpu_io 0x%04x\n", inb(chip->mpu_io + tmp));
2124
2125 for (tmp = 0; tmp <= 0x07; tmp += 1)
2126 snd_azf3328_dbgmisc("0x%02x: game200 0x%04x, game208 0x%04x\n",
2127 tmp, inb(0x200 + tmp), inb(0x208 + tmp));
2128
2129 for (tmp = 0; tmp <= 0x01; tmp += 1)
2130 snd_azf3328_dbgmisc(
2131 "0x%02x: mpu300 0x%04x, mpu310 0x%04x, mpu320 0x%04x, "
2132 "mpu330 0x%04x opl388 0x%04x opl38c 0x%04x\n",
2133 tmp,
2134 inb(0x300 + tmp),
2135 inb(0x310 + tmp),
2136 inb(0x320 + tmp),
2137 inb(0x330 + tmp),
2138 inb(0x388 + tmp),
2139 inb(0x38c + tmp)
2140 );
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002141
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002142 for (tmp = 0; tmp < AZF_IO_SIZE_CTRL; tmp += 2)
2143 snd_azf3328_dbgmisc("ctrl 0x%02x: 0x%04x\n",
2144 tmp, snd_azf3328_ctrl_inw(chip, tmp)
Andreas Mohr02330fb2008-05-16 12:18:29 +02002145 );
Andreas Mohre24a1212007-03-26 12:49:45 +02002146
2147 for (tmp = 0; tmp < AZF_IO_SIZE_MIXER; tmp += 2)
Andreas Mohr02330fb2008-05-16 12:18:29 +02002148 snd_azf3328_dbgmisc("mixer 0x%02x: 0x%04x\n",
2149 tmp, snd_azf3328_mixer_inw(chip, tmp)
2150 );
2151#endif /* DEBUG_MISC */
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002152}
2153
2154static int __devinit
Takashi Iwai95de7762005-11-17 15:02:42 +01002155snd_azf3328_create(struct snd_card *card,
Andreas Mohr02330fb2008-05-16 12:18:29 +02002156 struct pci_dev *pci,
2157 unsigned long device_type,
2158 struct snd_azf3328 **rchip)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002159{
Takashi Iwai95de7762005-11-17 15:02:42 +01002160 struct snd_azf3328 *chip;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002161 int err;
Takashi Iwai95de7762005-11-17 15:02:42 +01002162 static struct snd_device_ops ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002163 .dev_free = snd_azf3328_dev_free,
2164 };
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002165 u8 dma_init;
2166 enum snd_azf3328_codec_type codec_type;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002167
2168 *rchip = NULL;
2169
Andreas Mohr02330fb2008-05-16 12:18:29 +02002170 err = pci_enable_device(pci);
2171 if (err < 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002172 return err;
2173
Takashi Iwaie560d8d2005-09-09 14:21:46 +02002174 chip = kzalloc(sizeof(*chip), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002175 if (chip == NULL) {
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002176 err = -ENOMEM;
2177 goto out_err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002178 }
2179 spin_lock_init(&chip->reg_lock);
2180 chip->card = card;
2181 chip->pci = pci;
2182 chip->irq = -1;
2183
2184 /* check if we can restrict PCI DMA transfers to 24 bits */
Yang Hongyang2f4f27d2009-04-06 19:01:18 -07002185 if (pci_set_dma_mask(pci, DMA_BIT_MASK(24)) < 0 ||
2186 pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(24)) < 0) {
Andreas Mohr02330fb2008-05-16 12:18:29 +02002187 snd_printk(KERN_ERR "architecture does not support "
2188 "24bit PCI busmaster DMA\n"
2189 );
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002190 err = -ENXIO;
2191 goto out_err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002192 }
2193
Andreas Mohr02330fb2008-05-16 12:18:29 +02002194 err = pci_request_regions(pci, "Aztech AZF3328");
2195 if (err < 0)
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002196 goto out_err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002197
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002198 chip->ctrl_io = pci_resource_start(pci, 0);
Andreas Mohr02330fb2008-05-16 12:18:29 +02002199 chip->game_io = pci_resource_start(pci, 1);
2200 chip->mpu_io = pci_resource_start(pci, 2);
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002201 chip->opl3_io = pci_resource_start(pci, 3);
Andreas Mohr02330fb2008-05-16 12:18:29 +02002202 chip->mixer_io = pci_resource_start(pci, 4);
2203
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002204 chip->codecs[AZF_CODEC_PLAYBACK].io_base =
2205 chip->ctrl_io + AZF_IO_OFFS_CODEC_PLAYBACK;
2206 chip->codecs[AZF_CODEC_PLAYBACK].name = "PLAYBACK";
2207 chip->codecs[AZF_CODEC_CAPTURE].io_base =
2208 chip->ctrl_io + AZF_IO_OFFS_CODEC_CAPTURE;
2209 chip->codecs[AZF_CODEC_CAPTURE].name = "CAPTURE";
2210 chip->codecs[AZF_CODEC_I2S_OUT].io_base =
2211 chip->ctrl_io + AZF_IO_OFFS_CODEC_I2S_OUT;
2212 chip->codecs[AZF_CODEC_I2S_OUT].name = "I2S_OUT";
Linus Torvalds1da177e2005-04-16 15:20:36 -07002213
Takashi Iwai437a5a42006-11-21 12:14:23 +01002214 if (request_irq(pci->irq, snd_azf3328_interrupt,
2215 IRQF_SHARED, card->shortname, chip)) {
Takashi Iwai99b359b2005-10-20 18:26:44 +02002216 snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002217 err = -EBUSY;
2218 goto out_err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002219 }
2220 chip->irq = pci->irq;
2221 pci_set_master(pci);
2222 synchronize_irq(chip->irq);
2223
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002224 snd_azf3328_debug_show_ports(chip);
Andreas Mohr02330fb2008-05-16 12:18:29 +02002225
2226 err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
2227 if (err < 0)
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002228 goto out_err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002229
2230 /* create mixer interface & switches */
Andreas Mohr02330fb2008-05-16 12:18:29 +02002231 err = snd_azf3328_mixer_new(chip);
2232 if (err < 0)
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002233 goto out_err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002234
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002235 /* standard codec init stuff */
2236 /* default DMA init value */
2237 dma_init = DMA_RUN_SOMETHING2|DMA_EPILOGUE_SOMETHING|DMA_SOMETHING_ELSE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002238
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002239 for (codec_type = AZF_CODEC_PLAYBACK;
2240 codec_type <= AZF_CODEC_I2S_OUT; ++codec_type) {
2241 struct snd_azf3328_codec_data *codec =
2242 &chip->codecs[codec_type];
Linus Torvalds1da177e2005-04-16 15:20:36 -07002243
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002244 /* shutdown codecs to save power */
2245 /* have ...ctrl_codec_activity() act properly */
2246 codec->running = 1;
2247 snd_azf3328_ctrl_codec_activity(chip, codec_type, 0);
2248
2249 spin_lock_irq(&chip->reg_lock);
2250 snd_azf3328_codec_outb(codec, IDX_IO_CODEC_DMA_FLAGS,
2251 dma_init);
2252 spin_unlock_irq(&chip->reg_lock);
2253 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002254
2255 snd_card_set_dev(card, &pci->dev);
2256
2257 *rchip = chip;
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002258
2259 err = 0;
2260 goto out;
2261
2262out_err:
2263 if (chip)
2264 snd_azf3328_free(chip);
2265 pci_disable_device(pci);
2266
2267out:
2268 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002269}
2270
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002271static int __devinit
2272snd_azf3328_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002273{
2274 static int dev;
Takashi Iwai95de7762005-11-17 15:02:42 +01002275 struct snd_card *card;
2276 struct snd_azf3328 *chip;
2277 struct snd_opl3 *opl3;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002278 int err;
2279
2280 snd_azf3328_dbgcallenter();
2281 if (dev >= SNDRV_CARDS)
2282 return -ENODEV;
2283 if (!enable[dev]) {
2284 dev++;
2285 return -ENOENT;
2286 }
2287
Takashi Iwaie58de7b2008-12-28 16:44:30 +01002288 err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
2289 if (err < 0)
2290 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002291
2292 strcpy(card->driver, "AZF3328");
2293 strcpy(card->shortname, "Aztech AZF3328 (PCI168)");
2294
Andreas Mohr02330fb2008-05-16 12:18:29 +02002295 err = snd_azf3328_create(card, pci, pci_id->driver_data, &chip);
2296 if (err < 0)
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002297 goto out_err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002298
Andreas Mohrca54bde2006-05-17 11:02:24 +02002299 card->private_data = chip;
2300
Andreas Mohr02330fb2008-05-16 12:18:29 +02002301 err = snd_mpu401_uart_new(
2302 card, 0, MPU401_HW_MPU401, chip->mpu_io, MPU401_INFO_INTEGRATED,
2303 pci->irq, 0, &chip->rmidi
2304 );
2305 if (err < 0) {
2306 snd_printk(KERN_ERR "azf3328: no MPU-401 device at 0x%lx?\n",
2307 chip->mpu_io
2308 );
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002309 goto out_err;
2310 }
2311
Andreas Mohr02330fb2008-05-16 12:18:29 +02002312 err = snd_azf3328_timer(chip, 0);
2313 if (err < 0)
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002314 goto out_err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002315
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002316 err = snd_azf3328_pcm(chip);
Andreas Mohr02330fb2008-05-16 12:18:29 +02002317 if (err < 0)
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002318 goto out_err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002319
Andreas Mohr02330fb2008-05-16 12:18:29 +02002320 if (snd_opl3_create(card, chip->opl3_io, chip->opl3_io+2,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002321 OPL3_HW_AUTO, 1, &opl3) < 0) {
Takashi Iwai99b359b2005-10-20 18:26:44 +02002322 snd_printk(KERN_ERR "azf3328: no OPL3 device at 0x%lx-0x%lx?\n",
Andreas Mohr02330fb2008-05-16 12:18:29 +02002323 chip->opl3_io, chip->opl3_io+2
2324 );
Linus Torvalds1da177e2005-04-16 15:20:36 -07002325 } else {
Andreas Mohr02330fb2008-05-16 12:18:29 +02002326 /* need to use IDs 1, 2 since ID 0 is snd_azf3328_timer above */
2327 err = snd_opl3_timer_new(opl3, 1, 2);
2328 if (err < 0)
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002329 goto out_err;
Andreas Mohr02330fb2008-05-16 12:18:29 +02002330 err = snd_opl3_hwdep_new(opl3, 0, 1, NULL);
2331 if (err < 0)
2332 goto out_err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002333 }
2334
Andreas Mohrca54bde2006-05-17 11:02:24 +02002335 opl3->private_data = chip;
2336
Linus Torvalds1da177e2005-04-16 15:20:36 -07002337 sprintf(card->longname, "%s at 0x%lx, irq %i",
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002338 card->shortname, chip->ctrl_io, chip->irq);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002339
Andreas Mohr02330fb2008-05-16 12:18:29 +02002340 err = snd_card_register(card);
2341 if (err < 0)
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002342 goto out_err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002343
2344#ifdef MODULE
2345 printk(
Andreas Mohre24a1212007-03-26 12:49:45 +02002346"azt3328: Sound driver for Aztech AZF3328-based soundcards such as PCI168.\n"
2347"azt3328: Hardware was completely undocumented, unfortunately.\n"
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002348"azt3328: Feel free to contact andi AT lisas.de for bug reports etc.!\n"
2349"azt3328: User-scalable sequencer timer set to %dHz (1024000Hz / %d).\n",
2350 1024000 / seqtimer_scaling, seqtimer_scaling);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002351#endif
2352
Andreas Mohr02330fb2008-05-16 12:18:29 +02002353 snd_azf3328_gameport(chip, dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002354
2355 pci_set_drvdata(pci, card);
2356 dev++;
2357
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002358 err = 0;
2359 goto out;
Andreas Mohr02330fb2008-05-16 12:18:29 +02002360
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002361out_err:
Andreas Mohr02330fb2008-05-16 12:18:29 +02002362 snd_printk(KERN_ERR "azf3328: something failed, exiting\n");
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002363 snd_card_free(card);
Andreas Mohr02330fb2008-05-16 12:18:29 +02002364
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002365out:
Linus Torvalds1da177e2005-04-16 15:20:36 -07002366 snd_azf3328_dbgcallleave();
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002367 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002368}
2369
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002370static void __devexit
2371snd_azf3328_remove(struct pci_dev *pci)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002372{
2373 snd_azf3328_dbgcallenter();
2374 snd_card_free(pci_get_drvdata(pci));
2375 pci_set_drvdata(pci, NULL);
2376 snd_azf3328_dbgcallleave();
2377}
2378
Andreas Mohrca54bde2006-05-17 11:02:24 +02002379#ifdef CONFIG_PM
2380static int
2381snd_azf3328_suspend(struct pci_dev *pci, pm_message_t state)
2382{
2383 struct snd_card *card = pci_get_drvdata(pci);
2384 struct snd_azf3328 *chip = card->private_data;
Andreas Mohr02330fb2008-05-16 12:18:29 +02002385 unsigned reg;
Andreas Mohrca54bde2006-05-17 11:02:24 +02002386
2387 snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
Andreas Mohr02330fb2008-05-16 12:18:29 +02002388
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002389 snd_pcm_suspend_all(chip->pcm[AZF_CODEC_PLAYBACK]);
2390 snd_pcm_suspend_all(chip->pcm[AZF_CODEC_I2S_OUT]);
Andreas Mohrca54bde2006-05-17 11:02:24 +02002391
Andreas Mohr02330fb2008-05-16 12:18:29 +02002392 for (reg = 0; reg < AZF_IO_SIZE_MIXER_PM / 2; ++reg)
2393 chip->saved_regs_mixer[reg] = inw(chip->mixer_io + reg * 2);
Andreas Mohrca54bde2006-05-17 11:02:24 +02002394
2395 /* make sure to disable master volume etc. to prevent looping sound */
2396 snd_azf3328_mixer_set_mute(chip, IDX_MIXER_PLAY_MASTER, 1);
2397 snd_azf3328_mixer_set_mute(chip, IDX_MIXER_WAVEOUT, 1);
Andreas Mohr02330fb2008-05-16 12:18:29 +02002398
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002399 for (reg = 0; reg < AZF_IO_SIZE_CTRL_PM / 2; ++reg)
2400 chip->saved_regs_ctrl[reg] = inw(chip->ctrl_io + reg * 2);
Andreas Mohr627d3e72008-06-23 11:50:47 +02002401
2402 /* manually store the one currently relevant write-only reg, too */
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002403 chip->saved_regs_ctrl[IDX_IO_6AH / 2] = chip->shadow_reg_ctrl_6AH;
Andreas Mohr627d3e72008-06-23 11:50:47 +02002404
Andreas Mohr02330fb2008-05-16 12:18:29 +02002405 for (reg = 0; reg < AZF_IO_SIZE_GAME_PM / 2; ++reg)
2406 chip->saved_regs_game[reg] = inw(chip->game_io + reg * 2);
2407 for (reg = 0; reg < AZF_IO_SIZE_MPU_PM / 2; ++reg)
2408 chip->saved_regs_mpu[reg] = inw(chip->mpu_io + reg * 2);
2409 for (reg = 0; reg < AZF_IO_SIZE_OPL3_PM / 2; ++reg)
2410 chip->saved_regs_opl3[reg] = inw(chip->opl3_io + reg * 2);
Andreas Mohrca54bde2006-05-17 11:02:24 +02002411
Andreas Mohrca54bde2006-05-17 11:02:24 +02002412 pci_disable_device(pci);
2413 pci_save_state(pci);
Takashi Iwai30b35392006-10-11 18:52:53 +02002414 pci_set_power_state(pci, pci_choose_state(pci, state));
Andreas Mohrca54bde2006-05-17 11:02:24 +02002415 return 0;
2416}
2417
2418static int
2419snd_azf3328_resume(struct pci_dev *pci)
2420{
2421 struct snd_card *card = pci_get_drvdata(pci);
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002422 const struct snd_azf3328 *chip = card->private_data;
Andreas Mohr02330fb2008-05-16 12:18:29 +02002423 unsigned reg;
Andreas Mohrca54bde2006-05-17 11:02:24 +02002424
Andreas Mohrca54bde2006-05-17 11:02:24 +02002425 pci_set_power_state(pci, PCI_D0);
Takashi Iwai30b35392006-10-11 18:52:53 +02002426 pci_restore_state(pci);
2427 if (pci_enable_device(pci) < 0) {
2428 printk(KERN_ERR "azt3328: pci_enable_device failed, "
2429 "disabling device\n");
2430 snd_card_disconnect(card);
2431 return -EIO;
2432 }
Andreas Mohrca54bde2006-05-17 11:02:24 +02002433 pci_set_master(pci);
2434
Andreas Mohr02330fb2008-05-16 12:18:29 +02002435 for (reg = 0; reg < AZF_IO_SIZE_GAME_PM / 2; ++reg)
2436 outw(chip->saved_regs_game[reg], chip->game_io + reg * 2);
2437 for (reg = 0; reg < AZF_IO_SIZE_MPU_PM / 2; ++reg)
2438 outw(chip->saved_regs_mpu[reg], chip->mpu_io + reg * 2);
2439 for (reg = 0; reg < AZF_IO_SIZE_OPL3_PM / 2; ++reg)
2440 outw(chip->saved_regs_opl3[reg], chip->opl3_io + reg * 2);
2441 for (reg = 0; reg < AZF_IO_SIZE_MIXER_PM / 2; ++reg)
2442 outw(chip->saved_regs_mixer[reg], chip->mixer_io + reg * 2);
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002443 for (reg = 0; reg < AZF_IO_SIZE_CTRL_PM / 2; ++reg)
2444 outw(chip->saved_regs_ctrl[reg], chip->ctrl_io + reg * 2);
Andreas Mohrca54bde2006-05-17 11:02:24 +02002445
2446 snd_power_change_state(card, SNDRV_CTL_POWER_D0);
2447 return 0;
2448}
Andreas Mohr02330fb2008-05-16 12:18:29 +02002449#endif /* CONFIG_PM */
Andreas Mohrca54bde2006-05-17 11:02:24 +02002450
2451
Linus Torvalds1da177e2005-04-16 15:20:36 -07002452static struct pci_driver driver = {
2453 .name = "AZF3328",
2454 .id_table = snd_azf3328_ids,
2455 .probe = snd_azf3328_probe,
2456 .remove = __devexit_p(snd_azf3328_remove),
Andreas Mohrca54bde2006-05-17 11:02:24 +02002457#ifdef CONFIG_PM
2458 .suspend = snd_azf3328_suspend,
2459 .resume = snd_azf3328_resume,
2460#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07002461};
2462
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002463static int __init
2464alsa_card_azf3328_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002465{
2466 int err;
2467 snd_azf3328_dbgcallenter();
Takashi Iwai01d25d42005-04-11 16:58:24 +02002468 err = pci_register_driver(&driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002469 snd_azf3328_dbgcallleave();
2470 return err;
2471}
2472
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002473static void __exit
2474alsa_card_azf3328_exit(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002475{
2476 snd_azf3328_dbgcallenter();
2477 pci_unregister_driver(&driver);
2478 snd_azf3328_dbgcallleave();
2479}
2480
2481module_init(alsa_card_azf3328_init)
2482module_exit(alsa_card_azf3328_exit)