blob: fdbb9c05c77b74274ee7c07e4056fc7fbc7944e6 [file] [log] [blame]
Andreas Mohrb5dc20c2011-02-19 00:49:32 +01001/* azt3328.c - driver for Aztech AZF3328 based soundcards (e.g. PCI168).
2 * Copyright (C) 2002, 2005 - 2011 by Andreas Mohr <andi AT lisas.de>
Linus Torvalds1da177e2005-04-16 15:20:36 -07003 *
4 * Framework borrowed from Bart Hartgers's als4000.c.
5 * Driver developed on PCI168 AP(W) version (PCI rev. 10, subsystem ID 1801),
6 * found in a Fujitsu-Siemens PC ("Cordant", aluminum case).
7 * Other versions are:
8 * PCI168 A(W), sub ID 1800
9 * PCI168 A/AP, sub ID 8000
10 * Please give me feedback in case you try my driver with one of these!!
11 *
Andreas Mohrdfbf9512009-07-05 13:55:46 +020012 * Keywords: Windows XP Vista 168nt4-125.zip 168win95-125.zip PCI 168 download
13 * (XP/Vista do not support this card at all but every Linux distribution
14 * has very good support out of the box;
15 * just to make sure that the right people hit this and get to know that,
16 * despite the high level of Internet ignorance - as usual :-P -
Andreas Mohr78df6172009-07-12 22:17:54 +020017 * about very good support for this card - on Linux!)
Andreas Mohrdfbf9512009-07-05 13:55:46 +020018 *
Linus Torvalds1da177e2005-04-16 15:20:36 -070019 * GPL LICENSE
20 * This program is free software; you can redistribute it and/or modify
21 * it under the terms of the GNU General Public License as published by
22 * the Free Software Foundation; either version 2 of the License, or
23 * (at your option) any later version.
24 *
25 * This program is distributed in the hope that it will be useful,
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 * GNU General Public License for more details.
29
30 * You should have received a copy of the GNU General Public License
31 * along with this program; if not, write to the Free Software
32 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
33 *
34 * NOTES
35 * Since Aztech does not provide any chipset documentation,
36 * even on repeated request to various addresses,
37 * and the answer that was finally given was negative
38 * (and I was stupid enough to manage to get hold of a PCI168 soundcard
39 * in the first place >:-P}),
40 * I was forced to base this driver on reverse engineering
41 * (3 weeks' worth of evenings filled with driver work).
Andreas Mohre2f87262006-05-17 11:04:19 +020042 * (and no, I did NOT go the easy way: to pick up a SB PCI128 for 9 Euros)
Linus Torvalds1da177e2005-04-16 15:20:36 -070043 *
Andreas Mohr02330fb2008-05-16 12:18:29 +020044 * It is quite likely that the AZF3328 chip is the PCI cousin of the
45 * AZF3318 ("azt1020 pnp", "MM Pro 16") ISA chip, given very similar specs.
Linus Torvalds1da177e2005-04-16 15:20:36 -070046 *
Andreas Mohr02330fb2008-05-16 12:18:29 +020047 * The AZF3328 chip (note: AZF3328, *not* AZT3328, that's just the driver name
48 * for compatibility reasons) from Azfin (joint-venture of Aztech and Fincitec,
49 * Fincitec acquired by National Semiconductor in 2002, together with the
50 * Fincitec-related company ARSmikro) has the following features:
51 *
52 * - compatibility & compliance:
53 * - Microsoft PC 97 ("PC 97 Hardware Design Guide",
54 * http://www.microsoft.com/whdc/archive/pcguides.mspx)
55 * - Microsoft PC 98 Baseline Audio
56 * - MPU401 UART
57 * - Sound Blaster Emulation (DOS Box)
Linus Torvalds1da177e2005-04-16 15:20:36 -070058 * - builtin AC97 conformant codec (SNR over 80dB)
Andreas Mohr13769e32006-05-17 11:03:16 +020059 * Note that "conformant" != "compliant"!! this chip's mixer register layout
60 * *differs* from the standard AC97 layout:
61 * they chose to not implement the headphone register (which is not a
62 * problem since it's merely optional), yet when doing this, they committed
63 * the grave sin of letting other registers follow immediately instead of
64 * keeping a headphone dummy register, thereby shifting the mixer register
65 * addresses illegally. So far unfortunately it looks like the very flexible
66 * ALSA AC97 support is still not enough to easily compensate for such a
67 * grave layout violation despite all tweaks and quirks mechanisms it offers.
Andreas Mohrb5dc20c2011-02-19 00:49:32 +010068 * Well, not quite: now ac97 layer is much improved (bus-specific ops!),
69 * thus I was able to implement support - it's actually working quite well.
70 * An interesting item might be Aztech AMR 2800-W, since it's an AC97
71 * modem card which might reveal the Aztech-specific codec ID which
72 * we might want to pretend, too. Dito PCI168's brother, PCI368,
73 * where the advertising datasheet says it's AC97-based and has a
74 * Digital Enhanced Game Port.
Andreas Mohr02330fb2008-05-16 12:18:29 +020075 * - builtin genuine OPL3 - verified to work fine, 20080506
Linus Torvalds1da177e2005-04-16 15:20:36 -070076 * - full duplex 16bit playback/record at independent sampling rate
Andreas Mohr02330fb2008-05-16 12:18:29 +020077 * - MPU401 (+ legacy address support, claimed by one official spec sheet)
78 * FIXME: how to enable legacy addr??
Linus Torvalds1da177e2005-04-16 15:20:36 -070079 * - game port (legacy address support)
Andreas Mohre24a1212007-03-26 12:49:45 +020080 * - builtin DirectInput support, helps reduce CPU overhead (interrupt-driven
Andreas Mohr02330fb2008-05-16 12:18:29 +020081 * features supported). - See common term "Digital Enhanced Game Port"...
82 * (probably DirectInput 3.0 spec - confirm)
83 * - builtin 3D enhancement (said to be YAMAHA Ymersion)
Linus Torvalds1da177e2005-04-16 15:20:36 -070084 * - built-in General DirectX timer having a 20 bits counter
Andreas Mohrd91c64c2005-10-25 11:17:45 +020085 * with 1us resolution (see below!)
Andreas Mohr02330fb2008-05-16 12:18:29 +020086 * - I2S serial output port for external DAC
Andreas Mohrdfbf9512009-07-05 13:55:46 +020087 * [FIXME: 3.3V or 5V level? maximum rate is 66.2kHz right?]
Linus Torvalds1da177e2005-04-16 15:20:36 -070088 * - supports 33MHz PCI spec 2.1, PCI power management 1.0, compliant with ACPI
89 * - supports hardware volume control
90 * - single chip low cost solution (128 pin QFP)
Andreas Mohrdfbf9512009-07-05 13:55:46 +020091 * - supports programmable Sub-vendor and Sub-system ID [24C02 SEEPROM chip]
Linus Torvalds1da177e2005-04-16 15:20:36 -070092 * required for Microsoft's logo compliance (FIXME: where?)
Andreas Mohr02330fb2008-05-16 12:18:29 +020093 * At least the Trident 4D Wave DX has one bit somewhere
94 * to enable writes to PCI subsystem VID registers, that should be it.
95 * This might easily be in extended PCI reg space, since PCI168 also has
96 * some custom data starting at 0x80. What kind of config settings
97 * are located in our extended PCI space anyway??
Linus Torvalds1da177e2005-04-16 15:20:36 -070098 * - PCI168 AP(W) card: power amplifier with 4 Watts/channel at 4 Ohms
Andreas Mohrdfbf9512009-07-05 13:55:46 +020099 * [TDA1517P chip]
Linus Torvalds1da177e2005-04-16 15:20:36 -0700100 *
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200101 * Note that this driver now is actually *better* than the Windows driver,
102 * since it additionally supports the card's 1MHz DirectX timer - just try
103 * the following snd-seq module parameters etc.:
104 * - options snd-seq seq_default_timer_class=2 seq_default_timer_sclass=0
105 * seq_default_timer_card=0 seq_client_load=1 seq_default_timer_device=0
106 * seq_default_timer_subdevice=0 seq_default_timer_resolution=1000000
107 * - "timidity -iAv -B2,8 -Os -EFreverb=0"
108 * - "pmidi -p 128:0 jazz.mid"
109 *
Andreas Mohr02330fb2008-05-16 12:18:29 +0200110 * OPL3 hardware playback testing, try something like:
111 * cat /proc/asound/hwdep
112 * and
113 * aconnect -o
114 * Then use
115 * sbiload -Dhw:x,y --opl3 /usr/share/sounds/opl3/std.o3 ......./drums.o3
116 * where x,y is the xx-yy number as given in hwdep.
117 * Then try
118 * pmidi -p a:b jazz.mid
119 * where a:b is the client number plus 0 usually, as given by aconnect above.
120 * Oh, and make sure to unmute the FM mixer control (doh!)
121 * NOTE: power use during OPL3 playback is _VERY_ high (70W --> 90W!)
122 * despite no CPU activity, possibly due to hindering ACPI idling somehow.
123 * Shouldn't be a problem of the AZF3328 chip itself, I'd hope.
124 * Higher PCM / FM mixer levels seem to conflict (causes crackling),
125 * at least sometimes. Maybe even use with hardware sequencer timer above :)
126 * adplay/adplug-utils might soon offer hardware-based OPL3 playback, too.
127 *
Linus Torvalds1da177e2005-04-16 15:20:36 -0700128 * Certain PCI versions of this card are susceptible to DMA traffic underruns
129 * in some systems (resulting in sound crackling/clicking/popping),
130 * probably because they don't have a DMA FIFO buffer or so.
131 * Overview (PCI ID/PCI subID/PCI rev.):
132 * - no DMA crackling on SiS735: 0x50DC/0x1801/16
133 * - unknown performance: 0x50DC/0x1801/10
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200134 * (well, it's not bad on an Athlon 1800 with now very optimized IRQ handler)
135 *
Linus Torvalds1da177e2005-04-16 15:20:36 -0700136 * Crackling happens with VIA chipsets or, in my case, an SiS735, which is
137 * supposed to be very fast and supposed to get rid of crackling much
138 * better than a VIA, yet ironically I still get crackling, like many other
139 * people with the same chipset.
140 * Possible remedies:
Andreas Mohr02330fb2008-05-16 12:18:29 +0200141 * - use speaker (amplifier) output instead of headphone output
142 * (in case crackling is due to overloaded output clipping)
Lucas De Marchi25985ed2011-03-30 22:57:33 -0300143 * - plug card into a different PCI slot, preferably one that isn't shared
Linus Torvalds1da177e2005-04-16 15:20:36 -0700144 * too much (this helps a lot, but not completely!)
145 * - get rid of PCI VGA card, use AGP instead
146 * - upgrade or downgrade BIOS
147 * - fiddle with PCI latency settings (setpci -v -s BUSID latency_timer=XX)
148 * Not too helpful.
149 * - Disable ACPI/power management/"Auto Detect RAM/PCI Clk" in BIOS
Andreas Mohr02330fb2008-05-16 12:18:29 +0200150 *
Linus Torvalds1da177e2005-04-16 15:20:36 -0700151 * BUGS
Andreas Mohr02330fb2008-05-16 12:18:29 +0200152 * - full-duplex might *still* be problematic, however a recent test was fine
Andreas Mohre24a1212007-03-26 12:49:45 +0200153 * - (non-bug) "Bass/Treble or 3D settings don't work" - they do get evaluated
154 * if you set PCM output switch to "pre 3D" instead of "post 3D".
155 * If this can't be set, then get a mixer application that Isn't Stupid (tm)
156 * (e.g. kmix, gamix) - unfortunately several are!!
Andreas Mohr02330fb2008-05-16 12:18:29 +0200157 * - locking is not entirely clean, especially the audio stream activity
158 * ints --> may be racy
159 * - an _unconnected_ secondary joystick at the gameport will be reported
160 * to be "active" (floating values, not precisely -1) due to the way we need
161 * to read the Digital Enhanced Game Port. Not sure whether it is fixable.
162 *
Linus Torvalds1da177e2005-04-16 15:20:36 -0700163 * TODO
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200164 * - use PCI_VDEVICE
165 * - verify driver status on x86_64
166 * - test multi-card driver operation
167 * - (ab)use 1MHz DirectX timer as kernel clocksource
Linus Torvalds1da177e2005-04-16 15:20:36 -0700168 * - test MPU401 MIDI playback etc.
Andreas Mohr02330fb2008-05-16 12:18:29 +0200169 * - add more power micro-management (disable various units of the card
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200170 * as long as they're unused, to improve audio quality and save power).
171 * However this requires more I/O ports which I haven't figured out yet
172 * and which thus might not even exist...
Andreas Mohrca54bde2006-05-17 11:02:24 +0200173 * The standard suspend/resume functionality could probably make use of
174 * some improvement, too...
Linus Torvalds1da177e2005-04-16 15:20:36 -0700175 * - figure out what all unknown port bits are responsible for
Andreas Mohr13769e32006-05-17 11:03:16 +0200176 * - figure out some cleverly evil scheme to possibly make ALSA AC97 code
177 * fully accept our quite incompatible ""AC97"" mixer and thus save some
178 * code (but I'm not too optimistic that doing this is possible at all)
Andreas Mohr02330fb2008-05-16 12:18:29 +0200179 * - use MMIO (memory-mapped I/O)? Slightly faster access, e.g. for gameport.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700180 */
181
Linus Torvalds1da177e2005-04-16 15:20:36 -0700182#include <asm/io.h>
183#include <linux/init.h>
Andreas Mohr689c6912010-12-27 21:17:35 +0100184#include <linux/bug.h> /* WARN_ONCE */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700185#include <linux/pci.h>
186#include <linux/delay.h>
187#include <linux/slab.h>
188#include <linux/gameport.h>
Paul Gortmaker65a77212011-07-15 13:13:37 -0400189#include <linux/module.h>
Matthias Gehre910638a2006-03-28 01:56:48 -0800190#include <linux/dma-mapping.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -0700191#include <sound/core.h>
192#include <sound/control.h>
193#include <sound/pcm.h>
194#include <sound/rawmidi.h>
195#include <sound/mpu401.h>
196#include <sound/opl3.h>
197#include <sound/initval.h>
Andreas Mohrb5dc20c2011-02-19 00:49:32 +0100198/*
199 * Config switch, to use ALSA's AC97 layer instead of old custom mixer crap.
200 * If the AC97 compatibility parts we needed to implement locally turn out
201 * to work nicely, then remove the old implementation eventually.
202 */
203#define AZF_USE_AC97_LAYER 1
204
205#ifdef AZF_USE_AC97_LAYER
206#include <sound/ac97_codec.h>
207#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700208#include "azt3328.h"
209
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200210MODULE_AUTHOR("Andreas Mohr <andi AT lisas.de>");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700211MODULE_DESCRIPTION("Aztech AZF3328 (PCI168)");
212MODULE_LICENSE("GPL");
213MODULE_SUPPORTED_DEVICE("{{Aztech,AZF3328}}");
214
215#if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE))
Andreas Mohr02330fb2008-05-16 12:18:29 +0200216#define SUPPORT_GAMEPORT 1
Linus Torvalds1da177e2005-04-16 15:20:36 -0700217#endif
218
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200219/* === Debug settings ===
220 Further diagnostic functionality than the settings below
Andreas Mohradf59312010-12-27 21:16:43 +0100221 does not need to be provided, since one can easily write a POSIX shell script
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200222 to dump the card's I/O ports (those listed in lspci -v -v):
Andreas Mohradf59312010-12-27 21:16:43 +0100223 dump()
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200224 {
225 local descr=$1; local addr=$2; local count=$3
226
227 echo "${descr}: ${count} @ ${addr}:"
Andreas Mohradf59312010-12-27 21:16:43 +0100228 dd if=/dev/port skip=`printf %d ${addr}` count=${count} bs=1 \
229 2>/dev/null| hexdump -C
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200230 }
231 and then use something like
232 "dump joy200 0x200 8", "dump mpu388 0x388 4", "dump joy 0xb400 8",
233 "dump codec00 0xa800 32", "dump mixer 0xb800 64", "dump synth 0xbc00 8",
234 possibly within a "while true; do ... sleep 1; done" loop.
235 Tweaking ports could be done using
236 VALSTRING="`printf "%02x" $value`"
Andreas Mohradf59312010-12-27 21:16:43 +0100237 printf "\x""$VALSTRING"|dd of=/dev/port seek=`printf %d ${addr}` bs=1 \
238 2>/dev/null
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200239*/
240
Linus Torvalds1da177e2005-04-16 15:20:36 -0700241static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
242module_param_array(index, int, NULL, 0444);
243MODULE_PARM_DESC(index, "Index value for AZF3328 soundcard.");
244
245static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
246module_param_array(id, charp, NULL, 0444);
247MODULE_PARM_DESC(id, "ID string for AZF3328 soundcard.");
248
Rusty Russella67ff6a2011-12-15 13:49:36 +1030249static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700250module_param_array(enable, bool, NULL, 0444);
251MODULE_PARM_DESC(enable, "Enable AZF3328 soundcard.");
252
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200253static int seqtimer_scaling = 128;
254module_param(seqtimer_scaling, int, 0444);
255MODULE_PARM_DESC(seqtimer_scaling, "Set 1024000Hz sequencer timer scale factor (lockup danger!). Default 128.");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700256
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200257enum snd_azf3328_codec_type {
Andreas Mohradf59312010-12-27 21:16:43 +0100258 /* warning: fixed indices (also used for bitmask checks!) */
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200259 AZF_CODEC_PLAYBACK = 0,
260 AZF_CODEC_CAPTURE = 1,
261 AZF_CODEC_I2S_OUT = 2,
Andreas Mohr02330fb2008-05-16 12:18:29 +0200262};
263
Andreas Mohrda237f32010-12-27 21:17:26 +0100264struct snd_azf3328_codec_data {
265 unsigned long io_base; /* keep first! (avoid offset calc) */
266 unsigned int dma_base; /* helper to avoid an indirection in hotpath */
267 spinlock_t *lock; /* TODO: convert to our own per-codec lock member */
268 struct snd_pcm_substream *substream;
269 bool running;
270 enum snd_azf3328_codec_type type;
271 const char *name;
272};
273
Takashi Iwai95de7762005-11-17 15:02:42 +0100274struct snd_azf3328 {
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200275 /* often-used fields towards beginning, then grouped */
Andreas Mohr02330fb2008-05-16 12:18:29 +0200276
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200277 unsigned long ctrl_io; /* usually 0xb000, size 128 */
Andreas Mohr02330fb2008-05-16 12:18:29 +0200278 unsigned long game_io; /* usually 0xb400, size 8 */
279 unsigned long mpu_io; /* usually 0xb800, size 4 */
280 unsigned long opl3_io; /* usually 0xbc00, size 8 */
281 unsigned long mixer_io; /* usually 0xc000, size 64 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700282
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200283 spinlock_t reg_lock;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700284
Takashi Iwai95de7762005-11-17 15:02:42 +0100285 struct snd_timer *timer;
Andreas Mohr02330fb2008-05-16 12:18:29 +0200286
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200287 struct snd_pcm *pcm[3];
288
289 /* playback, recording and I2S out codecs */
290 struct snd_azf3328_codec_data codecs[3];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700291
Andreas Mohrb5dc20c2011-02-19 00:49:32 +0100292#ifdef AZF_USE_AC97_LAYER
293 struct snd_ac97 *ac97;
294#endif
295
Takashi Iwai95de7762005-11-17 15:02:42 +0100296 struct snd_card *card;
297 struct snd_rawmidi *rmidi;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700298
Andreas Mohr02330fb2008-05-16 12:18:29 +0200299#ifdef SUPPORT_GAMEPORT
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200300 struct gameport *gameport;
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200301 u16 axes[4];
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200302#endif
303
304 struct pci_dev *pci;
305 int irq;
Andreas Mohrca54bde2006-05-17 11:02:24 +0200306
Andreas Mohr627d3e72008-06-23 11:50:47 +0200307 /* register 0x6a is write-only, thus need to remember setting.
308 * If we need to add more registers here, then we might try to fold this
309 * into some transparent combined shadow register handling with
310 * CONFIG_PM register storage below, but that's slightly difficult. */
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200311 u16 shadow_reg_ctrl_6AH;
Andreas Mohr627d3e72008-06-23 11:50:47 +0200312
Takashi Iwaic7561cd2012-08-14 18:12:04 +0200313#ifdef CONFIG_PM_SLEEP
Andreas Mohrca54bde2006-05-17 11:02:24 +0200314 /* register value containers for power management
Andreas Mohr78df6172009-07-12 22:17:54 +0200315 * Note: not always full I/O range preserved (similar to Win driver!) */
316 u32 saved_regs_ctrl[AZF_ALIGN(AZF_IO_SIZE_CTRL_PM) / 4];
317 u32 saved_regs_game[AZF_ALIGN(AZF_IO_SIZE_GAME_PM) / 4];
318 u32 saved_regs_mpu[AZF_ALIGN(AZF_IO_SIZE_MPU_PM) / 4];
319 u32 saved_regs_opl3[AZF_ALIGN(AZF_IO_SIZE_OPL3_PM) / 4];
320 u32 saved_regs_mixer[AZF_ALIGN(AZF_IO_SIZE_MIXER_PM) / 4];
Andreas Mohrca54bde2006-05-17 11:02:24 +0200321#endif
Takashi Iwai95de7762005-11-17 15:02:42 +0100322};
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200323
Benoit Taine9baa3c32014-08-08 15:56:03 +0200324static const struct pci_device_id snd_azf3328_ids[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700325 { 0x122D, 0x50DC, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* PCI168/3328 */
326 { 0x122D, 0x80DA, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* 3328 */
327 { 0, }
328};
329
330MODULE_DEVICE_TABLE(pci, snd_azf3328_ids);
331
Andreas Mohr02330fb2008-05-16 12:18:29 +0200332
333static int
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200334snd_azf3328_io_reg_setb(unsigned reg, u8 mask, bool do_set)
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200335{
Andreas Mohradf59312010-12-27 21:16:43 +0100336 /* Well, strictly spoken, the inb/outb sequence isn't atomic
337 and would need locking. However we currently don't care
338 since it potentially complicates matters. */
Andreas Mohr02330fb2008-05-16 12:18:29 +0200339 u8 prev = inb(reg), new;
340
341 new = (do_set) ? (prev|mask) : (prev & ~mask);
342 /* we need to always write the new value no matter whether it differs
343 * or not, since some register bits don't indicate their setting */
344 outb(new, reg);
345 if (new != prev)
346 return 1;
347
348 return 0;
349}
350
Andreas Mohr02330fb2008-05-16 12:18:29 +0200351static inline void
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200352snd_azf3328_codec_outb(const struct snd_azf3328_codec_data *codec,
353 unsigned reg,
354 u8 value
355)
Andreas Mohr02330fb2008-05-16 12:18:29 +0200356{
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200357 outb(value, codec->io_base + reg);
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200358}
359
360static inline u8
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200361snd_azf3328_codec_inb(const struct snd_azf3328_codec_data *codec, unsigned reg)
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200362{
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200363 return inb(codec->io_base + reg);
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200364}
365
366static inline void
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200367snd_azf3328_codec_outw(const struct snd_azf3328_codec_data *codec,
368 unsigned reg,
369 u16 value
370)
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200371{
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200372 outw(value, codec->io_base + reg);
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200373}
374
375static inline u16
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200376snd_azf3328_codec_inw(const struct snd_azf3328_codec_data *codec, unsigned reg)
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200377{
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200378 return inw(codec->io_base + reg);
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200379}
380
381static inline void
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200382snd_azf3328_codec_outl(const struct snd_azf3328_codec_data *codec,
383 unsigned reg,
384 u32 value
385)
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200386{
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200387 outl(value, codec->io_base + reg);
Andreas Mohr02330fb2008-05-16 12:18:29 +0200388}
389
Andreas Mohr689c6912010-12-27 21:17:35 +0100390static inline void
391snd_azf3328_codec_outl_multi(const struct snd_azf3328_codec_data *codec,
392 unsigned reg, const void *buffer, int count
393)
394{
395 unsigned long addr = codec->io_base + reg;
396 if (count) {
397 const u32 *buf = buffer;
398 do {
399 outl(*buf++, addr);
400 addr += 4;
401 } while (--count);
402 }
403}
404
Andreas Mohr02330fb2008-05-16 12:18:29 +0200405static inline u32
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200406snd_azf3328_codec_inl(const struct snd_azf3328_codec_data *codec, unsigned reg)
Andreas Mohr02330fb2008-05-16 12:18:29 +0200407{
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200408 return inl(codec->io_base + reg);
409}
410
411static inline void
412snd_azf3328_ctrl_outb(const struct snd_azf3328 *chip, unsigned reg, u8 value)
413{
414 outb(value, chip->ctrl_io + reg);
415}
416
417static inline u8
418snd_azf3328_ctrl_inb(const struct snd_azf3328 *chip, unsigned reg)
419{
420 return inb(chip->ctrl_io + reg);
421}
422
Takashi Iwai4a8d9d72014-02-25 14:04:46 +0100423static inline u16
424snd_azf3328_ctrl_inw(const struct snd_azf3328 *chip, unsigned reg)
425{
426 return inw(chip->ctrl_io + reg);
427}
428
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200429static inline void
430snd_azf3328_ctrl_outw(const struct snd_azf3328 *chip, unsigned reg, u16 value)
431{
432 outw(value, chip->ctrl_io + reg);
433}
434
435static inline void
436snd_azf3328_ctrl_outl(const struct snd_azf3328 *chip, unsigned reg, u32 value)
437{
438 outl(value, chip->ctrl_io + reg);
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200439}
440
441static inline void
Andreas Mohr02330fb2008-05-16 12:18:29 +0200442snd_azf3328_game_outb(const struct snd_azf3328 *chip, unsigned reg, u8 value)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700443{
Andreas Mohr02330fb2008-05-16 12:18:29 +0200444 outb(value, chip->game_io + reg);
445}
446
447static inline void
448snd_azf3328_game_outw(const struct snd_azf3328 *chip, unsigned reg, u16 value)
449{
450 outw(value, chip->game_io + reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700451}
452
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200453static inline u8
Andreas Mohr02330fb2008-05-16 12:18:29 +0200454snd_azf3328_game_inb(const struct snd_azf3328 *chip, unsigned reg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700455{
Andreas Mohr02330fb2008-05-16 12:18:29 +0200456 return inb(chip->game_io + reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700457}
458
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200459static inline u16
Andreas Mohr02330fb2008-05-16 12:18:29 +0200460snd_azf3328_game_inw(const struct snd_azf3328 *chip, unsigned reg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700461{
Andreas Mohr02330fb2008-05-16 12:18:29 +0200462 return inw(chip->game_io + reg);
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200463}
464
Andreas Mohr02330fb2008-05-16 12:18:29 +0200465static inline void
466snd_azf3328_mixer_outw(const struct snd_azf3328 *chip, unsigned reg, u16 value)
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200467{
Andreas Mohr02330fb2008-05-16 12:18:29 +0200468 outw(value, chip->mixer_io + reg);
469}
470
471static inline u16
472snd_azf3328_mixer_inw(const struct snd_azf3328 *chip, unsigned reg)
473{
474 return inw(chip->mixer_io + reg);
475}
476
477#define AZF_MUTE_BIT 0x80
478
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200479static bool
Andreas Mohrb5dc20c2011-02-19 00:49:32 +0100480snd_azf3328_mixer_mute_control(const struct snd_azf3328 *chip,
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200481 unsigned reg, bool do_mute
Andreas Mohr02330fb2008-05-16 12:18:29 +0200482)
483{
484 unsigned long portbase = chip->mixer_io + reg + 1;
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200485 bool updated;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700486
487 /* the mute bit is on the *second* (i.e. right) register of a
488 * left/right channel setting */
Andreas Mohr02330fb2008-05-16 12:18:29 +0200489 updated = snd_azf3328_io_reg_setb(portbase, AZF_MUTE_BIT, do_mute);
490
491 /* indicate whether it was muted before */
492 return (do_mute) ? !updated : updated;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700493}
494
Andreas Mohrb5dc20c2011-02-19 00:49:32 +0100495static inline bool
496snd_azf3328_mixer_mute_control_master(const struct snd_azf3328 *chip,
497 bool do_mute
498)
499{
500 return snd_azf3328_mixer_mute_control(
501 chip,
502 IDX_MIXER_PLAY_MASTER,
503 do_mute
504 );
505}
506
507static inline bool
508snd_azf3328_mixer_mute_control_pcm(const struct snd_azf3328 *chip,
509 bool do_mute
510)
511{
512 return snd_azf3328_mixer_mute_control(
513 chip,
514 IDX_MIXER_WAVEOUT,
515 do_mute
516 );
517}
518
519static inline void
520snd_azf3328_mixer_reset(const struct snd_azf3328 *chip)
521{
522 /* reset (close) mixer:
523 * first mute master volume, then reset
524 */
525 snd_azf3328_mixer_mute_control_master(chip, 1);
526 snd_azf3328_mixer_outw(chip, IDX_MIXER_RESET, 0x0000);
527}
528
529#ifdef AZF_USE_AC97_LAYER
530
531static inline void
Takashi Iwai4a8d9d72014-02-25 14:04:46 +0100532snd_azf3328_mixer_ac97_map_unsupported(const struct snd_azf3328 *chip,
533 unsigned short reg, const char *mode)
Andreas Mohrb5dc20c2011-02-19 00:49:32 +0100534{
535 /* need to add some more or less clever emulation? */
Takashi Iwai4a8d9d72014-02-25 14:04:46 +0100536 dev_warn(chip->card->dev,
537 "missing %s emulation for AC97 register 0x%02x!\n",
Andreas Mohrb5dc20c2011-02-19 00:49:32 +0100538 mode, reg);
539}
540
541/*
542 * Need to have _special_ AC97 mixer hardware register index mapper,
543 * to compensate for the issue of a rather AC97-incompatible hardware layout.
544 */
545#define AZF_REG_MASK 0x3f
546#define AZF_AC97_REG_UNSUPPORTED 0x8000
547#define AZF_AC97_REG_REAL_IO_READ 0x4000
548#define AZF_AC97_REG_REAL_IO_WRITE 0x2000
549#define AZF_AC97_REG_REAL_IO_RW \
550 (AZF_AC97_REG_REAL_IO_READ | AZF_AC97_REG_REAL_IO_WRITE)
551#define AZF_AC97_REG_EMU_IO_READ 0x0400
552#define AZF_AC97_REG_EMU_IO_WRITE 0x0200
553#define AZF_AC97_REG_EMU_IO_RW \
554 (AZF_AC97_REG_EMU_IO_READ | AZF_AC97_REG_EMU_IO_WRITE)
555static unsigned short
556snd_azf3328_mixer_ac97_map_reg_idx(unsigned short reg)
557{
558 static const struct {
559 unsigned short azf_reg;
560 } azf_reg_mapper[] = {
561 /* Especially when taking into consideration
562 * mono/stereo-based sequence of azf vs. AC97 control series,
563 * it's quite obvious that azf simply got rid
564 * of the AC97_HEADPHONE control at its intended offset,
565 * thus shifted _all_ controls by one,
566 * and _then_ simply added it as an FMSYNTH control at the end,
567 * to make up for the offset.
568 * This means we'll have to translate indices here as
569 * needed and then do some tiny AC97 patch action
570 * (snd_ac97_rename_vol_ctl() etc.) - that's it.
571 */
572 { /* AC97_RESET */ IDX_MIXER_RESET
573 | AZF_AC97_REG_REAL_IO_WRITE
574 | AZF_AC97_REG_EMU_IO_READ },
575 { /* AC97_MASTER */ IDX_MIXER_PLAY_MASTER },
576 /* note large shift: AC97_HEADPHONE to IDX_MIXER_FMSYNTH! */
577 { /* AC97_HEADPHONE */ IDX_MIXER_FMSYNTH },
578 { /* AC97_MASTER_MONO */ IDX_MIXER_MODEMOUT },
579 { /* AC97_MASTER_TONE */ IDX_MIXER_BASSTREBLE },
580 { /* AC97_PC_BEEP */ IDX_MIXER_PCBEEP },
581 { /* AC97_PHONE */ IDX_MIXER_MODEMIN },
582 { /* AC97_MIC */ IDX_MIXER_MIC },
583 { /* AC97_LINE */ IDX_MIXER_LINEIN },
584 { /* AC97_CD */ IDX_MIXER_CDAUDIO },
585 { /* AC97_VIDEO */ IDX_MIXER_VIDEO },
586 { /* AC97_AUX */ IDX_MIXER_AUX },
587 { /* AC97_PCM */ IDX_MIXER_WAVEOUT },
588 { /* AC97_REC_SEL */ IDX_MIXER_REC_SELECT },
589 { /* AC97_REC_GAIN */ IDX_MIXER_REC_VOLUME },
590 { /* AC97_REC_GAIN_MIC */ AZF_AC97_REG_EMU_IO_RW },
591 { /* AC97_GENERAL_PURPOSE */ IDX_MIXER_ADVCTL2 },
592 { /* AC97_3D_CONTROL */ IDX_MIXER_ADVCTL1 },
593 };
594
595 unsigned short reg_azf = AZF_AC97_REG_UNSUPPORTED;
596
597 /* azf3328 supports the low-numbered and low-spec:ed range
598 of AC97 regs only */
599 if (reg <= AC97_3D_CONTROL) {
600 unsigned short reg_idx = reg / 2;
601 reg_azf = azf_reg_mapper[reg_idx].azf_reg;
602 /* a translation-only entry means it's real read/write: */
603 if (!(reg_azf & ~AZF_REG_MASK))
604 reg_azf |= AZF_AC97_REG_REAL_IO_RW;
605 } else {
606 switch (reg) {
607 case AC97_POWERDOWN:
608 reg_azf = AZF_AC97_REG_EMU_IO_RW;
609 break;
610 case AC97_EXTENDED_ID:
611 reg_azf = AZF_AC97_REG_EMU_IO_READ;
612 break;
613 case AC97_EXTENDED_STATUS:
614 /* I don't know what the h*ll AC97 layer
615 * would consult this _extended_ register for
616 * given a base-AC97-advertised card,
617 * but let's just emulate it anyway :-P
618 */
619 reg_azf = AZF_AC97_REG_EMU_IO_RW;
620 break;
621 case AC97_VENDOR_ID1:
622 case AC97_VENDOR_ID2:
623 reg_azf = AZF_AC97_REG_EMU_IO_READ;
624 break;
625 }
626 }
627 return reg_azf;
628}
629
630static const unsigned short
631azf_emulated_ac97_caps =
632 AC97_BC_DEDICATED_MIC |
633 AC97_BC_BASS_TREBLE |
634 /* Headphone is an FM Synth control here */
635 AC97_BC_HEADPHONE |
636 /* no AC97_BC_LOUDNESS! */
637 /* mask 0x7c00 is
638 vendor-specific 3D enhancement
639 vendor indicator.
640 Since there actually _is_ an
641 entry for Aztech Labs
642 (13), make damn sure
643 to indicate it. */
644 (13 << 10);
645
646static const unsigned short
647azf_emulated_ac97_powerdown =
648 /* pretend everything to be active */
649 AC97_PD_ADC_STATUS |
650 AC97_PD_DAC_STATUS |
651 AC97_PD_MIXER_STATUS |
652 AC97_PD_VREF_STATUS;
653
654/*
655 * Emulated, _inofficial_ vendor ID
656 * (there might be some devices such as the MR 2800-W
657 * which could reveal the real Aztech AC97 ID).
658 * We choose to use "AZT" prefix, and then use 1 to indicate PCI168
659 * (better don't use 0x68 since there's a PCI368 as well).
660 */
661static const unsigned int
662azf_emulated_ac97_vendor_id = 0x415a5401;
663
664static unsigned short
665snd_azf3328_mixer_ac97_read(struct snd_ac97 *ac97, unsigned short reg_ac97)
666{
667 const struct snd_azf3328 *chip = ac97->private_data;
668 unsigned short reg_azf = snd_azf3328_mixer_ac97_map_reg_idx(reg_ac97);
669 unsigned short reg_val = 0;
Peter Senna Tschudine0f17c72013-09-22 20:44:12 +0200670 bool unsupported = false;
Andreas Mohrb5dc20c2011-02-19 00:49:32 +0100671
Takashi Iwai4a8d9d72014-02-25 14:04:46 +0100672 dev_dbg(chip->card->dev, "snd_azf3328_mixer_ac97_read reg_ac97 %u\n",
673 reg_ac97);
Andreas Mohrb5dc20c2011-02-19 00:49:32 +0100674 if (reg_azf & AZF_AC97_REG_UNSUPPORTED)
Peter Senna Tschudine0f17c72013-09-22 20:44:12 +0200675 unsupported = true;
Andreas Mohrb5dc20c2011-02-19 00:49:32 +0100676 else {
677 if (reg_azf & AZF_AC97_REG_REAL_IO_READ)
678 reg_val = snd_azf3328_mixer_inw(chip,
679 reg_azf & AZF_REG_MASK);
680 else {
681 /*
682 * Proceed with dummy I/O read,
683 * to ensure compatible timing where this may matter.
684 * (ALSA AC97 layer usually doesn't call I/O functions
685 * due to intelligent I/O caching anyway)
686 * Choose a mixer register that's thoroughly unrelated
687 * to common audio (try to minimize distortion).
688 */
689 snd_azf3328_mixer_inw(chip, IDX_MIXER_SOMETHING30H);
690 }
691
692 if (reg_azf & AZF_AC97_REG_EMU_IO_READ) {
693 switch (reg_ac97) {
694 case AC97_RESET:
695 reg_val |= azf_emulated_ac97_caps;
696 break;
697 case AC97_POWERDOWN:
698 reg_val |= azf_emulated_ac97_powerdown;
699 break;
700 case AC97_EXTENDED_ID:
701 case AC97_EXTENDED_STATUS:
702 /* AFAICS we simply can't support anything: */
703 reg_val |= 0;
704 break;
705 case AC97_VENDOR_ID1:
706 reg_val = azf_emulated_ac97_vendor_id >> 16;
707 break;
708 case AC97_VENDOR_ID2:
709 reg_val = azf_emulated_ac97_vendor_id & 0xffff;
710 break;
711 default:
Peter Senna Tschudine0f17c72013-09-22 20:44:12 +0200712 unsupported = true;
Andreas Mohrb5dc20c2011-02-19 00:49:32 +0100713 break;
714 }
715 }
716 }
717 if (unsupported)
Takashi Iwai4a8d9d72014-02-25 14:04:46 +0100718 snd_azf3328_mixer_ac97_map_unsupported(chip, reg_ac97, "read");
Andreas Mohrb5dc20c2011-02-19 00:49:32 +0100719
720 return reg_val;
721}
722
723static void
724snd_azf3328_mixer_ac97_write(struct snd_ac97 *ac97,
725 unsigned short reg_ac97, unsigned short val)
726{
727 const struct snd_azf3328 *chip = ac97->private_data;
728 unsigned short reg_azf = snd_azf3328_mixer_ac97_map_reg_idx(reg_ac97);
Peter Senna Tschudine0f17c72013-09-22 20:44:12 +0200729 bool unsupported = false;
Andreas Mohrb5dc20c2011-02-19 00:49:32 +0100730
Takashi Iwai4a8d9d72014-02-25 14:04:46 +0100731 dev_dbg(chip->card->dev,
Andreas Mohrb5dc20c2011-02-19 00:49:32 +0100732 "snd_azf3328_mixer_ac97_write reg_ac97 %u val %u\n",
Takashi Iwai4a8d9d72014-02-25 14:04:46 +0100733 reg_ac97, val);
Andreas Mohrb5dc20c2011-02-19 00:49:32 +0100734 if (reg_azf & AZF_AC97_REG_UNSUPPORTED)
Peter Senna Tschudine0f17c72013-09-22 20:44:12 +0200735 unsupported = true;
Andreas Mohrb5dc20c2011-02-19 00:49:32 +0100736 else {
737 if (reg_azf & AZF_AC97_REG_REAL_IO_WRITE)
738 snd_azf3328_mixer_outw(
739 chip,
740 reg_azf & AZF_REG_MASK,
741 val
742 );
743 else
744 if (reg_azf & AZF_AC97_REG_EMU_IO_WRITE) {
745 switch (reg_ac97) {
746 case AC97_REC_GAIN_MIC:
747 case AC97_POWERDOWN:
748 case AC97_EXTENDED_STATUS:
749 /*
750 * Silently swallow these writes.
751 * Since for most registers our card doesn't
752 * actually support a comparable feature,
753 * this is exactly what we should do here.
754 * The AC97 layer's I/O caching probably
755 * automatically takes care of all the rest...
756 * (remembers written values etc.)
757 */
758 break;
759 default:
Peter Senna Tschudine0f17c72013-09-22 20:44:12 +0200760 unsupported = true;
Andreas Mohrb5dc20c2011-02-19 00:49:32 +0100761 break;
762 }
763 }
764 }
765 if (unsupported)
Takashi Iwai4a8d9d72014-02-25 14:04:46 +0100766 snd_azf3328_mixer_ac97_map_unsupported(chip, reg_ac97, "write");
Andreas Mohrb5dc20c2011-02-19 00:49:32 +0100767}
768
Bill Pembertone23e7a12012-12-06 12:35:10 -0500769static int
Andreas Mohrb5dc20c2011-02-19 00:49:32 +0100770snd_azf3328_mixer_new(struct snd_azf3328 *chip)
771{
772 struct snd_ac97_bus *bus;
773 struct snd_ac97_template ac97;
774 static struct snd_ac97_bus_ops ops = {
775 .write = snd_azf3328_mixer_ac97_write,
776 .read = snd_azf3328_mixer_ac97_read,
777 };
778 int rc;
779
780 memset(&ac97, 0, sizeof(ac97));
781 ac97.scaps = AC97_SCAP_SKIP_MODEM
782 | AC97_SCAP_AUDIO /* we support audio! */
783 | AC97_SCAP_NO_SPDIF;
784 ac97.private_data = chip;
785 ac97.pci = chip->pci;
786
787 /*
788 * ALSA's AC97 layer has terrible init crackling issues,
789 * unfortunately, and since it makes use of AC97_RESET,
790 * there's no use trying to mute Master Playback proactively.
791 */
792
793 rc = snd_ac97_bus(chip->card, 0, &ops, NULL, &bus);
794 if (!rc)
795 rc = snd_ac97_mixer(bus, &ac97, &chip->ac97);
796 /*
797 * Make sure to complain loudly in case of AC97 init failure,
798 * since failure may happen quite often,
799 * due to this card being a very quirky AC97 "lookalike".
800 */
801 if (rc)
Takashi Iwai4a8d9d72014-02-25 14:04:46 +0100802 dev_err(chip->card->dev, "AC97 init failed, err %d!\n", rc);
Andreas Mohrb5dc20c2011-02-19 00:49:32 +0100803
804 /* If we return an error here, then snd_card_free() should
805 * free up any ac97 codecs that got created, as well as the bus.
806 */
807 return rc;
808}
809#else /* AZF_USE_AC97_LAYER */
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200810static void
Andreas Mohr02330fb2008-05-16 12:18:29 +0200811snd_azf3328_mixer_write_volume_gradually(const struct snd_azf3328 *chip,
812 unsigned reg,
813 unsigned char dst_vol_left,
814 unsigned char dst_vol_right,
815 int chan_sel, int delay
816)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700817{
Andreas Mohr02330fb2008-05-16 12:18:29 +0200818 unsigned long portbase = chip->mixer_io + reg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700819 unsigned char curr_vol_left = 0, curr_vol_right = 0;
Andreas Mohr02330fb2008-05-16 12:18:29 +0200820 int left_change = 0, right_change = 0;
821
Andreas Mohr02330fb2008-05-16 12:18:29 +0200822 if (chan_sel & SET_CHAN_LEFT) {
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200823 curr_vol_left = inb(portbase + 1);
Andreas Mohr02330fb2008-05-16 12:18:29 +0200824
825 /* take care of muting flag contained in left channel */
826 if (curr_vol_left & AZF_MUTE_BIT)
827 dst_vol_left |= AZF_MUTE_BIT;
828 else
829 dst_vol_left &= ~AZF_MUTE_BIT;
830
831 left_change = (curr_vol_left > dst_vol_left) ? -1 : 1;
832 }
833
834 if (chan_sel & SET_CHAN_RIGHT) {
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200835 curr_vol_right = inb(portbase + 0);
Andreas Mohr02330fb2008-05-16 12:18:29 +0200836
837 right_change = (curr_vol_right > dst_vol_right) ? -1 : 1;
838 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700839
Andreas Mohre2f87262006-05-17 11:04:19 +0200840 do {
Andreas Mohr02330fb2008-05-16 12:18:29 +0200841 if (left_change) {
842 if (curr_vol_left != dst_vol_left) {
843 curr_vol_left += left_change;
844 outb(curr_vol_left, portbase + 1);
845 } else
846 left_change = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700847 }
Andreas Mohr02330fb2008-05-16 12:18:29 +0200848 if (right_change) {
849 if (curr_vol_right != dst_vol_right) {
850 curr_vol_right += right_change;
851
Linus Torvalds1da177e2005-04-16 15:20:36 -0700852 /* during volume change, the right channel is crackling
853 * somewhat more than the left channel, unfortunately.
854 * This seems to be a hardware issue. */
Andreas Mohr02330fb2008-05-16 12:18:29 +0200855 outb(curr_vol_right, portbase + 0);
856 } else
857 right_change = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700858 }
859 if (delay)
860 mdelay(delay);
Andreas Mohr02330fb2008-05-16 12:18:29 +0200861 } while ((left_change) || (right_change));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700862}
863
864/*
865 * general mixer element
866 */
Takashi Iwai95de7762005-11-17 15:02:42 +0100867struct azf3328_mixer_reg {
Andreas Mohr02330fb2008-05-16 12:18:29 +0200868 unsigned reg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700869 unsigned int lchan_shift, rchan_shift;
870 unsigned int mask;
871 unsigned int invert: 1;
872 unsigned int stereo: 1;
873 unsigned int enum_c: 4;
Takashi Iwai95de7762005-11-17 15:02:42 +0100874};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700875
876#define COMPOSE_MIXER_REG(reg,lchan_shift,rchan_shift,mask,invert,stereo,enum_c) \
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200877 ((reg) | (lchan_shift << 8) | (rchan_shift << 12) | \
878 (mask << 16) | \
879 (invert << 24) | \
880 (stereo << 25) | \
881 (enum_c << 26))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700882
Takashi Iwai95de7762005-11-17 15:02:42 +0100883static void snd_azf3328_mixer_reg_decode(struct azf3328_mixer_reg *r, unsigned long val)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700884{
885 r->reg = val & 0xff;
886 r->lchan_shift = (val >> 8) & 0x0f;
887 r->rchan_shift = (val >> 12) & 0x0f;
888 r->mask = (val >> 16) & 0xff;
889 r->invert = (val >> 24) & 1;
890 r->stereo = (val >> 25) & 1;
891 r->enum_c = (val >> 26) & 0x0f;
892}
893
894/*
895 * mixer switches/volumes
896 */
897
898#define AZF3328_MIXER_SWITCH(xname, reg, shift, invert) \
899{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
900 .info = snd_azf3328_info_mixer, \
901 .get = snd_azf3328_get_mixer, .put = snd_azf3328_put_mixer, \
902 .private_value = COMPOSE_MIXER_REG(reg, shift, 0, 0x1, invert, 0, 0), \
903}
904
905#define AZF3328_MIXER_VOL_STEREO(xname, reg, mask, invert) \
906{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
907 .info = snd_azf3328_info_mixer, \
908 .get = snd_azf3328_get_mixer, .put = snd_azf3328_put_mixer, \
909 .private_value = COMPOSE_MIXER_REG(reg, 8, 0, mask, invert, 1, 0), \
910}
911
912#define AZF3328_MIXER_VOL_MONO(xname, reg, mask, is_right_chan) \
913{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
914 .info = snd_azf3328_info_mixer, \
915 .get = snd_azf3328_get_mixer, .put = snd_azf3328_put_mixer, \
916 .private_value = COMPOSE_MIXER_REG(reg, is_right_chan ? 0 : 8, 0, mask, 1, 0, 0), \
917}
918
919#define AZF3328_MIXER_VOL_SPECIAL(xname, reg, mask, shift, invert) \
920{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
921 .info = snd_azf3328_info_mixer, \
922 .get = snd_azf3328_get_mixer, .put = snd_azf3328_put_mixer, \
923 .private_value = COMPOSE_MIXER_REG(reg, shift, 0, mask, invert, 0, 0), \
924}
925
926#define AZF3328_MIXER_ENUM(xname, reg, enum_c, shift) \
927{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
928 .info = snd_azf3328_info_mixer_enum, \
929 .get = snd_azf3328_get_mixer_enum, .put = snd_azf3328_put_mixer_enum, \
930 .private_value = COMPOSE_MIXER_REG(reg, shift, 0, 0, 0, 0, enum_c), \
931}
932
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200933static int
Takashi Iwai95de7762005-11-17 15:02:42 +0100934snd_azf3328_info_mixer(struct snd_kcontrol *kcontrol,
935 struct snd_ctl_elem_info *uinfo)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700936{
Takashi Iwai95de7762005-11-17 15:02:42 +0100937 struct azf3328_mixer_reg reg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700938
Linus Torvalds1da177e2005-04-16 15:20:36 -0700939 snd_azf3328_mixer_reg_decode(&reg, kcontrol->private_value);
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200940 uinfo->type = reg.mask == 1 ?
941 SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700942 uinfo->count = reg.stereo + 1;
943 uinfo->value.integer.min = 0;
944 uinfo->value.integer.max = reg.mask;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700945 return 0;
946}
947
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200948static int
Takashi Iwai95de7762005-11-17 15:02:42 +0100949snd_azf3328_get_mixer(struct snd_kcontrol *kcontrol,
950 struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700951{
Takashi Iwai95de7762005-11-17 15:02:42 +0100952 struct snd_azf3328 *chip = snd_kcontrol_chip(kcontrol);
953 struct azf3328_mixer_reg reg;
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200954 u16 oreg, val;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700955
Linus Torvalds1da177e2005-04-16 15:20:36 -0700956 snd_azf3328_mixer_reg_decode(&reg, kcontrol->private_value);
957
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200958 oreg = snd_azf3328_mixer_inw(chip, reg.reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700959 val = (oreg >> reg.lchan_shift) & reg.mask;
960 if (reg.invert)
961 val = reg.mask - val;
962 ucontrol->value.integer.value[0] = val;
963 if (reg.stereo) {
964 val = (oreg >> reg.rchan_shift) & reg.mask;
965 if (reg.invert)
966 val = reg.mask - val;
967 ucontrol->value.integer.value[1] = val;
968 }
Takashi Iwai4a8d9d72014-02-25 14:04:46 +0100969 dev_dbg(chip->card->dev,
970 "get: %02x is %04x -> vol %02lx|%02lx (shift %02d|%02d, mask %02x, inv. %d, stereo %d)\n",
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200971 reg.reg, oreg,
972 ucontrol->value.integer.value[0], ucontrol->value.integer.value[1],
973 reg.lchan_shift, reg.rchan_shift, reg.mask, reg.invert, reg.stereo);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700974 return 0;
975}
976
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200977static int
Takashi Iwai95de7762005-11-17 15:02:42 +0100978snd_azf3328_put_mixer(struct snd_kcontrol *kcontrol,
979 struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700980{
Takashi Iwai95de7762005-11-17 15:02:42 +0100981 struct snd_azf3328 *chip = snd_kcontrol_chip(kcontrol);
982 struct azf3328_mixer_reg reg;
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200983 u16 oreg, nreg, val;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700984
Linus Torvalds1da177e2005-04-16 15:20:36 -0700985 snd_azf3328_mixer_reg_decode(&reg, kcontrol->private_value);
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200986 oreg = snd_azf3328_mixer_inw(chip, reg.reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700987 val = ucontrol->value.integer.value[0] & reg.mask;
988 if (reg.invert)
989 val = reg.mask - val;
990 nreg = oreg & ~(reg.mask << reg.lchan_shift);
991 nreg |= (val << reg.lchan_shift);
992 if (reg.stereo) {
993 val = ucontrol->value.integer.value[1] & reg.mask;
994 if (reg.invert)
995 val = reg.mask - val;
996 nreg &= ~(reg.mask << reg.rchan_shift);
997 nreg |= (val << reg.rchan_shift);
998 }
999 if (reg.mask >= 0x07) /* it's a volume control, so better take care */
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001000 snd_azf3328_mixer_write_volume_gradually(
1001 chip, reg.reg, nreg >> 8, nreg & 0xff,
1002 /* just set both channels, doesn't matter */
1003 SET_CHAN_LEFT|SET_CHAN_RIGHT,
1004 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001005 else
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001006 snd_azf3328_mixer_outw(chip, reg.reg, nreg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001007
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001008 dev_dbg(chip->card->dev,
1009 "put: %02x to %02lx|%02lx, oreg %04x; shift %02d|%02d -> nreg %04x; after: %04x\n",
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001010 reg.reg, ucontrol->value.integer.value[0], ucontrol->value.integer.value[1],
1011 oreg, reg.lchan_shift, reg.rchan_shift,
1012 nreg, snd_azf3328_mixer_inw(chip, reg.reg));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001013 return (nreg != oreg);
1014}
1015
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001016static int
Takashi Iwai95de7762005-11-17 15:02:42 +01001017snd_azf3328_info_mixer_enum(struct snd_kcontrol *kcontrol,
1018 struct snd_ctl_elem_info *uinfo)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001019{
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001020 static const char * const texts1[] = {
Andreas Mohr13769e32006-05-17 11:03:16 +02001021 "Mic1", "Mic2"
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001022 };
1023 static const char * const texts2[] = {
Andreas Mohr13769e32006-05-17 11:03:16 +02001024 "Mix", "Mic"
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001025 };
1026 static const char * const texts3[] = {
Andreas Mohr02330fb2008-05-16 12:18:29 +02001027 "Mic", "CD", "Video", "Aux",
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001028 "Line", "Mix", "Mix Mono", "Phone"
Linus Torvalds1da177e2005-04-16 15:20:36 -07001029 };
Andreas Mohr13769e32006-05-17 11:03:16 +02001030 static const char * const texts4[] = {
1031 "pre 3D", "post 3D"
1032 };
Takashi Iwai95de7762005-11-17 15:02:42 +01001033 struct azf3328_mixer_reg reg;
Andreas Mohr627d3e72008-06-23 11:50:47 +02001034 const char * const *p = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001035
1036 snd_azf3328_mixer_reg_decode(&reg, kcontrol->private_value);
Andreas Mohre2f87262006-05-17 11:04:19 +02001037 if (reg.reg == IDX_MIXER_ADVCTL2) {
Andreas Mohr13769e32006-05-17 11:03:16 +02001038 switch(reg.lchan_shift) {
1039 case 8: /* modem out sel */
Andreas Mohr627d3e72008-06-23 11:50:47 +02001040 p = texts1;
Andreas Mohr13769e32006-05-17 11:03:16 +02001041 break;
1042 case 9: /* mono sel source */
Andreas Mohr627d3e72008-06-23 11:50:47 +02001043 p = texts2;
Andreas Mohr13769e32006-05-17 11:03:16 +02001044 break;
1045 case 15: /* PCM Out Path */
Andreas Mohr627d3e72008-06-23 11:50:47 +02001046 p = texts4;
Andreas Mohr13769e32006-05-17 11:03:16 +02001047 break;
1048 }
Takashi Iwai9b311a02014-10-20 18:16:13 +02001049 } else if (reg.reg == IDX_MIXER_REC_SELECT)
Andreas Mohr627d3e72008-06-23 11:50:47 +02001050 p = texts3;
Andreas Mohr02330fb2008-05-16 12:18:29 +02001051
Takashi Iwai9b311a02014-10-20 18:16:13 +02001052 return snd_ctl_enum_info(uinfo,
1053 (reg.reg == IDX_MIXER_REC_SELECT) ? 2 : 1,
1054 reg.enum_c, p);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001055}
1056
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001057static int
Takashi Iwai95de7762005-11-17 15:02:42 +01001058snd_azf3328_get_mixer_enum(struct snd_kcontrol *kcontrol,
1059 struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001060{
Takashi Iwai95de7762005-11-17 15:02:42 +01001061 struct snd_azf3328 *chip = snd_kcontrol_chip(kcontrol);
1062 struct azf3328_mixer_reg reg;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001063 unsigned short val;
Andreas Mohr02330fb2008-05-16 12:18:29 +02001064
Linus Torvalds1da177e2005-04-16 15:20:36 -07001065 snd_azf3328_mixer_reg_decode(&reg, kcontrol->private_value);
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001066 val = snd_azf3328_mixer_inw(chip, reg.reg);
Andreas Mohre2f87262006-05-17 11:04:19 +02001067 if (reg.reg == IDX_MIXER_REC_SELECT) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001068 ucontrol->value.enumerated.item[0] = (val >> 8) & (reg.enum_c - 1);
1069 ucontrol->value.enumerated.item[1] = (val >> 0) & (reg.enum_c - 1);
Andreas Mohre2f87262006-05-17 11:04:19 +02001070 } else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001071 ucontrol->value.enumerated.item[0] = (val >> reg.lchan_shift) & (reg.enum_c - 1);
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001072
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001073 dev_dbg(chip->card->dev,
1074 "get_enum: %02x is %04x -> %d|%d (shift %02d, enum_c %d)\n",
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001075 reg.reg, val, ucontrol->value.enumerated.item[0], ucontrol->value.enumerated.item[1],
1076 reg.lchan_shift, reg.enum_c);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001077 return 0;
1078}
1079
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001080static int
Takashi Iwai95de7762005-11-17 15:02:42 +01001081snd_azf3328_put_mixer_enum(struct snd_kcontrol *kcontrol,
1082 struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001083{
Takashi Iwai95de7762005-11-17 15:02:42 +01001084 struct snd_azf3328 *chip = snd_kcontrol_chip(kcontrol);
1085 struct azf3328_mixer_reg reg;
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001086 u16 oreg, nreg, val;
Andreas Mohr02330fb2008-05-16 12:18:29 +02001087
Linus Torvalds1da177e2005-04-16 15:20:36 -07001088 snd_azf3328_mixer_reg_decode(&reg, kcontrol->private_value);
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001089 oreg = snd_azf3328_mixer_inw(chip, reg.reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001090 val = oreg;
Andreas Mohre2f87262006-05-17 11:04:19 +02001091 if (reg.reg == IDX_MIXER_REC_SELECT) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001092 if (ucontrol->value.enumerated.item[0] > reg.enum_c - 1U ||
1093 ucontrol->value.enumerated.item[1] > reg.enum_c - 1U)
1094 return -EINVAL;
1095 val = (ucontrol->value.enumerated.item[0] << 8) |
1096 (ucontrol->value.enumerated.item[1] << 0);
Andreas Mohre2f87262006-05-17 11:04:19 +02001097 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001098 if (ucontrol->value.enumerated.item[0] > reg.enum_c - 1U)
1099 return -EINVAL;
1100 val &= ~((reg.enum_c - 1) << reg.lchan_shift);
1101 val |= (ucontrol->value.enumerated.item[0] << reg.lchan_shift);
1102 }
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001103 snd_azf3328_mixer_outw(chip, reg.reg, val);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001104 nreg = val;
1105
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001106 dev_dbg(chip->card->dev,
1107 "put_enum: %02x to %04x, oreg %04x\n", reg.reg, val, oreg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001108 return (nreg != oreg);
1109}
1110
Bill Pembertone23e7a12012-12-06 12:35:10 -05001111static struct snd_kcontrol_new snd_azf3328_mixer_controls[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001112 AZF3328_MIXER_SWITCH("Master Playback Switch", IDX_MIXER_PLAY_MASTER, 15, 1),
1113 AZF3328_MIXER_VOL_STEREO("Master Playback Volume", IDX_MIXER_PLAY_MASTER, 0x1f, 1),
Andreas Mohr627d3e72008-06-23 11:50:47 +02001114 AZF3328_MIXER_SWITCH("PCM Playback Switch", IDX_MIXER_WAVEOUT, 15, 1),
1115 AZF3328_MIXER_VOL_STEREO("PCM Playback Volume",
1116 IDX_MIXER_WAVEOUT, 0x1f, 1),
1117 AZF3328_MIXER_SWITCH("PCM 3D Bypass Playback Switch",
1118 IDX_MIXER_ADVCTL2, 7, 1),
Linus Torvalds1da177e2005-04-16 15:20:36 -07001119 AZF3328_MIXER_SWITCH("FM Playback Switch", IDX_MIXER_FMSYNTH, 15, 1),
1120 AZF3328_MIXER_VOL_STEREO("FM Playback Volume", IDX_MIXER_FMSYNTH, 0x1f, 1),
1121 AZF3328_MIXER_SWITCH("CD Playback Switch", IDX_MIXER_CDAUDIO, 15, 1),
1122 AZF3328_MIXER_VOL_STEREO("CD Playback Volume", IDX_MIXER_CDAUDIO, 0x1f, 1),
1123 AZF3328_MIXER_SWITCH("Capture Switch", IDX_MIXER_REC_VOLUME, 15, 1),
1124 AZF3328_MIXER_VOL_STEREO("Capture Volume", IDX_MIXER_REC_VOLUME, 0x0f, 0),
1125 AZF3328_MIXER_ENUM("Capture Source", IDX_MIXER_REC_SELECT, 8, 0),
1126 AZF3328_MIXER_SWITCH("Mic Playback Switch", IDX_MIXER_MIC, 15, 1),
1127 AZF3328_MIXER_VOL_MONO("Mic Playback Volume", IDX_MIXER_MIC, 0x1f, 1),
1128 AZF3328_MIXER_SWITCH("Mic Boost (+20dB)", IDX_MIXER_MIC, 6, 0),
1129 AZF3328_MIXER_SWITCH("Line Playback Switch", IDX_MIXER_LINEIN, 15, 1),
1130 AZF3328_MIXER_VOL_STEREO("Line Playback Volume", IDX_MIXER_LINEIN, 0x1f, 1),
Jaroslav Kyselad355c82a2009-11-03 15:47:25 +01001131 AZF3328_MIXER_SWITCH("Beep Playback Switch", IDX_MIXER_PCBEEP, 15, 1),
1132 AZF3328_MIXER_VOL_SPECIAL("Beep Playback Volume", IDX_MIXER_PCBEEP, 0x0f, 1, 1),
Linus Torvalds1da177e2005-04-16 15:20:36 -07001133 AZF3328_MIXER_SWITCH("Video Playback Switch", IDX_MIXER_VIDEO, 15, 1),
1134 AZF3328_MIXER_VOL_STEREO("Video Playback Volume", IDX_MIXER_VIDEO, 0x1f, 1),
1135 AZF3328_MIXER_SWITCH("Aux Playback Switch", IDX_MIXER_AUX, 15, 1),
1136 AZF3328_MIXER_VOL_STEREO("Aux Playback Volume", IDX_MIXER_AUX, 0x1f, 1),
1137 AZF3328_MIXER_SWITCH("Modem Playback Switch", IDX_MIXER_MODEMOUT, 15, 1),
1138 AZF3328_MIXER_VOL_MONO("Modem Playback Volume", IDX_MIXER_MODEMOUT, 0x1f, 1),
1139 AZF3328_MIXER_SWITCH("Modem Capture Switch", IDX_MIXER_MODEMIN, 15, 1),
1140 AZF3328_MIXER_VOL_MONO("Modem Capture Volume", IDX_MIXER_MODEMIN, 0x1f, 1),
Andreas Mohr13769e32006-05-17 11:03:16 +02001141 AZF3328_MIXER_ENUM("Mic Select", IDX_MIXER_ADVCTL2, 2, 8),
1142 AZF3328_MIXER_ENUM("Mono Output Select", IDX_MIXER_ADVCTL2, 2, 9),
Andreas Mohre24a1212007-03-26 12:49:45 +02001143 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 -07001144 AZF3328_MIXER_VOL_SPECIAL("Tone Control - Treble", IDX_MIXER_BASSTREBLE, 0x07, 1, 0),
1145 AZF3328_MIXER_VOL_SPECIAL("Tone Control - Bass", IDX_MIXER_BASSTREBLE, 0x07, 9, 0),
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001146 AZF3328_MIXER_SWITCH("3D Control - Switch", IDX_MIXER_ADVCTL2, 13, 0),
Andreas Mohr13769e32006-05-17 11:03:16 +02001147 AZF3328_MIXER_VOL_SPECIAL("3D Control - Width", IDX_MIXER_ADVCTL1, 0x07, 1, 0), /* "3D Width" */
1148 AZF3328_MIXER_VOL_SPECIAL("3D Control - Depth", IDX_MIXER_ADVCTL1, 0x03, 8, 0), /* "Hifi 3D" */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001149#if MIXER_TESTING
1150 AZF3328_MIXER_SWITCH("0", IDX_MIXER_ADVCTL2, 0, 0),
1151 AZF3328_MIXER_SWITCH("1", IDX_MIXER_ADVCTL2, 1, 0),
1152 AZF3328_MIXER_SWITCH("2", IDX_MIXER_ADVCTL2, 2, 0),
1153 AZF3328_MIXER_SWITCH("3", IDX_MIXER_ADVCTL2, 3, 0),
1154 AZF3328_MIXER_SWITCH("4", IDX_MIXER_ADVCTL2, 4, 0),
1155 AZF3328_MIXER_SWITCH("5", IDX_MIXER_ADVCTL2, 5, 0),
1156 AZF3328_MIXER_SWITCH("6", IDX_MIXER_ADVCTL2, 6, 0),
1157 AZF3328_MIXER_SWITCH("7", IDX_MIXER_ADVCTL2, 7, 0),
1158 AZF3328_MIXER_SWITCH("8", IDX_MIXER_ADVCTL2, 8, 0),
1159 AZF3328_MIXER_SWITCH("9", IDX_MIXER_ADVCTL2, 9, 0),
1160 AZF3328_MIXER_SWITCH("10", IDX_MIXER_ADVCTL2, 10, 0),
1161 AZF3328_MIXER_SWITCH("11", IDX_MIXER_ADVCTL2, 11, 0),
1162 AZF3328_MIXER_SWITCH("12", IDX_MIXER_ADVCTL2, 12, 0),
1163 AZF3328_MIXER_SWITCH("13", IDX_MIXER_ADVCTL2, 13, 0),
1164 AZF3328_MIXER_SWITCH("14", IDX_MIXER_ADVCTL2, 14, 0),
1165 AZF3328_MIXER_SWITCH("15", IDX_MIXER_ADVCTL2, 15, 0),
1166#endif
1167};
1168
Bill Pembertone23e7a12012-12-06 12:35:10 -05001169static u16 snd_azf3328_init_values[][2] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001170 { IDX_MIXER_PLAY_MASTER, MIXER_MUTE_MASK|0x1f1f },
1171 { IDX_MIXER_MODEMOUT, MIXER_MUTE_MASK|0x1f1f },
1172 { IDX_MIXER_BASSTREBLE, 0x0000 },
1173 { IDX_MIXER_PCBEEP, MIXER_MUTE_MASK|0x1f1f },
1174 { IDX_MIXER_MODEMIN, MIXER_MUTE_MASK|0x1f1f },
1175 { IDX_MIXER_MIC, MIXER_MUTE_MASK|0x001f },
1176 { IDX_MIXER_LINEIN, MIXER_MUTE_MASK|0x1f1f },
1177 { IDX_MIXER_CDAUDIO, MIXER_MUTE_MASK|0x1f1f },
1178 { IDX_MIXER_VIDEO, MIXER_MUTE_MASK|0x1f1f },
1179 { IDX_MIXER_AUX, MIXER_MUTE_MASK|0x1f1f },
1180 { IDX_MIXER_WAVEOUT, MIXER_MUTE_MASK|0x1f1f },
1181 { IDX_MIXER_FMSYNTH, MIXER_MUTE_MASK|0x1f1f },
1182 { IDX_MIXER_REC_VOLUME, MIXER_MUTE_MASK|0x0707 },
1183};
1184
Bill Pembertone23e7a12012-12-06 12:35:10 -05001185static int
Takashi Iwai95de7762005-11-17 15:02:42 +01001186snd_azf3328_mixer_new(struct snd_azf3328 *chip)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001187{
Takashi Iwai95de7762005-11-17 15:02:42 +01001188 struct snd_card *card;
1189 const struct snd_kcontrol_new *sw;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001190 unsigned int idx;
1191 int err;
1192
Takashi Iwaida3cec32008-08-08 17:12:14 +02001193 if (snd_BUG_ON(!chip || !chip->card))
1194 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001195
1196 card = chip->card;
1197
1198 /* mixer reset */
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001199 snd_azf3328_mixer_outw(chip, IDX_MIXER_RESET, 0x0000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001200
1201 /* mute and zero volume channels */
Andreas Mohr02330fb2008-05-16 12:18:29 +02001202 for (idx = 0; idx < ARRAY_SIZE(snd_azf3328_init_values); ++idx) {
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001203 snd_azf3328_mixer_outw(chip,
1204 snd_azf3328_init_values[idx][0],
1205 snd_azf3328_init_values[idx][1]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001206 }
Andreas Mohr02330fb2008-05-16 12:18:29 +02001207
Linus Torvalds1da177e2005-04-16 15:20:36 -07001208 /* add mixer controls */
1209 sw = snd_azf3328_mixer_controls;
Andreas Mohr02330fb2008-05-16 12:18:29 +02001210 for (idx = 0; idx < ARRAY_SIZE(snd_azf3328_mixer_controls);
1211 ++idx, ++sw) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001212 if ((err = snd_ctl_add(chip->card, snd_ctl_new1(sw, chip))) < 0)
1213 return err;
1214 }
1215 snd_component_add(card, "AZF3328 mixer");
1216 strcpy(card->mixername, "AZF3328 mixer");
1217
Linus Torvalds1da177e2005-04-16 15:20:36 -07001218 return 0;
1219}
Andreas Mohrb5dc20c2011-02-19 00:49:32 +01001220#endif /* AZF_USE_AC97_LAYER */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001221
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001222static int
Takashi Iwai95de7762005-11-17 15:02:42 +01001223snd_azf3328_hw_params(struct snd_pcm_substream *substream,
1224 struct snd_pcm_hw_params *hw_params)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001225{
Takashi Iwai4162cd32014-02-25 12:59:05 +01001226 return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001227}
1228
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001229static int
Takashi Iwai95de7762005-11-17 15:02:42 +01001230snd_azf3328_hw_free(struct snd_pcm_substream *substream)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001231{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001232 snd_pcm_lib_free_pages(substream);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001233 return 0;
1234}
1235
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001236static void
Andreas Mohrda237f32010-12-27 21:17:26 +01001237snd_azf3328_codec_setfmt(struct snd_azf3328_codec_data *codec,
Andreas Mohr627d3e72008-06-23 11:50:47 +02001238 enum azf_freq_t bitrate,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001239 unsigned int format_width,
1240 unsigned int channels
1241)
1242{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001243 unsigned long flags;
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001244 u16 val = 0xff00;
Andreas Mohr8d9a1142010-12-27 21:16:49 +01001245 u8 freq = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001246
Linus Torvalds1da177e2005-04-16 15:20:36 -07001247 switch (bitrate) {
Andreas Mohrc9ba3742011-01-25 06:46:31 +01001248 case AZF_FREQ_4000: freq = SOUNDFORMAT_FREQ_SUSPECTED_4000; break;
1249 case AZF_FREQ_4800: freq = SOUNDFORMAT_FREQ_SUSPECTED_4800; break;
1250 case AZF_FREQ_5512:
1251 /* the AZF3328 names it "5510" for some strange reason */
1252 freq = SOUNDFORMAT_FREQ_5510; break;
1253 case AZF_FREQ_6620: freq = SOUNDFORMAT_FREQ_6620; break;
1254 case AZF_FREQ_8000: freq = SOUNDFORMAT_FREQ_8000; break;
1255 case AZF_FREQ_9600: freq = SOUNDFORMAT_FREQ_9600; break;
1256 case AZF_FREQ_11025: freq = SOUNDFORMAT_FREQ_11025; break;
1257 case AZF_FREQ_13240: freq = SOUNDFORMAT_FREQ_SUSPECTED_13240; break;
1258 case AZF_FREQ_16000: freq = SOUNDFORMAT_FREQ_16000; break;
1259 case AZF_FREQ_22050: freq = SOUNDFORMAT_FREQ_22050; break;
1260 case AZF_FREQ_32000: freq = SOUNDFORMAT_FREQ_32000; break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001261 default:
Takashi Iwai99b359b2005-10-20 18:26:44 +02001262 snd_printk(KERN_WARNING "unknown bitrate %d, assuming 44.1kHz!\n", bitrate);
Andreas Mohr02330fb2008-05-16 12:18:29 +02001263 /* fall-through */
Andreas Mohrc9ba3742011-01-25 06:46:31 +01001264 case AZF_FREQ_44100: freq = SOUNDFORMAT_FREQ_44100; break;
1265 case AZF_FREQ_48000: freq = SOUNDFORMAT_FREQ_48000; break;
1266 case AZF_FREQ_66200: freq = SOUNDFORMAT_FREQ_SUSPECTED_66200; break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001267 }
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001268 /* val = 0xff07; 3m27.993s (65301Hz; -> 64000Hz???) hmm, 66120, 65967, 66123 */
1269 /* val = 0xff09; 17m15.098s (13123,478Hz; -> 12000Hz???) hmm, 13237.2Hz? */
1270 /* val = 0xff0a; 47m30.599s (4764,891Hz; -> 4800Hz???) yup, 4803Hz */
1271 /* val = 0xff0c; 57m0.510s (4010,263Hz; -> 4000Hz???) yup, 4003Hz */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001272 /* val = 0xff05; 5m11.556s (... -> 44100Hz) */
1273 /* val = 0xff03; 10m21.529s (21872,463Hz; -> 22050Hz???) */
1274 /* val = 0xff0f; 20m41.883s (10937,993Hz; -> 11025Hz???) */
1275 /* val = 0xff0d; 41m23.135s (5523,600Hz; -> 5512Hz???) */
1276 /* val = 0xff0e; 28m30.777s (8017Hz; -> 8000Hz???) */
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001277
Andreas Mohr8d9a1142010-12-27 21:16:49 +01001278 val |= freq;
1279
Linus Torvalds1da177e2005-04-16 15:20:36 -07001280 if (channels == 2)
1281 val |= SOUNDFORMAT_FLAG_2CHANNELS;
1282
1283 if (format_width == 16)
1284 val |= SOUNDFORMAT_FLAG_16BIT;
1285
Andreas Mohrda237f32010-12-27 21:17:26 +01001286 spin_lock_irqsave(codec->lock, flags);
Andreas Mohr02330fb2008-05-16 12:18:29 +02001287
Linus Torvalds1da177e2005-04-16 15:20:36 -07001288 /* set bitrate/format */
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001289 snd_azf3328_codec_outw(codec, IDX_IO_CODEC_SOUNDFORMAT, val);
Andreas Mohr02330fb2008-05-16 12:18:29 +02001290
Linus Torvalds1da177e2005-04-16 15:20:36 -07001291 /* changing the bitrate/format settings switches off the
1292 * audio output with an annoying click in case of 8/16bit format change
1293 * (maybe shutting down DAC/ADC?), thus immediately
1294 * do some tweaking to reenable it and get rid of the clicking
1295 * (FIXME: yes, it works, but what exactly am I doing here?? :)
1296 * FIXME: does this have some side effects for full-duplex
1297 * or other dramatic side effects? */
Andreas Mohradf59312010-12-27 21:16:43 +01001298 /* do it for non-capture codecs only */
Andreas Mohrda237f32010-12-27 21:17:26 +01001299 if (codec->type != AZF_CODEC_CAPTURE)
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001300 snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS,
1301 snd_azf3328_codec_inw(codec, IDX_IO_CODEC_DMA_FLAGS) |
1302 DMA_RUN_SOMETHING1 |
1303 DMA_RUN_SOMETHING2 |
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001304 SOMETHING_ALMOST_ALWAYS_SET |
1305 DMA_EPILOGUE_SOMETHING |
1306 DMA_SOMETHING_ELSE
1307 );
Linus Torvalds1da177e2005-04-16 15:20:36 -07001308
Andreas Mohrda237f32010-12-27 21:17:26 +01001309 spin_unlock_irqrestore(codec->lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001310}
1311
Andreas Mohr02330fb2008-05-16 12:18:29 +02001312static inline void
Andreas Mohrda237f32010-12-27 21:17:26 +01001313snd_azf3328_codec_setfmt_lowpower(struct snd_azf3328_codec_data *codec
Andreas Mohr02330fb2008-05-16 12:18:29 +02001314)
1315{
1316 /* choose lowest frequency for low power consumption.
1317 * While this will cause louder noise due to rather coarse frequency,
1318 * it should never matter since output should always
1319 * get disabled properly when idle anyway. */
Andreas Mohrda237f32010-12-27 21:17:26 +01001320 snd_azf3328_codec_setfmt(codec, AZF_FREQ_4000, 8, 1);
Andreas Mohr02330fb2008-05-16 12:18:29 +02001321}
1322
Andreas Mohr627d3e72008-06-23 11:50:47 +02001323static void
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001324snd_azf3328_ctrl_reg_6AH_update(struct snd_azf3328 *chip,
Andreas Mohr627d3e72008-06-23 11:50:47 +02001325 unsigned bitmask,
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001326 bool enable
Andreas Mohr627d3e72008-06-23 11:50:47 +02001327)
1328{
Andreas Mohr78df6172009-07-12 22:17:54 +02001329 bool do_mask = !enable;
1330 if (do_mask)
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001331 chip->shadow_reg_ctrl_6AH |= bitmask;
Andreas Mohr78df6172009-07-12 22:17:54 +02001332 else
1333 chip->shadow_reg_ctrl_6AH &= ~bitmask;
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001334 dev_dbg(chip->card->dev,
1335 "6AH_update mask 0x%04x do_mask %d: val 0x%04x\n",
1336 bitmask, do_mask, chip->shadow_reg_ctrl_6AH);
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001337 snd_azf3328_ctrl_outw(chip, IDX_IO_6AH, chip->shadow_reg_ctrl_6AH);
Andreas Mohr627d3e72008-06-23 11:50:47 +02001338}
1339
Andreas Mohr02330fb2008-05-16 12:18:29 +02001340static inline void
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001341snd_azf3328_ctrl_enable_codecs(struct snd_azf3328 *chip, bool enable)
Andreas Mohr02330fb2008-05-16 12:18:29 +02001342{
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001343 dev_dbg(chip->card->dev, "codec_enable %d\n", enable);
Andreas Mohr02330fb2008-05-16 12:18:29 +02001344 /* no idea what exactly is being done here, but I strongly assume it's
1345 * PM related */
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001346 snd_azf3328_ctrl_reg_6AH_update(
Andreas Mohr627d3e72008-06-23 11:50:47 +02001347 chip, IO_6A_PAUSE_PLAYBACK_BIT8, enable
Andreas Mohr02330fb2008-05-16 12:18:29 +02001348 );
1349}
1350
1351static void
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001352snd_azf3328_ctrl_codec_activity(struct snd_azf3328 *chip,
1353 enum snd_azf3328_codec_type codec_type,
1354 bool enable
Andreas Mohr02330fb2008-05-16 12:18:29 +02001355)
1356{
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001357 struct snd_azf3328_codec_data *codec = &chip->codecs[codec_type];
1358 bool need_change = (codec->running != enable);
Andreas Mohr02330fb2008-05-16 12:18:29 +02001359
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001360 dev_dbg(chip->card->dev,
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001361 "codec_activity: %s codec, enable %d, need_change %d\n",
1362 codec->name, enable, need_change
Andreas Mohr02330fb2008-05-16 12:18:29 +02001363 );
1364 if (need_change) {
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001365 static const struct {
1366 enum snd_azf3328_codec_type other1;
1367 enum snd_azf3328_codec_type other2;
1368 } peer_codecs[3] =
1369 { { AZF_CODEC_CAPTURE, AZF_CODEC_I2S_OUT },
1370 { AZF_CODEC_PLAYBACK, AZF_CODEC_I2S_OUT },
1371 { AZF_CODEC_PLAYBACK, AZF_CODEC_CAPTURE } };
1372 bool call_function;
1373
1374 if (enable)
1375 /* if enable codec, call enable_codecs func
1376 to enable codec supply... */
1377 call_function = 1;
1378 else {
1379 /* ...otherwise call enable_codecs func
1380 (which globally shuts down operation of codecs)
1381 only in case the other codecs are currently
1382 not active either! */
Andreas Mohr78df6172009-07-12 22:17:54 +02001383 call_function =
1384 ((!chip->codecs[peer_codecs[codec_type].other1]
1385 .running)
1386 && (!chip->codecs[peer_codecs[codec_type].other2]
1387 .running));
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001388 }
1389 if (call_function)
1390 snd_azf3328_ctrl_enable_codecs(chip, enable);
Andreas Mohr02330fb2008-05-16 12:18:29 +02001391
1392 /* ...and adjust clock, too
1393 * (reduce noise and power consumption) */
1394 if (!enable)
Andreas Mohrda237f32010-12-27 21:17:26 +01001395 snd_azf3328_codec_setfmt_lowpower(codec);
Andreas Mohr78df6172009-07-12 22:17:54 +02001396 codec->running = enable;
Andreas Mohr02330fb2008-05-16 12:18:29 +02001397 }
Andreas Mohr02330fb2008-05-16 12:18:29 +02001398}
1399
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001400static void
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001401snd_azf3328_codec_setdmaa(struct snd_azf3328 *chip,
1402 struct snd_azf3328_codec_data *codec,
1403 unsigned long addr,
1404 unsigned int period_bytes,
1405 unsigned int buffer_bytes
Andreas Mohr02330fb2008-05-16 12:18:29 +02001406)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001407{
Andreas Mohr689c6912010-12-27 21:17:35 +01001408 WARN_ONCE(period_bytes & 1, "odd period length!?\n");
1409 WARN_ONCE(buffer_bytes != 2 * period_bytes,
1410 "missed our input expectations! %u vs. %u\n",
1411 buffer_bytes, period_bytes);
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001412 if (!codec->running) {
1413 /* AZF3328 uses a two buffer pointer DMA transfer approach */
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001414
Andreas Mohr689c6912010-12-27 21:17:35 +01001415 unsigned long flags;
Andreas Mohr02330fb2008-05-16 12:18:29 +02001416
1417 /* width 32bit (prevent overflow): */
Andreas Mohr689c6912010-12-27 21:17:35 +01001418 u32 area_length;
1419 struct codec_setup_io {
1420 u32 dma_start_1;
1421 u32 dma_start_2;
1422 u32 dma_lengths;
1423 } __attribute__((packed)) setup_io;
Andreas Mohr02330fb2008-05-16 12:18:29 +02001424
Andreas Mohr689c6912010-12-27 21:17:35 +01001425 area_length = buffer_bytes/2;
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001426
Andreas Mohr689c6912010-12-27 21:17:35 +01001427 setup_io.dma_start_1 = addr;
1428 setup_io.dma_start_2 = addr+area_length;
1429
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001430 dev_dbg(chip->card->dev,
Andreas Mohr689c6912010-12-27 21:17:35 +01001431 "setdma: buffers %08x[%u] / %08x[%u], %u, %u\n",
1432 setup_io.dma_start_1, area_length,
1433 setup_io.dma_start_2, area_length,
1434 period_bytes, buffer_bytes);
1435
1436 /* Hmm, are we really supposed to decrement this by 1??
1437 Most definitely certainly not: configuring full length does
1438 work properly (i.e. likely better), and BTW we
1439 violated possibly differing frame sizes with this...
1440
1441 area_length--; |* max. index *|
1442 */
Andreas Mohr79741502010-11-21 12:09:32 +01001443
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001444 /* build combined I/O buffer length word */
Andreas Mohr689c6912010-12-27 21:17:35 +01001445 setup_io.dma_lengths = (area_length << 16) | (area_length);
1446
Andreas Mohrda237f32010-12-27 21:17:26 +01001447 spin_lock_irqsave(codec->lock, flags);
Andreas Mohr689c6912010-12-27 21:17:35 +01001448 snd_azf3328_codec_outl_multi(
1449 codec, IDX_IO_CODEC_DMA_START_1, &setup_io, 3
1450 );
Andreas Mohrda237f32010-12-27 21:17:26 +01001451 spin_unlock_irqrestore(codec->lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001452 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001453}
1454
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001455static int
Andreas Mohrda237f32010-12-27 21:17:26 +01001456snd_azf3328_pcm_prepare(struct snd_pcm_substream *substream)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001457{
Takashi Iwai95de7762005-11-17 15:02:42 +01001458 struct snd_pcm_runtime *runtime = substream->runtime;
Andreas Mohrda237f32010-12-27 21:17:26 +01001459 struct snd_azf3328_codec_data *codec = runtime->private_data;
Andreas Mohr34585592010-12-27 21:17:11 +01001460#if 0
Linus Torvalds1da177e2005-04-16 15:20:36 -07001461 unsigned int size = snd_pcm_lib_buffer_bytes(substream);
1462 unsigned int count = snd_pcm_lib_period_bytes(substream);
1463#endif
1464
Andreas Mohr34585592010-12-27 21:17:11 +01001465 codec->dma_base = runtime->dma_addr;
1466
Linus Torvalds1da177e2005-04-16 15:20:36 -07001467#if 0
Andreas Mohrda237f32010-12-27 21:17:26 +01001468 snd_azf3328_codec_setfmt(codec,
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001469 runtime->rate,
1470 snd_pcm_format_width(runtime->format),
1471 runtime->channels);
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001472 snd_azf3328_codec_setdmaa(chip, codec,
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001473 runtime->dma_addr, count, size);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001474#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001475 return 0;
1476}
1477
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001478static int
Andreas Mohrda237f32010-12-27 21:17:26 +01001479snd_azf3328_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001480{
Takashi Iwai95de7762005-11-17 15:02:42 +01001481 struct snd_azf3328 *chip = snd_pcm_substream_chip(substream);
1482 struct snd_pcm_runtime *runtime = substream->runtime;
Andreas Mohrda237f32010-12-27 21:17:26 +01001483 struct snd_azf3328_codec_data *codec = runtime->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001484 int result = 0;
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001485 u16 flags1;
Peter Senna Tschudine0f17c72013-09-22 20:44:12 +02001486 bool previously_muted = false;
Andreas Mohrda237f32010-12-27 21:17:26 +01001487 bool is_main_mixer_playback_codec = (AZF_CODEC_PLAYBACK == codec->type);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001488
Linus Torvalds1da177e2005-04-16 15:20:36 -07001489 switch (cmd) {
1490 case SNDRV_PCM_TRIGGER_START:
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001491 dev_dbg(chip->card->dev, "START PCM %s\n", codec->name);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001492
Andreas Mohrda237f32010-12-27 21:17:26 +01001493 if (is_main_mixer_playback_codec) {
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001494 /* mute WaveOut (avoid clicking during setup) */
1495 previously_muted =
Andreas Mohrb5dc20c2011-02-19 00:49:32 +01001496 snd_azf3328_mixer_mute_control_pcm(
1497 chip, 1
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001498 );
1499 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001500
Andreas Mohrda237f32010-12-27 21:17:26 +01001501 snd_azf3328_codec_setfmt(codec,
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001502 runtime->rate,
1503 snd_pcm_format_width(runtime->format),
1504 runtime->channels);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001505
Andreas Mohrda237f32010-12-27 21:17:26 +01001506 spin_lock(codec->lock);
Andreas Mohr02330fb2008-05-16 12:18:29 +02001507 /* first, remember current value: */
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001508 flags1 = snd_azf3328_codec_inw(codec, IDX_IO_CODEC_DMA_FLAGS);
Andreas Mohr02330fb2008-05-16 12:18:29 +02001509
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001510 /* stop transfer */
1511 flags1 &= ~DMA_RESUME;
1512 snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, flags1);
Andreas Mohr02330fb2008-05-16 12:18:29 +02001513
Linus Torvalds1da177e2005-04-16 15:20:36 -07001514 /* FIXME: clear interrupts or what??? */
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001515 snd_azf3328_codec_outw(codec, IDX_IO_CODEC_IRQTYPE, 0xffff);
Andreas Mohrda237f32010-12-27 21:17:26 +01001516 spin_unlock(codec->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001517
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001518 snd_azf3328_codec_setdmaa(chip, codec, runtime->dma_addr,
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001519 snd_pcm_lib_period_bytes(substream),
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001520 snd_pcm_lib_buffer_bytes(substream)
1521 );
Linus Torvalds1da177e2005-04-16 15:20:36 -07001522
Andreas Mohrda237f32010-12-27 21:17:26 +01001523 spin_lock(codec->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001524#ifdef WIN9X
1525 /* FIXME: enable playback/recording??? */
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001526 flags1 |= DMA_RUN_SOMETHING1 | DMA_RUN_SOMETHING2;
1527 snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, flags1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001528
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001529 /* start transfer again */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001530 /* FIXME: what is this value (0x0010)??? */
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001531 flags1 |= DMA_RESUME | DMA_EPILOGUE_SOMETHING;
1532 snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, flags1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001533#else /* NT4 */
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001534 snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS,
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001535 0x0000);
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001536 snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS,
1537 DMA_RUN_SOMETHING1);
1538 snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS,
1539 DMA_RUN_SOMETHING1 |
1540 DMA_RUN_SOMETHING2);
1541 snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS,
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001542 DMA_RESUME |
1543 SOMETHING_ALMOST_ALWAYS_SET |
1544 DMA_EPILOGUE_SOMETHING |
1545 DMA_SOMETHING_ELSE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001546#endif
Andreas Mohrda237f32010-12-27 21:17:26 +01001547 spin_unlock(codec->lock);
1548 snd_azf3328_ctrl_codec_activity(chip, codec->type, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001549
Andreas Mohrda237f32010-12-27 21:17:26 +01001550 if (is_main_mixer_playback_codec) {
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001551 /* now unmute WaveOut */
1552 if (!previously_muted)
Andreas Mohrb5dc20c2011-02-19 00:49:32 +01001553 snd_azf3328_mixer_mute_control_pcm(
1554 chip, 0
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001555 );
1556 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001557
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001558 dev_dbg(chip->card->dev, "PCM STARTED %s\n", codec->name);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001559 break;
Andreas Mohrca54bde2006-05-17 11:02:24 +02001560 case SNDRV_PCM_TRIGGER_RESUME:
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001561 dev_dbg(chip->card->dev, "PCM RESUME %s\n", codec->name);
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001562 /* resume codec if we were active */
Andreas Mohrda237f32010-12-27 21:17:26 +01001563 spin_lock(codec->lock);
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001564 if (codec->running)
1565 snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS,
1566 snd_azf3328_codec_inw(
1567 codec, IDX_IO_CODEC_DMA_FLAGS
1568 ) | DMA_RESUME
1569 );
Andreas Mohrda237f32010-12-27 21:17:26 +01001570 spin_unlock(codec->lock);
Andreas Mohrca54bde2006-05-17 11:02:24 +02001571 break;
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001572 case SNDRV_PCM_TRIGGER_STOP:
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001573 dev_dbg(chip->card->dev, "PCM STOP %s\n", codec->name);
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001574
Andreas Mohrda237f32010-12-27 21:17:26 +01001575 if (is_main_mixer_playback_codec) {
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001576 /* mute WaveOut (avoid clicking during setup) */
1577 previously_muted =
Andreas Mohrb5dc20c2011-02-19 00:49:32 +01001578 snd_azf3328_mixer_mute_control_pcm(
1579 chip, 1
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001580 );
1581 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001582
Andreas Mohrda237f32010-12-27 21:17:26 +01001583 spin_lock(codec->lock);
Andreas Mohr02330fb2008-05-16 12:18:29 +02001584 /* first, remember current value: */
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001585 flags1 = snd_azf3328_codec_inw(codec, IDX_IO_CODEC_DMA_FLAGS);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001586
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001587 /* stop transfer */
1588 flags1 &= ~DMA_RESUME;
1589 snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, flags1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001590
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001591 /* hmm, is this really required? we're resetting the same bit
1592 * immediately thereafter... */
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001593 flags1 |= DMA_RUN_SOMETHING1;
1594 snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, flags1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001595
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001596 flags1 &= ~DMA_RUN_SOMETHING1;
1597 snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, flags1);
Andreas Mohrda237f32010-12-27 21:17:26 +01001598 spin_unlock(codec->lock);
1599 snd_azf3328_ctrl_codec_activity(chip, codec->type, 0);
Andreas Mohr02330fb2008-05-16 12:18:29 +02001600
Andreas Mohrda237f32010-12-27 21:17:26 +01001601 if (is_main_mixer_playback_codec) {
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001602 /* now unmute WaveOut */
1603 if (!previously_muted)
Andreas Mohrb5dc20c2011-02-19 00:49:32 +01001604 snd_azf3328_mixer_mute_control_pcm(
1605 chip, 0
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001606 );
1607 }
Andreas Mohr02330fb2008-05-16 12:18:29 +02001608
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001609 dev_dbg(chip->card->dev, "PCM STOPPED %s\n", codec->name);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001610 break;
Andreas Mohrca54bde2006-05-17 11:02:24 +02001611 case SNDRV_PCM_TRIGGER_SUSPEND:
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001612 dev_dbg(chip->card->dev, "PCM SUSPEND %s\n", codec->name);
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001613 /* make sure codec is stopped */
1614 snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS,
1615 snd_azf3328_codec_inw(
1616 codec, IDX_IO_CODEC_DMA_FLAGS
1617 ) & ~DMA_RESUME
1618 );
Andreas Mohrca54bde2006-05-17 11:02:24 +02001619 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001620 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001621 WARN(1, "FIXME: SNDRV_PCM_TRIGGER_PAUSE_PUSH NIY!\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001622 break;
1623 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001624 WARN(1, "FIXME: SNDRV_PCM_TRIGGER_PAUSE_RELEASE NIY!\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001625 break;
1626 default:
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001627 WARN(1, "FIXME: unknown trigger mode!\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001628 return -EINVAL;
1629 }
Andreas Mohr02330fb2008-05-16 12:18:29 +02001630
Linus Torvalds1da177e2005-04-16 15:20:36 -07001631 return result;
1632}
1633
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001634static snd_pcm_uframes_t
Andreas Mohrda237f32010-12-27 21:17:26 +01001635snd_azf3328_pcm_pointer(struct snd_pcm_substream *substream
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001636)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001637{
Andreas Mohrda237f32010-12-27 21:17:26 +01001638 const struct snd_azf3328_codec_data *codec =
1639 substream->runtime->private_data;
Andreas Mohr34585592010-12-27 21:17:11 +01001640 unsigned long result;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001641 snd_pcm_uframes_t frmres;
1642
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001643 result = snd_azf3328_codec_inl(codec, IDX_IO_CODEC_DMA_CURRPOS);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001644
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001645 /* calculate offset */
Andreas Mohr34585592010-12-27 21:17:11 +01001646#ifdef QUERY_HARDWARE
1647 result -= snd_azf3328_codec_inl(codec, IDX_IO_CODEC_DMA_START_1);
1648#else
1649 result -= codec->dma_base;
1650#endif
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001651 frmres = bytes_to_frames( substream->runtime, result);
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001652 dev_dbg(substream->pcm->card->dev, "%08li %s @ 0x%8lx, frames %8ld\n",
1653 jiffies, codec->name, result, frmres);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001654 return frmres;
1655}
1656
Andreas Mohr02330fb2008-05-16 12:18:29 +02001657/******************************************************************/
1658
1659#ifdef SUPPORT_GAMEPORT
1660static inline void
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001661snd_azf3328_gameport_irq_enable(struct snd_azf3328 *chip,
1662 bool enable
1663)
Andreas Mohr02330fb2008-05-16 12:18:29 +02001664{
1665 snd_azf3328_io_reg_setb(
1666 chip->game_io+IDX_GAME_HWCONFIG,
1667 GAME_HWCFG_IRQ_ENABLE,
1668 enable
1669 );
1670}
1671
1672static inline void
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001673snd_azf3328_gameport_legacy_address_enable(struct snd_azf3328 *chip,
1674 bool enable
1675)
Andreas Mohr02330fb2008-05-16 12:18:29 +02001676{
1677 snd_azf3328_io_reg_setb(
1678 chip->game_io+IDX_GAME_HWCONFIG,
1679 GAME_HWCFG_LEGACY_ADDRESS_ENABLE,
1680 enable
1681 );
1682}
1683
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001684static void
1685snd_azf3328_gameport_set_counter_frequency(struct snd_azf3328 *chip,
1686 unsigned int freq_cfg
1687)
Andreas Mohr02330fb2008-05-16 12:18:29 +02001688{
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001689 snd_azf3328_io_reg_setb(
1690 chip->game_io+IDX_GAME_HWCONFIG,
1691 0x02,
1692 (freq_cfg & 1) != 0
1693 );
1694 snd_azf3328_io_reg_setb(
1695 chip->game_io+IDX_GAME_HWCONFIG,
1696 0x04,
1697 (freq_cfg & 2) != 0
1698 );
1699}
1700
1701static inline void
1702snd_azf3328_gameport_axis_circuit_enable(struct snd_azf3328 *chip, bool enable)
1703{
1704 snd_azf3328_ctrl_reg_6AH_update(
Andreas Mohr627d3e72008-06-23 11:50:47 +02001705 chip, IO_6A_SOMETHING2_GAMEPORT, enable
Andreas Mohr02330fb2008-05-16 12:18:29 +02001706 );
1707}
1708
1709static inline void
1710snd_azf3328_gameport_interrupt(struct snd_azf3328 *chip)
1711{
1712 /*
1713 * skeleton handler only
1714 * (we do not want axis reading in interrupt handler - too much load!)
1715 */
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001716 dev_dbg(chip->card->dev, "gameport irq\n");
Andreas Mohr02330fb2008-05-16 12:18:29 +02001717
1718 /* this should ACK the gameport IRQ properly, hopefully. */
1719 snd_azf3328_game_inw(chip, IDX_GAME_AXIS_VALUE);
1720}
1721
1722static int
1723snd_azf3328_gameport_open(struct gameport *gameport, int mode)
1724{
1725 struct snd_azf3328 *chip = gameport_get_port_data(gameport);
1726 int res;
1727
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001728 dev_dbg(chip->card->dev, "gameport_open, mode %d\n", mode);
Andreas Mohr02330fb2008-05-16 12:18:29 +02001729 switch (mode) {
1730 case GAMEPORT_MODE_COOKED:
1731 case GAMEPORT_MODE_RAW:
1732 res = 0;
1733 break;
1734 default:
1735 res = -1;
1736 break;
1737 }
1738
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001739 snd_azf3328_gameport_set_counter_frequency(chip,
1740 GAME_HWCFG_ADC_COUNTER_FREQ_STD);
Andreas Mohr02330fb2008-05-16 12:18:29 +02001741 snd_azf3328_gameport_axis_circuit_enable(chip, (res == 0));
1742
1743 return res;
1744}
1745
1746static void
1747snd_azf3328_gameport_close(struct gameport *gameport)
1748{
1749 struct snd_azf3328 *chip = gameport_get_port_data(gameport);
1750
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001751 dev_dbg(chip->card->dev, "gameport_close\n");
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001752 snd_azf3328_gameport_set_counter_frequency(chip,
1753 GAME_HWCFG_ADC_COUNTER_FREQ_1_200);
Andreas Mohr02330fb2008-05-16 12:18:29 +02001754 snd_azf3328_gameport_axis_circuit_enable(chip, 0);
1755}
1756
1757static int
1758snd_azf3328_gameport_cooked_read(struct gameport *gameport,
1759 int *axes,
1760 int *buttons
1761)
1762{
1763 struct snd_azf3328 *chip = gameport_get_port_data(gameport);
1764 int i;
1765 u8 val;
1766 unsigned long flags;
1767
Takashi Iwaida3cec32008-08-08 17:12:14 +02001768 if (snd_BUG_ON(!chip))
1769 return 0;
Andreas Mohr02330fb2008-05-16 12:18:29 +02001770
1771 spin_lock_irqsave(&chip->reg_lock, flags);
1772 val = snd_azf3328_game_inb(chip, IDX_GAME_LEGACY_COMPATIBLE);
1773 *buttons = (~(val) >> 4) & 0xf;
1774
1775 /* ok, this one is a bit dirty: cooked_read is being polled by a timer,
1776 * thus we're atomic and cannot actively wait in here
1777 * (which would be useful for us since it probably would be better
1778 * to trigger a measurement in here, then wait a short amount of
1779 * time until it's finished, then read values of _this_ measurement).
1780 *
1781 * Thus we simply resort to reading values if they're available already
1782 * and trigger the next measurement.
1783 */
1784
1785 val = snd_azf3328_game_inb(chip, IDX_GAME_AXES_CONFIG);
1786 if (val & GAME_AXES_SAMPLING_READY) {
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001787 for (i = 0; i < ARRAY_SIZE(chip->axes); ++i) {
Andreas Mohr02330fb2008-05-16 12:18:29 +02001788 /* configure the axis to read */
1789 val = (i << 4) | 0x0f;
1790 snd_azf3328_game_outb(chip, IDX_GAME_AXES_CONFIG, val);
1791
1792 chip->axes[i] = snd_azf3328_game_inw(
1793 chip, IDX_GAME_AXIS_VALUE
1794 );
1795 }
1796 }
1797
Andreas Mohradf59312010-12-27 21:16:43 +01001798 /* trigger next sampling of axes, to be evaluated the next time we
Andreas Mohr02330fb2008-05-16 12:18:29 +02001799 * enter this function */
1800
1801 /* for some very, very strange reason we cannot enable
1802 * Measurement Ready monitoring for all axes here,
1803 * at least not when only one joystick connected */
1804 val = 0x03; /* we're able to monitor axes 1 and 2 only */
1805 snd_azf3328_game_outb(chip, IDX_GAME_AXES_CONFIG, val);
1806
1807 snd_azf3328_game_outw(chip, IDX_GAME_AXIS_VALUE, 0xffff);
1808 spin_unlock_irqrestore(&chip->reg_lock, flags);
1809
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001810 for (i = 0; i < ARRAY_SIZE(chip->axes); i++) {
Andreas Mohr02330fb2008-05-16 12:18:29 +02001811 axes[i] = chip->axes[i];
1812 if (axes[i] == 0xffff)
1813 axes[i] = -1;
1814 }
1815
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001816 dev_dbg(chip->card->dev, "cooked_read: axes %d %d %d %d buttons %d\n",
1817 axes[0], axes[1], axes[2], axes[3], *buttons);
Andreas Mohr02330fb2008-05-16 12:18:29 +02001818
1819 return 0;
1820}
1821
Bill Pembertone23e7a12012-12-06 12:35:10 -05001822static int
Andreas Mohr02330fb2008-05-16 12:18:29 +02001823snd_azf3328_gameport(struct snd_azf3328 *chip, int dev)
1824{
1825 struct gameport *gp;
1826
Andreas Mohr02330fb2008-05-16 12:18:29 +02001827 chip->gameport = gp = gameport_allocate_port();
1828 if (!gp) {
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001829 dev_err(chip->card->dev, "cannot alloc memory for gameport\n");
Andreas Mohr02330fb2008-05-16 12:18:29 +02001830 return -ENOMEM;
1831 }
1832
1833 gameport_set_name(gp, "AZF3328 Gameport");
1834 gameport_set_phys(gp, "pci%s/gameport0", pci_name(chip->pci));
1835 gameport_set_dev_parent(gp, &chip->pci->dev);
Andreas Mohr627d3e72008-06-23 11:50:47 +02001836 gp->io = chip->game_io;
Andreas Mohr02330fb2008-05-16 12:18:29 +02001837 gameport_set_port_data(gp, chip);
1838
1839 gp->open = snd_azf3328_gameport_open;
1840 gp->close = snd_azf3328_gameport_close;
1841 gp->fuzz = 16; /* seems ok */
1842 gp->cooked_read = snd_azf3328_gameport_cooked_read;
1843
1844 /* DISABLE legacy address: we don't need it! */
1845 snd_azf3328_gameport_legacy_address_enable(chip, 0);
1846
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001847 snd_azf3328_gameport_set_counter_frequency(chip,
1848 GAME_HWCFG_ADC_COUNTER_FREQ_1_200);
Andreas Mohr02330fb2008-05-16 12:18:29 +02001849 snd_azf3328_gameport_axis_circuit_enable(chip, 0);
1850
1851 gameport_register_port(chip->gameport);
1852
1853 return 0;
1854}
1855
1856static void
1857snd_azf3328_gameport_free(struct snd_azf3328 *chip)
1858{
1859 if (chip->gameport) {
1860 gameport_unregister_port(chip->gameport);
1861 chip->gameport = NULL;
1862 }
1863 snd_azf3328_gameport_irq_enable(chip, 0);
1864}
1865#else
1866static inline int
1867snd_azf3328_gameport(struct snd_azf3328 *chip, int dev) { return -ENOSYS; }
1868static inline void
1869snd_azf3328_gameport_free(struct snd_azf3328 *chip) { }
1870static inline void
1871snd_azf3328_gameport_interrupt(struct snd_azf3328 *chip)
1872{
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001873 dev_warn(chip->card->dev, "huh, game port IRQ occurred!?\n");
Andreas Mohr02330fb2008-05-16 12:18:29 +02001874}
1875#endif /* SUPPORT_GAMEPORT */
1876
1877/******************************************************************/
1878
Andreas Mohr627d3e72008-06-23 11:50:47 +02001879static inline void
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001880snd_azf3328_irq_log_unknown_type(struct snd_azf3328 *chip, u8 which)
Andreas Mohr627d3e72008-06-23 11:50:47 +02001881{
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001882 dev_dbg(chip->card->dev,
1883 "unknown IRQ type (%x) occurred, please report!\n",
1884 which);
Andreas Mohr627d3e72008-06-23 11:50:47 +02001885}
1886
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001887static inline void
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001888snd_azf3328_pcm_interrupt(struct snd_azf3328 *chip,
1889 const struct snd_azf3328_codec_data *first_codec,
Andreas Mohrda237f32010-12-27 21:17:26 +01001890 u8 status
1891)
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001892{
1893 u8 which;
1894 enum snd_azf3328_codec_type codec_type;
Andreas Mohrda237f32010-12-27 21:17:26 +01001895 const struct snd_azf3328_codec_data *codec = first_codec;
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001896
1897 for (codec_type = AZF_CODEC_PLAYBACK;
1898 codec_type <= AZF_CODEC_I2S_OUT;
Andreas Mohrda237f32010-12-27 21:17:26 +01001899 ++codec_type, ++codec) {
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001900
1901 /* skip codec if there's no interrupt for it */
1902 if (!(status & (1 << codec_type)))
1903 continue;
1904
Andreas Mohrda237f32010-12-27 21:17:26 +01001905 spin_lock(codec->lock);
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001906 which = snd_azf3328_codec_inb(codec, IDX_IO_CODEC_IRQTYPE);
1907 /* ack all IRQ types immediately */
1908 snd_azf3328_codec_outb(codec, IDX_IO_CODEC_IRQTYPE, which);
Andreas Mohrda237f32010-12-27 21:17:26 +01001909 spin_unlock(codec->lock);
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001910
Andreas Mohrda237f32010-12-27 21:17:26 +01001911 if (codec->substream) {
Andreas Mohr78df6172009-07-12 22:17:54 +02001912 snd_pcm_period_elapsed(codec->substream);
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001913 dev_dbg(chip->card->dev, "%s period done (#%x), @ %x\n",
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001914 codec->name,
1915 which,
1916 snd_azf3328_codec_inl(
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001917 codec, IDX_IO_CODEC_DMA_CURRPOS));
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001918 } else
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001919 dev_warn(chip->card->dev, "irq handler problem!\n");
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001920 if (which & IRQ_SOMETHING)
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001921 snd_azf3328_irq_log_unknown_type(chip, which);
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001922 }
1923}
1924
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001925static irqreturn_t
David Howells7d12e782006-10-05 14:55:46 +01001926snd_azf3328_interrupt(int irq, void *dev_id)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001927{
Takashi Iwai95de7762005-11-17 15:02:42 +01001928 struct snd_azf3328 *chip = dev_id;
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001929 u8 status;
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001930 static unsigned long irq_count;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001931
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001932 status = snd_azf3328_ctrl_inb(chip, IDX_IO_IRQSTATUS);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001933
1934 /* fast path out, to ease interrupt sharing */
Andreas Mohr02330fb2008-05-16 12:18:29 +02001935 if (!(status &
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001936 (IRQ_PLAYBACK|IRQ_RECORDING|IRQ_I2S_OUT
1937 |IRQ_GAMEPORT|IRQ_MPU401|IRQ_TIMER)
Andreas Mohr02330fb2008-05-16 12:18:29 +02001938 ))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001939 return IRQ_NONE; /* must be interrupt for another device */
1940
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001941 dev_dbg(chip->card->dev,
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001942 "irq_count %ld! IDX_IO_IRQSTATUS %04x\n",
Andreas Mohr627d3e72008-06-23 11:50:47 +02001943 irq_count++ /* debug-only */,
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001944 status);
Andreas Mohr02330fb2008-05-16 12:18:29 +02001945
Andreas Mohre2f87262006-05-17 11:04:19 +02001946 if (status & IRQ_TIMER) {
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001947 /* dev_dbg(chip->card->dev, "timer %ld\n",
Andreas Mohr02330fb2008-05-16 12:18:29 +02001948 snd_azf3328_codec_inl(chip, IDX_IO_TIMER_VALUE)
1949 & TIMER_VALUE_MASK
1950 ); */
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001951 if (chip->timer)
1952 snd_timer_interrupt(chip->timer, chip->timer->sticks);
1953 /* ACK timer */
1954 spin_lock(&chip->reg_lock);
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001955 snd_azf3328_ctrl_outb(chip, IDX_IO_TIMER_VALUE + 3, 0x07);
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001956 spin_unlock(&chip->reg_lock);
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001957 dev_dbg(chip->card->dev, "timer IRQ\n");
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001958 }
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001959
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001960 if (status & (IRQ_PLAYBACK|IRQ_RECORDING|IRQ_I2S_OUT))
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001961 snd_azf3328_pcm_interrupt(chip, chip->codecs, status);
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001962
Andreas Mohr02330fb2008-05-16 12:18:29 +02001963 if (status & IRQ_GAMEPORT)
1964 snd_azf3328_gameport_interrupt(chip);
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001965
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001966 /* MPU401 has less critical IRQ requirements
1967 * than timer and playback/recording, right? */
Andreas Mohre2f87262006-05-17 11:04:19 +02001968 if (status & IRQ_MPU401) {
David Howells7d12e782006-10-05 14:55:46 +01001969 snd_mpu401_uart_interrupt(irq, chip->rmidi->private_data);
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001970
1971 /* hmm, do we have to ack the IRQ here somehow?
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001972 * If so, then I don't know how yet... */
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001973 dev_dbg(chip->card->dev, "MPU401 IRQ\n");
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001974 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001975 return IRQ_HANDLED;
1976}
1977
1978/*****************************************************************/
1979
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001980/* as long as we think we have identical snd_pcm_hardware parameters
1981 for playback, capture and i2s out, we can use the same physical struct
1982 since the struct is simply being copied into a member.
1983*/
1984static const struct snd_pcm_hardware snd_azf3328_hardware =
Linus Torvalds1da177e2005-04-16 15:20:36 -07001985{
1986 /* FIXME!! Correct? */
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001987 .info = SNDRV_PCM_INFO_MMAP |
1988 SNDRV_PCM_INFO_INTERLEAVED |
1989 SNDRV_PCM_INFO_MMAP_VALID,
1990 .formats = SNDRV_PCM_FMTBIT_S8 |
1991 SNDRV_PCM_FMTBIT_U8 |
1992 SNDRV_PCM_FMTBIT_S16_LE |
1993 SNDRV_PCM_FMTBIT_U16_LE,
1994 .rates = SNDRV_PCM_RATE_5512 |
1995 SNDRV_PCM_RATE_8000_48000 |
1996 SNDRV_PCM_RATE_KNOT,
Andreas Mohr02330fb2008-05-16 12:18:29 +02001997 .rate_min = AZF_FREQ_4000,
1998 .rate_max = AZF_FREQ_66200,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001999 .channels_min = 1,
2000 .channels_max = 2,
Andreas Mohr79741502010-11-21 12:09:32 +01002001 .buffer_bytes_max = (64*1024),
2002 .period_bytes_min = 1024,
2003 .period_bytes_max = (32*1024),
2004 /* We simply have two DMA areas (instead of a list of descriptors
2005 such as other cards); I believe that this is a fixed hardware
2006 attribute and there isn't much driver magic to be done to expand it.
2007 Thus indicate that we have at least and at most 2 periods. */
2008 .periods_min = 2,
2009 .periods_max = 2,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002010 /* FIXME: maybe that card actually has a FIFO?
2011 * Hmm, it seems newer revisions do have one, but we still don't know
2012 * its size... */
2013 .fifo_size = 0,
2014};
2015
Linus Torvalds1da177e2005-04-16 15:20:36 -07002016
2017static unsigned int snd_azf3328_fixed_rates[] = {
Andreas Mohr02330fb2008-05-16 12:18:29 +02002018 AZF_FREQ_4000,
2019 AZF_FREQ_4800,
2020 AZF_FREQ_5512,
2021 AZF_FREQ_6620,
2022 AZF_FREQ_8000,
2023 AZF_FREQ_9600,
2024 AZF_FREQ_11025,
2025 AZF_FREQ_13240,
2026 AZF_FREQ_16000,
2027 AZF_FREQ_22050,
2028 AZF_FREQ_32000,
2029 AZF_FREQ_44100,
2030 AZF_FREQ_48000,
2031 AZF_FREQ_66200
2032};
2033
Takashi Iwai95de7762005-11-17 15:02:42 +01002034static struct snd_pcm_hw_constraint_list snd_azf3328_hw_constraints_rates = {
Andreas Mohr02330fb2008-05-16 12:18:29 +02002035 .count = ARRAY_SIZE(snd_azf3328_fixed_rates),
Linus Torvalds1da177e2005-04-16 15:20:36 -07002036 .list = snd_azf3328_fixed_rates,
2037 .mask = 0,
2038};
2039
2040/*****************************************************************/
2041
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002042static int
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002043snd_azf3328_pcm_open(struct snd_pcm_substream *substream,
2044 enum snd_azf3328_codec_type codec_type
2045)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002046{
Takashi Iwai95de7762005-11-17 15:02:42 +01002047 struct snd_azf3328 *chip = snd_pcm_substream_chip(substream);
2048 struct snd_pcm_runtime *runtime = substream->runtime;
Andreas Mohrda237f32010-12-27 21:17:26 +01002049 struct snd_azf3328_codec_data *codec = &chip->codecs[codec_type];
Linus Torvalds1da177e2005-04-16 15:20:36 -07002050
Andreas Mohrda237f32010-12-27 21:17:26 +01002051 codec->substream = substream;
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002052
2053 /* same parameters for all our codecs - at least we think so... */
2054 runtime->hw = snd_azf3328_hardware;
2055
Linus Torvalds1da177e2005-04-16 15:20:36 -07002056 snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
2057 &snd_azf3328_hw_constraints_rates);
Andreas Mohrda237f32010-12-27 21:17:26 +01002058 runtime->private_data = codec;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002059 return 0;
2060}
2061
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002062static int
Andreas Mohrda237f32010-12-27 21:17:26 +01002063snd_azf3328_pcm_playback_open(struct snd_pcm_substream *substream)
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002064{
2065 return snd_azf3328_pcm_open(substream, AZF_CODEC_PLAYBACK);
2066}
2067
2068static int
Andreas Mohrda237f32010-12-27 21:17:26 +01002069snd_azf3328_pcm_capture_open(struct snd_pcm_substream *substream)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002070{
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002071 return snd_azf3328_pcm_open(substream, AZF_CODEC_CAPTURE);
2072}
2073
2074static int
Andreas Mohrda237f32010-12-27 21:17:26 +01002075snd_azf3328_pcm_i2s_out_open(struct snd_pcm_substream *substream)
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002076{
2077 return snd_azf3328_pcm_open(substream, AZF_CODEC_I2S_OUT);
2078}
2079
2080static int
Andreas Mohrda237f32010-12-27 21:17:26 +01002081snd_azf3328_pcm_close(struct snd_pcm_substream *substream
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002082)
2083{
Andreas Mohrda237f32010-12-27 21:17:26 +01002084 struct snd_azf3328_codec_data *codec =
2085 substream->runtime->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002086
Andreas Mohrda237f32010-12-27 21:17:26 +01002087 codec->substream = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002088 return 0;
2089}
2090
Linus Torvalds1da177e2005-04-16 15:20:36 -07002091/******************************************************************/
2092
Takashi Iwai95de7762005-11-17 15:02:42 +01002093static struct snd_pcm_ops snd_azf3328_playback_ops = {
Andreas Mohrda237f32010-12-27 21:17:26 +01002094 .open = snd_azf3328_pcm_playback_open,
2095 .close = snd_azf3328_pcm_close,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002096 .ioctl = snd_pcm_lib_ioctl,
2097 .hw_params = snd_azf3328_hw_params,
2098 .hw_free = snd_azf3328_hw_free,
Andreas Mohrda237f32010-12-27 21:17:26 +01002099 .prepare = snd_azf3328_pcm_prepare,
2100 .trigger = snd_azf3328_pcm_trigger,
2101 .pointer = snd_azf3328_pcm_pointer
Linus Torvalds1da177e2005-04-16 15:20:36 -07002102};
2103
Takashi Iwai95de7762005-11-17 15:02:42 +01002104static struct snd_pcm_ops snd_azf3328_capture_ops = {
Andreas Mohrda237f32010-12-27 21:17:26 +01002105 .open = snd_azf3328_pcm_capture_open,
2106 .close = snd_azf3328_pcm_close,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002107 .ioctl = snd_pcm_lib_ioctl,
2108 .hw_params = snd_azf3328_hw_params,
2109 .hw_free = snd_azf3328_hw_free,
Andreas Mohrda237f32010-12-27 21:17:26 +01002110 .prepare = snd_azf3328_pcm_prepare,
2111 .trigger = snd_azf3328_pcm_trigger,
2112 .pointer = snd_azf3328_pcm_pointer
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002113};
2114
2115static struct snd_pcm_ops snd_azf3328_i2s_out_ops = {
Andreas Mohrda237f32010-12-27 21:17:26 +01002116 .open = snd_azf3328_pcm_i2s_out_open,
2117 .close = snd_azf3328_pcm_close,
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002118 .ioctl = snd_pcm_lib_ioctl,
2119 .hw_params = snd_azf3328_hw_params,
2120 .hw_free = snd_azf3328_hw_free,
Andreas Mohrda237f32010-12-27 21:17:26 +01002121 .prepare = snd_azf3328_pcm_prepare,
2122 .trigger = snd_azf3328_pcm_trigger,
2123 .pointer = snd_azf3328_pcm_pointer
Linus Torvalds1da177e2005-04-16 15:20:36 -07002124};
2125
Bill Pembertone23e7a12012-12-06 12:35:10 -05002126static int
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002127snd_azf3328_pcm(struct snd_azf3328 *chip)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002128{
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002129enum { AZF_PCMDEV_STD, AZF_PCMDEV_I2S_OUT, NUM_AZF_PCMDEVS }; /* pcm devices */
2130
Takashi Iwai95de7762005-11-17 15:02:42 +01002131 struct snd_pcm *pcm;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002132 int err;
2133
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002134 err = snd_pcm_new(chip->card, "AZF3328 DSP", AZF_PCMDEV_STD,
2135 1, 1, &pcm);
2136 if (err < 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002137 return err;
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002138 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
2139 &snd_azf3328_playback_ops);
2140 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
2141 &snd_azf3328_capture_ops);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002142
2143 pcm->private_data = chip;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002144 pcm->info_flags = 0;
2145 strcpy(pcm->name, chip->card->shortname);
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002146 /* same pcm object for playback/capture (see snd_pcm_new() above) */
2147 chip->pcm[AZF_CODEC_PLAYBACK] = pcm;
2148 chip->pcm[AZF_CODEC_CAPTURE] = pcm;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002149
2150 snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002151 snd_dma_pci_data(chip->pci),
2152 64*1024, 64*1024);
2153
2154 err = snd_pcm_new(chip->card, "AZF3328 I2S OUT", AZF_PCMDEV_I2S_OUT,
2155 1, 0, &pcm);
2156 if (err < 0)
2157 return err;
2158 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
2159 &snd_azf3328_i2s_out_ops);
2160
2161 pcm->private_data = chip;
2162 pcm->info_flags = 0;
2163 strcpy(pcm->name, chip->card->shortname);
2164 chip->pcm[AZF_CODEC_I2S_OUT] = pcm;
2165
2166 snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
2167 snd_dma_pci_data(chip->pci),
2168 64*1024, 64*1024);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002169
Linus Torvalds1da177e2005-04-16 15:20:36 -07002170 return 0;
2171}
2172
2173/******************************************************************/
2174
Andreas Mohr02330fb2008-05-16 12:18:29 +02002175/*** NOTE: the physical timer resolution actually is 1024000 ticks per second
2176 *** (probably derived from main crystal via a divider of 24),
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002177 *** but announcing those attributes to user-space would make programs
2178 *** configure the timer to a 1 tick value, resulting in an absolutely fatal
2179 *** timer IRQ storm.
2180 *** Thus I chose to announce a down-scaled virtual timer to the outside and
2181 *** calculate real timer countdown values internally.
2182 *** (the scale factor can be set via module parameter "seqtimer_scaling").
2183 ***/
2184
2185static int
Takashi Iwai95de7762005-11-17 15:02:42 +01002186snd_azf3328_timer_start(struct snd_timer *timer)
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002187{
Takashi Iwai95de7762005-11-17 15:02:42 +01002188 struct snd_azf3328 *chip;
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002189 unsigned long flags;
2190 unsigned int delay;
2191
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002192 chip = snd_timer_chip(timer);
2193 delay = ((timer->sticks * seqtimer_scaling) - 1) & TIMER_VALUE_MASK;
Andreas Mohre2f87262006-05-17 11:04:19 +02002194 if (delay < 49) {
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002195 /* uhoh, that's not good, since user-space won't know about
2196 * this timing tweak
2197 * (we need to do it to avoid a lockup, though) */
2198
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01002199 dev_dbg(chip->card->dev, "delay was too low (%d)!\n", delay);
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002200 delay = 49; /* minimum time is 49 ticks */
2201 }
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01002202 dev_dbg(chip->card->dev, "setting timer countdown value %d\n", delay);
Andreas Mohr02330fb2008-05-16 12:18:29 +02002203 delay |= TIMER_COUNTDOWN_ENABLE | TIMER_IRQ_ENABLE;
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002204 spin_lock_irqsave(&chip->reg_lock, flags);
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002205 snd_azf3328_ctrl_outl(chip, IDX_IO_TIMER_VALUE, delay);
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002206 spin_unlock_irqrestore(&chip->reg_lock, flags);
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002207 return 0;
2208}
2209
2210static int
Takashi Iwai95de7762005-11-17 15:02:42 +01002211snd_azf3328_timer_stop(struct snd_timer *timer)
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002212{
Takashi Iwai95de7762005-11-17 15:02:42 +01002213 struct snd_azf3328 *chip;
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002214 unsigned long flags;
2215
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002216 chip = snd_timer_chip(timer);
2217 spin_lock_irqsave(&chip->reg_lock, flags);
2218 /* disable timer countdown and interrupt */
Andreas Mohr79741502010-11-21 12:09:32 +01002219 /* Hmm, should we write TIMER_IRQ_ACK here?
2220 YES indeed, otherwise a rogue timer operation - which prompts
2221 ALSA(?) to call repeated stop() in vain, but NOT start() -
2222 will never end (value 0x03 is kept shown in control byte).
2223 Simply manually poking 0x04 _once_ immediately successfully stops
2224 the hardware/ALSA interrupt activity. */
2225 snd_azf3328_ctrl_outb(chip, IDX_IO_TIMER_VALUE + 3, 0x04);
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002226 spin_unlock_irqrestore(&chip->reg_lock, flags);
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002227 return 0;
2228}
2229
2230
2231static int
Takashi Iwai95de7762005-11-17 15:02:42 +01002232snd_azf3328_timer_precise_resolution(struct snd_timer *timer,
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002233 unsigned long *num, unsigned long *den)
2234{
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002235 *num = 1;
2236 *den = 1024000 / seqtimer_scaling;
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002237 return 0;
2238}
2239
Takashi Iwai95de7762005-11-17 15:02:42 +01002240static struct snd_timer_hardware snd_azf3328_timer_hw = {
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002241 .flags = SNDRV_TIMER_HW_AUTO,
2242 .resolution = 977, /* 1000000/1024000 = 0.9765625us */
2243 .ticks = 1024000, /* max tick count, defined by the value register; actually it's not 1024000, but 1048576, but we don't care */
2244 .start = snd_azf3328_timer_start,
2245 .stop = snd_azf3328_timer_stop,
2246 .precise_resolution = snd_azf3328_timer_precise_resolution,
2247};
2248
Bill Pembertone23e7a12012-12-06 12:35:10 -05002249static int
Takashi Iwai95de7762005-11-17 15:02:42 +01002250snd_azf3328_timer(struct snd_azf3328 *chip, int device)
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002251{
Takashi Iwai95de7762005-11-17 15:02:42 +01002252 struct snd_timer *timer = NULL;
2253 struct snd_timer_id tid;
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002254 int err;
2255
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002256 tid.dev_class = SNDRV_TIMER_CLASS_CARD;
2257 tid.dev_sclass = SNDRV_TIMER_SCLASS_NONE;
2258 tid.card = chip->card->number;
2259 tid.device = device;
2260 tid.subdevice = 0;
2261
2262 snd_azf3328_timer_hw.resolution *= seqtimer_scaling;
2263 snd_azf3328_timer_hw.ticks /= seqtimer_scaling;
Andreas Mohr02330fb2008-05-16 12:18:29 +02002264
2265 err = snd_timer_new(chip->card, "AZF3328", &tid, &timer);
2266 if (err < 0)
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002267 goto out;
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002268
2269 strcpy(timer->name, "AZF3328 timer");
2270 timer->private_data = chip;
2271 timer->hw = snd_azf3328_timer_hw;
2272
2273 chip->timer = timer;
2274
Andreas Mohr02330fb2008-05-16 12:18:29 +02002275 snd_azf3328_timer_stop(timer);
2276
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002277 err = 0;
2278
2279out:
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002280 return err;
2281}
2282
2283/******************************************************************/
2284
Andreas Mohr02330fb2008-05-16 12:18:29 +02002285static int
2286snd_azf3328_free(struct snd_azf3328 *chip)
2287{
2288 if (chip->irq < 0)
2289 goto __end_hw;
2290
Andreas Mohrb5dc20c2011-02-19 00:49:32 +01002291 snd_azf3328_mixer_reset(chip);
Andreas Mohr02330fb2008-05-16 12:18:29 +02002292
2293 snd_azf3328_timer_stop(chip->timer);
2294 snd_azf3328_gameport_free(chip);
2295
2296 if (chip->irq >= 0)
2297 synchronize_irq(chip->irq);
2298__end_hw:
2299 if (chip->irq >= 0)
2300 free_irq(chip->irq, chip);
2301 pci_release_regions(chip->pci);
2302 pci_disable_device(chip->pci);
2303
2304 kfree(chip);
2305 return 0;
2306}
2307
2308static int
2309snd_azf3328_dev_free(struct snd_device *device)
2310{
2311 struct snd_azf3328 *chip = device->device_data;
2312 return snd_azf3328_free(chip);
2313}
2314
Linus Torvalds1da177e2005-04-16 15:20:36 -07002315#if 0
2316/* check whether a bit can be modified */
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002317static void
Andreas Mohr02330fb2008-05-16 12:18:29 +02002318snd_azf3328_test_bit(unsigned unsigned reg, int bit)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002319{
2320 unsigned char val, valoff, valon;
2321
2322 val = inb(reg);
2323
2324 outb(val & ~(1 << bit), reg);
2325 valoff = inb(reg);
2326
2327 outb(val|(1 << bit), reg);
2328 valon = inb(reg);
Andreas Mohr02330fb2008-05-16 12:18:29 +02002329
Linus Torvalds1da177e2005-04-16 15:20:36 -07002330 outb(val, reg);
2331
Andreas Mohr78df6172009-07-12 22:17:54 +02002332 printk(KERN_DEBUG "reg %04x bit %d: %02x %02x %02x\n",
Andreas Mohr02330fb2008-05-16 12:18:29 +02002333 reg, bit, val, valoff, valon
2334 );
Linus Torvalds1da177e2005-04-16 15:20:36 -07002335}
2336#endif
2337
Andreas Mohr02330fb2008-05-16 12:18:29 +02002338static inline void
Takashi Iwai95de7762005-11-17 15:02:42 +01002339snd_azf3328_debug_show_ports(const struct snd_azf3328 *chip)
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002340{
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002341 u16 tmp;
2342
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01002343 dev_dbg(chip->card->dev,
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002344 "ctrl_io 0x%lx, game_io 0x%lx, mpu_io 0x%lx, "
Andreas Mohr02330fb2008-05-16 12:18:29 +02002345 "opl3_io 0x%lx, mixer_io 0x%lx, irq %d\n",
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002346 chip->ctrl_io, chip->game_io, chip->mpu_io,
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01002347 chip->opl3_io, chip->mixer_io, chip->irq);
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002348
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01002349 dev_dbg(chip->card->dev,
2350 "game %02x %02x %02x %02x %02x %02x\n",
Andreas Mohr02330fb2008-05-16 12:18:29 +02002351 snd_azf3328_game_inb(chip, 0),
2352 snd_azf3328_game_inb(chip, 1),
2353 snd_azf3328_game_inb(chip, 2),
2354 snd_azf3328_game_inb(chip, 3),
2355 snd_azf3328_game_inb(chip, 4),
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01002356 snd_azf3328_game_inb(chip, 5));
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002357
Andreas Mohr02330fb2008-05-16 12:18:29 +02002358 for (tmp = 0; tmp < 0x07; tmp += 1)
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01002359 dev_dbg(chip->card->dev,
2360 "mpu_io 0x%04x\n", inb(chip->mpu_io + tmp));
Andreas Mohr02330fb2008-05-16 12:18:29 +02002361
2362 for (tmp = 0; tmp <= 0x07; tmp += 1)
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01002363 dev_dbg(chip->card->dev,
2364 "0x%02x: game200 0x%04x, game208 0x%04x\n",
Andreas Mohr02330fb2008-05-16 12:18:29 +02002365 tmp, inb(0x200 + tmp), inb(0x208 + tmp));
2366
2367 for (tmp = 0; tmp <= 0x01; tmp += 1)
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01002368 dev_dbg(chip->card->dev,
Andreas Mohr02330fb2008-05-16 12:18:29 +02002369 "0x%02x: mpu300 0x%04x, mpu310 0x%04x, mpu320 0x%04x, "
2370 "mpu330 0x%04x opl388 0x%04x opl38c 0x%04x\n",
2371 tmp,
2372 inb(0x300 + tmp),
2373 inb(0x310 + tmp),
2374 inb(0x320 + tmp),
2375 inb(0x330 + tmp),
2376 inb(0x388 + tmp),
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01002377 inb(0x38c + tmp));
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002378
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002379 for (tmp = 0; tmp < AZF_IO_SIZE_CTRL; tmp += 2)
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01002380 dev_dbg(chip->card->dev,
2381 "ctrl 0x%02x: 0x%04x\n",
2382 tmp, snd_azf3328_ctrl_inw(chip, tmp));
Andreas Mohre24a1212007-03-26 12:49:45 +02002383
2384 for (tmp = 0; tmp < AZF_IO_SIZE_MIXER; tmp += 2)
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01002385 dev_dbg(chip->card->dev,
2386 "mixer 0x%02x: 0x%04x\n",
2387 tmp, snd_azf3328_mixer_inw(chip, tmp));
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002388}
2389
Bill Pembertone23e7a12012-12-06 12:35:10 -05002390static int
Takashi Iwai95de7762005-11-17 15:02:42 +01002391snd_azf3328_create(struct snd_card *card,
Andreas Mohr02330fb2008-05-16 12:18:29 +02002392 struct pci_dev *pci,
2393 unsigned long device_type,
2394 struct snd_azf3328 **rchip)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002395{
Takashi Iwai95de7762005-11-17 15:02:42 +01002396 struct snd_azf3328 *chip;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002397 int err;
Takashi Iwai95de7762005-11-17 15:02:42 +01002398 static struct snd_device_ops ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002399 .dev_free = snd_azf3328_dev_free,
2400 };
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002401 u8 dma_init;
2402 enum snd_azf3328_codec_type codec_type;
Andreas Mohrda237f32010-12-27 21:17:26 +01002403 struct snd_azf3328_codec_data *codec_setup;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002404
2405 *rchip = NULL;
2406
Andreas Mohr02330fb2008-05-16 12:18:29 +02002407 err = pci_enable_device(pci);
2408 if (err < 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002409 return err;
2410
Takashi Iwaie560d8d2005-09-09 14:21:46 +02002411 chip = kzalloc(sizeof(*chip), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002412 if (chip == NULL) {
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002413 err = -ENOMEM;
2414 goto out_err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002415 }
2416 spin_lock_init(&chip->reg_lock);
2417 chip->card = card;
2418 chip->pci = pci;
2419 chip->irq = -1;
2420
2421 /* check if we can restrict PCI DMA transfers to 24 bits */
Yang Hongyang2f4f27d2009-04-06 19:01:18 -07002422 if (pci_set_dma_mask(pci, DMA_BIT_MASK(24)) < 0 ||
2423 pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(24)) < 0) {
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01002424 dev_err(card->dev,
2425 "architecture does not support 24bit PCI busmaster DMA\n"
Andreas Mohr02330fb2008-05-16 12:18:29 +02002426 );
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002427 err = -ENXIO;
2428 goto out_err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002429 }
2430
Andreas Mohr02330fb2008-05-16 12:18:29 +02002431 err = pci_request_regions(pci, "Aztech AZF3328");
2432 if (err < 0)
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002433 goto out_err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002434
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002435 chip->ctrl_io = pci_resource_start(pci, 0);
Andreas Mohr02330fb2008-05-16 12:18:29 +02002436 chip->game_io = pci_resource_start(pci, 1);
2437 chip->mpu_io = pci_resource_start(pci, 2);
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002438 chip->opl3_io = pci_resource_start(pci, 3);
Andreas Mohr02330fb2008-05-16 12:18:29 +02002439 chip->mixer_io = pci_resource_start(pci, 4);
2440
Andreas Mohr9fd8d362010-12-27 21:17:00 +01002441 codec_setup = &chip->codecs[AZF_CODEC_PLAYBACK];
2442 codec_setup->io_base = chip->ctrl_io + AZF_IO_OFFS_CODEC_PLAYBACK;
Andreas Mohrda237f32010-12-27 21:17:26 +01002443 codec_setup->lock = &chip->reg_lock;
2444 codec_setup->type = AZF_CODEC_PLAYBACK;
Andreas Mohr9fd8d362010-12-27 21:17:00 +01002445 codec_setup->name = "PLAYBACK";
2446
2447 codec_setup = &chip->codecs[AZF_CODEC_CAPTURE];
2448 codec_setup->io_base = chip->ctrl_io + AZF_IO_OFFS_CODEC_CAPTURE;
Andreas Mohrda237f32010-12-27 21:17:26 +01002449 codec_setup->lock = &chip->reg_lock;
2450 codec_setup->type = AZF_CODEC_CAPTURE;
Andreas Mohr9fd8d362010-12-27 21:17:00 +01002451 codec_setup->name = "CAPTURE";
2452
2453 codec_setup = &chip->codecs[AZF_CODEC_I2S_OUT];
2454 codec_setup->io_base = chip->ctrl_io + AZF_IO_OFFS_CODEC_I2S_OUT;
Andreas Mohrda237f32010-12-27 21:17:26 +01002455 codec_setup->lock = &chip->reg_lock;
2456 codec_setup->type = AZF_CODEC_I2S_OUT;
Andreas Mohr9fd8d362010-12-27 21:17:00 +01002457 codec_setup->name = "I2S_OUT";
Linus Torvalds1da177e2005-04-16 15:20:36 -07002458
Takashi Iwai437a5a42006-11-21 12:14:23 +01002459 if (request_irq(pci->irq, snd_azf3328_interrupt,
Takashi Iwai934c2b62011-06-10 16:36:37 +02002460 IRQF_SHARED, KBUILD_MODNAME, chip)) {
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01002461 dev_err(card->dev, "unable to grab IRQ %d\n", pci->irq);
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002462 err = -EBUSY;
2463 goto out_err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002464 }
2465 chip->irq = pci->irq;
2466 pci_set_master(pci);
2467 synchronize_irq(chip->irq);
2468
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002469 snd_azf3328_debug_show_ports(chip);
Andreas Mohr02330fb2008-05-16 12:18:29 +02002470
2471 err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
2472 if (err < 0)
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002473 goto out_err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002474
2475 /* create mixer interface & switches */
Andreas Mohr02330fb2008-05-16 12:18:29 +02002476 err = snd_azf3328_mixer_new(chip);
2477 if (err < 0)
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002478 goto out_err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002479
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002480 /* standard codec init stuff */
2481 /* default DMA init value */
2482 dma_init = DMA_RUN_SOMETHING2|DMA_EPILOGUE_SOMETHING|DMA_SOMETHING_ELSE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002483
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002484 for (codec_type = AZF_CODEC_PLAYBACK;
2485 codec_type <= AZF_CODEC_I2S_OUT; ++codec_type) {
2486 struct snd_azf3328_codec_data *codec =
2487 &chip->codecs[codec_type];
Linus Torvalds1da177e2005-04-16 15:20:36 -07002488
Andreas Mohradf59312010-12-27 21:16:43 +01002489 /* shutdown codecs to reduce power / noise */
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002490 /* have ...ctrl_codec_activity() act properly */
2491 codec->running = 1;
2492 snd_azf3328_ctrl_codec_activity(chip, codec_type, 0);
2493
Andreas Mohrda237f32010-12-27 21:17:26 +01002494 spin_lock_irq(codec->lock);
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002495 snd_azf3328_codec_outb(codec, IDX_IO_CODEC_DMA_FLAGS,
2496 dma_init);
Andreas Mohrda237f32010-12-27 21:17:26 +01002497 spin_unlock_irq(codec->lock);
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002498 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002499
Linus Torvalds1da177e2005-04-16 15:20:36 -07002500 *rchip = chip;
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002501
2502 err = 0;
2503 goto out;
2504
2505out_err:
2506 if (chip)
2507 snd_azf3328_free(chip);
2508 pci_disable_device(pci);
2509
2510out:
2511 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002512}
2513
Bill Pembertone23e7a12012-12-06 12:35:10 -05002514static int
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002515snd_azf3328_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002516{
2517 static int dev;
Takashi Iwai95de7762005-11-17 15:02:42 +01002518 struct snd_card *card;
2519 struct snd_azf3328 *chip;
2520 struct snd_opl3 *opl3;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002521 int err;
2522
Julia Lawalla5a39732011-08-10 11:49:04 +02002523 if (dev >= SNDRV_CARDS) {
2524 err = -ENODEV;
2525 goto out;
2526 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002527 if (!enable[dev]) {
2528 dev++;
Julia Lawalla5a39732011-08-10 11:49:04 +02002529 err = -ENOENT;
2530 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002531 }
2532
Takashi Iwai60c57722014-01-29 14:20:19 +01002533 err = snd_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE,
2534 0, &card);
Takashi Iwaie58de7b2008-12-28 16:44:30 +01002535 if (err < 0)
Julia Lawalla5a39732011-08-10 11:49:04 +02002536 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002537
2538 strcpy(card->driver, "AZF3328");
2539 strcpy(card->shortname, "Aztech AZF3328 (PCI168)");
2540
Andreas Mohr02330fb2008-05-16 12:18:29 +02002541 err = snd_azf3328_create(card, pci, pci_id->driver_data, &chip);
2542 if (err < 0)
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002543 goto out_err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002544
Andreas Mohrca54bde2006-05-17 11:02:24 +02002545 card->private_data = chip;
2546
Andreas Mohr78df6172009-07-12 22:17:54 +02002547 /* chose to use MPU401_HW_AZT2320 ID instead of MPU401_HW_MPU401,
2548 since our hardware ought to be similar, thus use same ID. */
Andreas Mohr02330fb2008-05-16 12:18:29 +02002549 err = snd_mpu401_uart_new(
Andreas Mohr78df6172009-07-12 22:17:54 +02002550 card, 0,
Clemens Ladischdba8b462011-09-13 11:24:41 +02002551 MPU401_HW_AZT2320, chip->mpu_io,
2552 MPU401_INFO_INTEGRATED | MPU401_INFO_IRQ_HOOK,
2553 -1, &chip->rmidi
Andreas Mohr02330fb2008-05-16 12:18:29 +02002554 );
2555 if (err < 0) {
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01002556 dev_err(card->dev, "no MPU-401 device at 0x%lx?\n",
Andreas Mohr02330fb2008-05-16 12:18:29 +02002557 chip->mpu_io
2558 );
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002559 goto out_err;
2560 }
2561
Andreas Mohr02330fb2008-05-16 12:18:29 +02002562 err = snd_azf3328_timer(chip, 0);
2563 if (err < 0)
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002564 goto out_err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002565
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002566 err = snd_azf3328_pcm(chip);
Andreas Mohr02330fb2008-05-16 12:18:29 +02002567 if (err < 0)
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002568 goto out_err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002569
Andreas Mohr02330fb2008-05-16 12:18:29 +02002570 if (snd_opl3_create(card, chip->opl3_io, chip->opl3_io+2,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002571 OPL3_HW_AUTO, 1, &opl3) < 0) {
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01002572 dev_err(card->dev, "no OPL3 device at 0x%lx-0x%lx?\n",
Andreas Mohr02330fb2008-05-16 12:18:29 +02002573 chip->opl3_io, chip->opl3_io+2
2574 );
Linus Torvalds1da177e2005-04-16 15:20:36 -07002575 } else {
Andreas Mohr02330fb2008-05-16 12:18:29 +02002576 /* need to use IDs 1, 2 since ID 0 is snd_azf3328_timer above */
2577 err = snd_opl3_timer_new(opl3, 1, 2);
2578 if (err < 0)
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002579 goto out_err;
Andreas Mohr02330fb2008-05-16 12:18:29 +02002580 err = snd_opl3_hwdep_new(opl3, 0, 1, NULL);
2581 if (err < 0)
2582 goto out_err;
Alban Bedel87c9e7d2012-02-25 16:15:57 +01002583 opl3->private_data = chip;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002584 }
2585
Linus Torvalds1da177e2005-04-16 15:20:36 -07002586 sprintf(card->longname, "%s at 0x%lx, irq %i",
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002587 card->shortname, chip->ctrl_io, chip->irq);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002588
Andreas Mohr02330fb2008-05-16 12:18:29 +02002589 err = snd_card_register(card);
2590 if (err < 0)
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002591 goto out_err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002592
2593#ifdef MODULE
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01002594 dev_info(card->dev,
2595 "Sound driver for Aztech AZF3328-based soundcards such as PCI168.\n");
2596 dev_info(card->dev,
2597 "Hardware was completely undocumented, unfortunately.\n");
2598 dev_info(card->dev,
2599 "Feel free to contact andi AT lisas.de for bug reports etc.!\n");
2600 dev_info(card->dev,
2601 "User-scalable sequencer timer set to %dHz (1024000Hz / %d).\n",
2602 1024000 / seqtimer_scaling, seqtimer_scaling);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002603#endif
2604
Andreas Mohr02330fb2008-05-16 12:18:29 +02002605 snd_azf3328_gameport(chip, dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002606
2607 pci_set_drvdata(pci, card);
2608 dev++;
2609
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002610 err = 0;
2611 goto out;
Andreas Mohr02330fb2008-05-16 12:18:29 +02002612
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002613out_err:
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01002614 dev_err(card->dev, "something failed, exiting\n");
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002615 snd_card_free(card);
Andreas Mohr02330fb2008-05-16 12:18:29 +02002616
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002617out:
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002618 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002619}
2620
Bill Pembertone23e7a12012-12-06 12:35:10 -05002621static void
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002622snd_azf3328_remove(struct pci_dev *pci)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002623{
Linus Torvalds1da177e2005-04-16 15:20:36 -07002624 snd_card_free(pci_get_drvdata(pci));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002625}
2626
Takashi Iwaic7561cd2012-08-14 18:12:04 +02002627#ifdef CONFIG_PM_SLEEP
Andreas Mohr78df6172009-07-12 22:17:54 +02002628static inline void
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01002629snd_azf3328_suspend_regs(const struct snd_azf3328 *chip,
2630 unsigned long io_addr, unsigned count, u32 *saved_regs)
Andreas Mohr78df6172009-07-12 22:17:54 +02002631{
2632 unsigned reg;
2633
2634 for (reg = 0; reg < count; ++reg) {
2635 *saved_regs = inl(io_addr);
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01002636 dev_dbg(chip->card->dev, "suspend: io 0x%04lx: 0x%08x\n",
Andreas Mohr78df6172009-07-12 22:17:54 +02002637 io_addr, *saved_regs);
2638 ++saved_regs;
2639 io_addr += sizeof(*saved_regs);
2640 }
2641}
2642
Andreas Mohrb5dc20c2011-02-19 00:49:32 +01002643static inline void
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01002644snd_azf3328_resume_regs(const struct snd_azf3328 *chip,
2645 const u32 *saved_regs,
Andreas Mohrb5dc20c2011-02-19 00:49:32 +01002646 unsigned long io_addr,
2647 unsigned count
2648)
2649{
2650 unsigned reg;
2651
2652 for (reg = 0; reg < count; ++reg) {
2653 outl(*saved_regs, io_addr);
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01002654 dev_dbg(chip->card->dev,
2655 "resume: io 0x%04lx: 0x%08x --> 0x%08x\n",
Andreas Mohrb5dc20c2011-02-19 00:49:32 +01002656 io_addr, *saved_regs, inl(io_addr));
2657 ++saved_regs;
2658 io_addr += sizeof(*saved_regs);
2659 }
2660}
2661
2662static inline void
2663snd_azf3328_suspend_ac97(struct snd_azf3328 *chip)
2664{
2665#ifdef AZF_USE_AC97_LAYER
2666 snd_ac97_suspend(chip->ac97);
2667#else
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01002668 snd_azf3328_suspend_regs(chip, chip->mixer_io,
Andreas Mohrb5dc20c2011-02-19 00:49:32 +01002669 ARRAY_SIZE(chip->saved_regs_mixer), chip->saved_regs_mixer);
2670
2671 /* make sure to disable master volume etc. to prevent looping sound */
2672 snd_azf3328_mixer_mute_control_master(chip, 1);
2673 snd_azf3328_mixer_mute_control_pcm(chip, 1);
2674#endif /* AZF_USE_AC97_LAYER */
2675}
2676
2677static inline void
2678snd_azf3328_resume_ac97(const struct snd_azf3328 *chip)
2679{
2680#ifdef AZF_USE_AC97_LAYER
2681 snd_ac97_resume(chip->ac97);
2682#else
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01002683 snd_azf3328_resume_regs(chip, chip->saved_regs_mixer, chip->mixer_io,
Andreas Mohrb5dc20c2011-02-19 00:49:32 +01002684 ARRAY_SIZE(chip->saved_regs_mixer));
2685
2686 /* unfortunately with 32bit transfers, IDX_MIXER_PLAY_MASTER (0x02)
2687 and IDX_MIXER_RESET (offset 0x00) get touched at the same time,
2688 resulting in a mixer reset condition persisting until _after_
2689 master vol was restored. Thus master vol needs an extra restore. */
2690 outw(((u16 *)chip->saved_regs_mixer)[1], chip->mixer_io + 2);
2691#endif /* AZF_USE_AC97_LAYER */
2692}
2693
Andreas Mohrca54bde2006-05-17 11:02:24 +02002694static int
Takashi Iwai68cb2b52012-07-02 15:20:37 +02002695snd_azf3328_suspend(struct device *dev)
Andreas Mohrca54bde2006-05-17 11:02:24 +02002696{
Takashi Iwai68cb2b52012-07-02 15:20:37 +02002697 struct pci_dev *pci = to_pci_dev(dev);
2698 struct snd_card *card = dev_get_drvdata(dev);
Andreas Mohrca54bde2006-05-17 11:02:24 +02002699 struct snd_azf3328 *chip = card->private_data;
Andreas Mohr78df6172009-07-12 22:17:54 +02002700 u16 *saved_regs_ctrl_u16;
Andreas Mohrca54bde2006-05-17 11:02:24 +02002701
2702 snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
Andreas Mohr02330fb2008-05-16 12:18:29 +02002703
Andreas Mohradf59312010-12-27 21:16:43 +01002704 /* same pcm object for playback/capture */
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002705 snd_pcm_suspend_all(chip->pcm[AZF_CODEC_PLAYBACK]);
2706 snd_pcm_suspend_all(chip->pcm[AZF_CODEC_I2S_OUT]);
Andreas Mohrca54bde2006-05-17 11:02:24 +02002707
Andreas Mohrb5dc20c2011-02-19 00:49:32 +01002708 snd_azf3328_suspend_ac97(chip);
Andreas Mohr02330fb2008-05-16 12:18:29 +02002709
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01002710 snd_azf3328_suspend_regs(chip, chip->ctrl_io,
Andreas Mohr78df6172009-07-12 22:17:54 +02002711 ARRAY_SIZE(chip->saved_regs_ctrl), chip->saved_regs_ctrl);
Andreas Mohr627d3e72008-06-23 11:50:47 +02002712
2713 /* manually store the one currently relevant write-only reg, too */
Andreas Mohr78df6172009-07-12 22:17:54 +02002714 saved_regs_ctrl_u16 = (u16 *)chip->saved_regs_ctrl;
2715 saved_regs_ctrl_u16[IDX_IO_6AH / 2] = chip->shadow_reg_ctrl_6AH;
Andreas Mohr627d3e72008-06-23 11:50:47 +02002716
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01002717 snd_azf3328_suspend_regs(chip, chip->game_io,
Andreas Mohr78df6172009-07-12 22:17:54 +02002718 ARRAY_SIZE(chip->saved_regs_game), chip->saved_regs_game);
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01002719 snd_azf3328_suspend_regs(chip, chip->mpu_io,
Andreas Mohr78df6172009-07-12 22:17:54 +02002720 ARRAY_SIZE(chip->saved_regs_mpu), chip->saved_regs_mpu);
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01002721 snd_azf3328_suspend_regs(chip, chip->opl3_io,
Andreas Mohr78df6172009-07-12 22:17:54 +02002722 ARRAY_SIZE(chip->saved_regs_opl3), chip->saved_regs_opl3);
Andreas Mohrca54bde2006-05-17 11:02:24 +02002723
Andreas Mohrca54bde2006-05-17 11:02:24 +02002724 pci_disable_device(pci);
2725 pci_save_state(pci);
Takashi Iwai68cb2b52012-07-02 15:20:37 +02002726 pci_set_power_state(pci, PCI_D3hot);
Andreas Mohrca54bde2006-05-17 11:02:24 +02002727 return 0;
2728}
2729
2730static int
Takashi Iwai68cb2b52012-07-02 15:20:37 +02002731snd_azf3328_resume(struct device *dev)
Andreas Mohrca54bde2006-05-17 11:02:24 +02002732{
Takashi Iwai68cb2b52012-07-02 15:20:37 +02002733 struct pci_dev *pci = to_pci_dev(dev);
2734 struct snd_card *card = dev_get_drvdata(dev);
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002735 const struct snd_azf3328 *chip = card->private_data;
Andreas Mohrca54bde2006-05-17 11:02:24 +02002736
Andreas Mohrca54bde2006-05-17 11:02:24 +02002737 pci_set_power_state(pci, PCI_D0);
Takashi Iwai30b35392006-10-11 18:52:53 +02002738 pci_restore_state(pci);
2739 if (pci_enable_device(pci) < 0) {
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01002740 dev_err(dev, "pci_enable_device failed, disabling device\n");
Takashi Iwai30b35392006-10-11 18:52:53 +02002741 snd_card_disconnect(card);
2742 return -EIO;
2743 }
Andreas Mohrca54bde2006-05-17 11:02:24 +02002744 pci_set_master(pci);
2745
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01002746 snd_azf3328_resume_regs(chip, chip->saved_regs_game, chip->game_io,
Andreas Mohr78df6172009-07-12 22:17:54 +02002747 ARRAY_SIZE(chip->saved_regs_game));
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01002748 snd_azf3328_resume_regs(chip, chip->saved_regs_mpu, chip->mpu_io,
Andreas Mohr78df6172009-07-12 22:17:54 +02002749 ARRAY_SIZE(chip->saved_regs_mpu));
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01002750 snd_azf3328_resume_regs(chip, chip->saved_regs_opl3, chip->opl3_io,
Andreas Mohr78df6172009-07-12 22:17:54 +02002751 ARRAY_SIZE(chip->saved_regs_opl3));
2752
Andreas Mohrb5dc20c2011-02-19 00:49:32 +01002753 snd_azf3328_resume_ac97(chip);
Andreas Mohr78df6172009-07-12 22:17:54 +02002754
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01002755 snd_azf3328_resume_regs(chip, chip->saved_regs_ctrl, chip->ctrl_io,
Andreas Mohr78df6172009-07-12 22:17:54 +02002756 ARRAY_SIZE(chip->saved_regs_ctrl));
Andreas Mohrca54bde2006-05-17 11:02:24 +02002757
2758 snd_power_change_state(card, SNDRV_CTL_POWER_D0);
2759 return 0;
2760}
Andreas Mohrca54bde2006-05-17 11:02:24 +02002761
Takashi Iwai68cb2b52012-07-02 15:20:37 +02002762static SIMPLE_DEV_PM_OPS(snd_azf3328_pm, snd_azf3328_suspend, snd_azf3328_resume);
2763#define SND_AZF3328_PM_OPS &snd_azf3328_pm
2764#else
2765#define SND_AZF3328_PM_OPS NULL
Takashi Iwaic7561cd2012-08-14 18:12:04 +02002766#endif /* CONFIG_PM_SLEEP */
Andreas Mohrca54bde2006-05-17 11:02:24 +02002767
Takashi Iwaie9f66d92012-04-24 12:25:00 +02002768static struct pci_driver azf3328_driver = {
Takashi Iwai3733e422011-06-10 16:20:20 +02002769 .name = KBUILD_MODNAME,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002770 .id_table = snd_azf3328_ids,
2771 .probe = snd_azf3328_probe,
Bill Pembertone23e7a12012-12-06 12:35:10 -05002772 .remove = snd_azf3328_remove,
Takashi Iwai68cb2b52012-07-02 15:20:37 +02002773 .driver = {
2774 .pm = SND_AZF3328_PM_OPS,
2775 },
Linus Torvalds1da177e2005-04-16 15:20:36 -07002776};
2777
Takashi Iwaie9f66d92012-04-24 12:25:00 +02002778module_pci_driver(azf3328_driver);