blob: 5a69e26cb2fb43e26b68c365159b3564a7171dde [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);
1037 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
1038 uinfo->count = (reg.reg == IDX_MIXER_REC_SELECT) ? 2 : 1;
1039 uinfo->value.enumerated.items = reg.enum_c;
1040 if (uinfo->value.enumerated.item > reg.enum_c - 1U)
1041 uinfo->value.enumerated.item = reg.enum_c - 1U;
Andreas Mohre2f87262006-05-17 11:04:19 +02001042 if (reg.reg == IDX_MIXER_ADVCTL2) {
Andreas Mohr13769e32006-05-17 11:03:16 +02001043 switch(reg.lchan_shift) {
1044 case 8: /* modem out sel */
Andreas Mohr627d3e72008-06-23 11:50:47 +02001045 p = texts1;
Andreas Mohr13769e32006-05-17 11:03:16 +02001046 break;
1047 case 9: /* mono sel source */
Andreas Mohr627d3e72008-06-23 11:50:47 +02001048 p = texts2;
Andreas Mohr13769e32006-05-17 11:03:16 +02001049 break;
1050 case 15: /* PCM Out Path */
Andreas Mohr627d3e72008-06-23 11:50:47 +02001051 p = texts4;
Andreas Mohr13769e32006-05-17 11:03:16 +02001052 break;
1053 }
Andreas Mohre2f87262006-05-17 11:04:19 +02001054 } else
Andreas Mohr02330fb2008-05-16 12:18:29 +02001055 if (reg.reg == IDX_MIXER_REC_SELECT)
Andreas Mohr627d3e72008-06-23 11:50:47 +02001056 p = texts3;
Andreas Mohr02330fb2008-05-16 12:18:29 +02001057
Andreas Mohr627d3e72008-06-23 11:50:47 +02001058 strcpy(uinfo->value.enumerated.name, p[uinfo->value.enumerated.item]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001059 return 0;
1060}
1061
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001062static int
Takashi Iwai95de7762005-11-17 15:02:42 +01001063snd_azf3328_get_mixer_enum(struct snd_kcontrol *kcontrol,
1064 struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001065{
Takashi Iwai95de7762005-11-17 15:02:42 +01001066 struct snd_azf3328 *chip = snd_kcontrol_chip(kcontrol);
1067 struct azf3328_mixer_reg reg;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001068 unsigned short val;
Andreas Mohr02330fb2008-05-16 12:18:29 +02001069
Linus Torvalds1da177e2005-04-16 15:20:36 -07001070 snd_azf3328_mixer_reg_decode(&reg, kcontrol->private_value);
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001071 val = snd_azf3328_mixer_inw(chip, reg.reg);
Andreas Mohre2f87262006-05-17 11:04:19 +02001072 if (reg.reg == IDX_MIXER_REC_SELECT) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001073 ucontrol->value.enumerated.item[0] = (val >> 8) & (reg.enum_c - 1);
1074 ucontrol->value.enumerated.item[1] = (val >> 0) & (reg.enum_c - 1);
Andreas Mohre2f87262006-05-17 11:04:19 +02001075 } else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001076 ucontrol->value.enumerated.item[0] = (val >> reg.lchan_shift) & (reg.enum_c - 1);
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001077
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001078 dev_dbg(chip->card->dev,
1079 "get_enum: %02x is %04x -> %d|%d (shift %02d, enum_c %d)\n",
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001080 reg.reg, val, ucontrol->value.enumerated.item[0], ucontrol->value.enumerated.item[1],
1081 reg.lchan_shift, reg.enum_c);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001082 return 0;
1083}
1084
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001085static int
Takashi Iwai95de7762005-11-17 15:02:42 +01001086snd_azf3328_put_mixer_enum(struct snd_kcontrol *kcontrol,
1087 struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001088{
Takashi Iwai95de7762005-11-17 15:02:42 +01001089 struct snd_azf3328 *chip = snd_kcontrol_chip(kcontrol);
1090 struct azf3328_mixer_reg reg;
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001091 u16 oreg, nreg, val;
Andreas Mohr02330fb2008-05-16 12:18:29 +02001092
Linus Torvalds1da177e2005-04-16 15:20:36 -07001093 snd_azf3328_mixer_reg_decode(&reg, kcontrol->private_value);
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001094 oreg = snd_azf3328_mixer_inw(chip, reg.reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001095 val = oreg;
Andreas Mohre2f87262006-05-17 11:04:19 +02001096 if (reg.reg == IDX_MIXER_REC_SELECT) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001097 if (ucontrol->value.enumerated.item[0] > reg.enum_c - 1U ||
1098 ucontrol->value.enumerated.item[1] > reg.enum_c - 1U)
1099 return -EINVAL;
1100 val = (ucontrol->value.enumerated.item[0] << 8) |
1101 (ucontrol->value.enumerated.item[1] << 0);
Andreas Mohre2f87262006-05-17 11:04:19 +02001102 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001103 if (ucontrol->value.enumerated.item[0] > reg.enum_c - 1U)
1104 return -EINVAL;
1105 val &= ~((reg.enum_c - 1) << reg.lchan_shift);
1106 val |= (ucontrol->value.enumerated.item[0] << reg.lchan_shift);
1107 }
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001108 snd_azf3328_mixer_outw(chip, reg.reg, val);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001109 nreg = val;
1110
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001111 dev_dbg(chip->card->dev,
1112 "put_enum: %02x to %04x, oreg %04x\n", reg.reg, val, oreg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001113 return (nreg != oreg);
1114}
1115
Bill Pembertone23e7a12012-12-06 12:35:10 -05001116static struct snd_kcontrol_new snd_azf3328_mixer_controls[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001117 AZF3328_MIXER_SWITCH("Master Playback Switch", IDX_MIXER_PLAY_MASTER, 15, 1),
1118 AZF3328_MIXER_VOL_STEREO("Master Playback Volume", IDX_MIXER_PLAY_MASTER, 0x1f, 1),
Andreas Mohr627d3e72008-06-23 11:50:47 +02001119 AZF3328_MIXER_SWITCH("PCM Playback Switch", IDX_MIXER_WAVEOUT, 15, 1),
1120 AZF3328_MIXER_VOL_STEREO("PCM Playback Volume",
1121 IDX_MIXER_WAVEOUT, 0x1f, 1),
1122 AZF3328_MIXER_SWITCH("PCM 3D Bypass Playback Switch",
1123 IDX_MIXER_ADVCTL2, 7, 1),
Linus Torvalds1da177e2005-04-16 15:20:36 -07001124 AZF3328_MIXER_SWITCH("FM Playback Switch", IDX_MIXER_FMSYNTH, 15, 1),
1125 AZF3328_MIXER_VOL_STEREO("FM Playback Volume", IDX_MIXER_FMSYNTH, 0x1f, 1),
1126 AZF3328_MIXER_SWITCH("CD Playback Switch", IDX_MIXER_CDAUDIO, 15, 1),
1127 AZF3328_MIXER_VOL_STEREO("CD Playback Volume", IDX_MIXER_CDAUDIO, 0x1f, 1),
1128 AZF3328_MIXER_SWITCH("Capture Switch", IDX_MIXER_REC_VOLUME, 15, 1),
1129 AZF3328_MIXER_VOL_STEREO("Capture Volume", IDX_MIXER_REC_VOLUME, 0x0f, 0),
1130 AZF3328_MIXER_ENUM("Capture Source", IDX_MIXER_REC_SELECT, 8, 0),
1131 AZF3328_MIXER_SWITCH("Mic Playback Switch", IDX_MIXER_MIC, 15, 1),
1132 AZF3328_MIXER_VOL_MONO("Mic Playback Volume", IDX_MIXER_MIC, 0x1f, 1),
1133 AZF3328_MIXER_SWITCH("Mic Boost (+20dB)", IDX_MIXER_MIC, 6, 0),
1134 AZF3328_MIXER_SWITCH("Line Playback Switch", IDX_MIXER_LINEIN, 15, 1),
1135 AZF3328_MIXER_VOL_STEREO("Line Playback Volume", IDX_MIXER_LINEIN, 0x1f, 1),
Jaroslav Kyselad355c82a2009-11-03 15:47:25 +01001136 AZF3328_MIXER_SWITCH("Beep Playback Switch", IDX_MIXER_PCBEEP, 15, 1),
1137 AZF3328_MIXER_VOL_SPECIAL("Beep Playback Volume", IDX_MIXER_PCBEEP, 0x0f, 1, 1),
Linus Torvalds1da177e2005-04-16 15:20:36 -07001138 AZF3328_MIXER_SWITCH("Video Playback Switch", IDX_MIXER_VIDEO, 15, 1),
1139 AZF3328_MIXER_VOL_STEREO("Video Playback Volume", IDX_MIXER_VIDEO, 0x1f, 1),
1140 AZF3328_MIXER_SWITCH("Aux Playback Switch", IDX_MIXER_AUX, 15, 1),
1141 AZF3328_MIXER_VOL_STEREO("Aux Playback Volume", IDX_MIXER_AUX, 0x1f, 1),
1142 AZF3328_MIXER_SWITCH("Modem Playback Switch", IDX_MIXER_MODEMOUT, 15, 1),
1143 AZF3328_MIXER_VOL_MONO("Modem Playback Volume", IDX_MIXER_MODEMOUT, 0x1f, 1),
1144 AZF3328_MIXER_SWITCH("Modem Capture Switch", IDX_MIXER_MODEMIN, 15, 1),
1145 AZF3328_MIXER_VOL_MONO("Modem Capture Volume", IDX_MIXER_MODEMIN, 0x1f, 1),
Andreas Mohr13769e32006-05-17 11:03:16 +02001146 AZF3328_MIXER_ENUM("Mic Select", IDX_MIXER_ADVCTL2, 2, 8),
1147 AZF3328_MIXER_ENUM("Mono Output Select", IDX_MIXER_ADVCTL2, 2, 9),
Andreas Mohre24a1212007-03-26 12:49:45 +02001148 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 -07001149 AZF3328_MIXER_VOL_SPECIAL("Tone Control - Treble", IDX_MIXER_BASSTREBLE, 0x07, 1, 0),
1150 AZF3328_MIXER_VOL_SPECIAL("Tone Control - Bass", IDX_MIXER_BASSTREBLE, 0x07, 9, 0),
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001151 AZF3328_MIXER_SWITCH("3D Control - Switch", IDX_MIXER_ADVCTL2, 13, 0),
Andreas Mohr13769e32006-05-17 11:03:16 +02001152 AZF3328_MIXER_VOL_SPECIAL("3D Control - Width", IDX_MIXER_ADVCTL1, 0x07, 1, 0), /* "3D Width" */
1153 AZF3328_MIXER_VOL_SPECIAL("3D Control - Depth", IDX_MIXER_ADVCTL1, 0x03, 8, 0), /* "Hifi 3D" */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001154#if MIXER_TESTING
1155 AZF3328_MIXER_SWITCH("0", IDX_MIXER_ADVCTL2, 0, 0),
1156 AZF3328_MIXER_SWITCH("1", IDX_MIXER_ADVCTL2, 1, 0),
1157 AZF3328_MIXER_SWITCH("2", IDX_MIXER_ADVCTL2, 2, 0),
1158 AZF3328_MIXER_SWITCH("3", IDX_MIXER_ADVCTL2, 3, 0),
1159 AZF3328_MIXER_SWITCH("4", IDX_MIXER_ADVCTL2, 4, 0),
1160 AZF3328_MIXER_SWITCH("5", IDX_MIXER_ADVCTL2, 5, 0),
1161 AZF3328_MIXER_SWITCH("6", IDX_MIXER_ADVCTL2, 6, 0),
1162 AZF3328_MIXER_SWITCH("7", IDX_MIXER_ADVCTL2, 7, 0),
1163 AZF3328_MIXER_SWITCH("8", IDX_MIXER_ADVCTL2, 8, 0),
1164 AZF3328_MIXER_SWITCH("9", IDX_MIXER_ADVCTL2, 9, 0),
1165 AZF3328_MIXER_SWITCH("10", IDX_MIXER_ADVCTL2, 10, 0),
1166 AZF3328_MIXER_SWITCH("11", IDX_MIXER_ADVCTL2, 11, 0),
1167 AZF3328_MIXER_SWITCH("12", IDX_MIXER_ADVCTL2, 12, 0),
1168 AZF3328_MIXER_SWITCH("13", IDX_MIXER_ADVCTL2, 13, 0),
1169 AZF3328_MIXER_SWITCH("14", IDX_MIXER_ADVCTL2, 14, 0),
1170 AZF3328_MIXER_SWITCH("15", IDX_MIXER_ADVCTL2, 15, 0),
1171#endif
1172};
1173
Bill Pembertone23e7a12012-12-06 12:35:10 -05001174static u16 snd_azf3328_init_values[][2] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001175 { IDX_MIXER_PLAY_MASTER, MIXER_MUTE_MASK|0x1f1f },
1176 { IDX_MIXER_MODEMOUT, MIXER_MUTE_MASK|0x1f1f },
1177 { IDX_MIXER_BASSTREBLE, 0x0000 },
1178 { IDX_MIXER_PCBEEP, MIXER_MUTE_MASK|0x1f1f },
1179 { IDX_MIXER_MODEMIN, MIXER_MUTE_MASK|0x1f1f },
1180 { IDX_MIXER_MIC, MIXER_MUTE_MASK|0x001f },
1181 { IDX_MIXER_LINEIN, MIXER_MUTE_MASK|0x1f1f },
1182 { IDX_MIXER_CDAUDIO, MIXER_MUTE_MASK|0x1f1f },
1183 { IDX_MIXER_VIDEO, MIXER_MUTE_MASK|0x1f1f },
1184 { IDX_MIXER_AUX, MIXER_MUTE_MASK|0x1f1f },
1185 { IDX_MIXER_WAVEOUT, MIXER_MUTE_MASK|0x1f1f },
1186 { IDX_MIXER_FMSYNTH, MIXER_MUTE_MASK|0x1f1f },
1187 { IDX_MIXER_REC_VOLUME, MIXER_MUTE_MASK|0x0707 },
1188};
1189
Bill Pembertone23e7a12012-12-06 12:35:10 -05001190static int
Takashi Iwai95de7762005-11-17 15:02:42 +01001191snd_azf3328_mixer_new(struct snd_azf3328 *chip)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001192{
Takashi Iwai95de7762005-11-17 15:02:42 +01001193 struct snd_card *card;
1194 const struct snd_kcontrol_new *sw;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001195 unsigned int idx;
1196 int err;
1197
Takashi Iwaida3cec32008-08-08 17:12:14 +02001198 if (snd_BUG_ON(!chip || !chip->card))
1199 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001200
1201 card = chip->card;
1202
1203 /* mixer reset */
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001204 snd_azf3328_mixer_outw(chip, IDX_MIXER_RESET, 0x0000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001205
1206 /* mute and zero volume channels */
Andreas Mohr02330fb2008-05-16 12:18:29 +02001207 for (idx = 0; idx < ARRAY_SIZE(snd_azf3328_init_values); ++idx) {
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001208 snd_azf3328_mixer_outw(chip,
1209 snd_azf3328_init_values[idx][0],
1210 snd_azf3328_init_values[idx][1]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001211 }
Andreas Mohr02330fb2008-05-16 12:18:29 +02001212
Linus Torvalds1da177e2005-04-16 15:20:36 -07001213 /* add mixer controls */
1214 sw = snd_azf3328_mixer_controls;
Andreas Mohr02330fb2008-05-16 12:18:29 +02001215 for (idx = 0; idx < ARRAY_SIZE(snd_azf3328_mixer_controls);
1216 ++idx, ++sw) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001217 if ((err = snd_ctl_add(chip->card, snd_ctl_new1(sw, chip))) < 0)
1218 return err;
1219 }
1220 snd_component_add(card, "AZF3328 mixer");
1221 strcpy(card->mixername, "AZF3328 mixer");
1222
Linus Torvalds1da177e2005-04-16 15:20:36 -07001223 return 0;
1224}
Andreas Mohrb5dc20c2011-02-19 00:49:32 +01001225#endif /* AZF_USE_AC97_LAYER */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001226
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001227static int
Takashi Iwai95de7762005-11-17 15:02:42 +01001228snd_azf3328_hw_params(struct snd_pcm_substream *substream,
1229 struct snd_pcm_hw_params *hw_params)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001230{
Takashi Iwai4162cd32014-02-25 12:59:05 +01001231 return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001232}
1233
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001234static int
Takashi Iwai95de7762005-11-17 15:02:42 +01001235snd_azf3328_hw_free(struct snd_pcm_substream *substream)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001236{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001237 snd_pcm_lib_free_pages(substream);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001238 return 0;
1239}
1240
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001241static void
Andreas Mohrda237f32010-12-27 21:17:26 +01001242snd_azf3328_codec_setfmt(struct snd_azf3328_codec_data *codec,
Andreas Mohr627d3e72008-06-23 11:50:47 +02001243 enum azf_freq_t bitrate,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001244 unsigned int format_width,
1245 unsigned int channels
1246)
1247{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001248 unsigned long flags;
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001249 u16 val = 0xff00;
Andreas Mohr8d9a1142010-12-27 21:16:49 +01001250 u8 freq = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001251
Linus Torvalds1da177e2005-04-16 15:20:36 -07001252 switch (bitrate) {
Andreas Mohrc9ba3742011-01-25 06:46:31 +01001253 case AZF_FREQ_4000: freq = SOUNDFORMAT_FREQ_SUSPECTED_4000; break;
1254 case AZF_FREQ_4800: freq = SOUNDFORMAT_FREQ_SUSPECTED_4800; break;
1255 case AZF_FREQ_5512:
1256 /* the AZF3328 names it "5510" for some strange reason */
1257 freq = SOUNDFORMAT_FREQ_5510; break;
1258 case AZF_FREQ_6620: freq = SOUNDFORMAT_FREQ_6620; break;
1259 case AZF_FREQ_8000: freq = SOUNDFORMAT_FREQ_8000; break;
1260 case AZF_FREQ_9600: freq = SOUNDFORMAT_FREQ_9600; break;
1261 case AZF_FREQ_11025: freq = SOUNDFORMAT_FREQ_11025; break;
1262 case AZF_FREQ_13240: freq = SOUNDFORMAT_FREQ_SUSPECTED_13240; break;
1263 case AZF_FREQ_16000: freq = SOUNDFORMAT_FREQ_16000; break;
1264 case AZF_FREQ_22050: freq = SOUNDFORMAT_FREQ_22050; break;
1265 case AZF_FREQ_32000: freq = SOUNDFORMAT_FREQ_32000; break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001266 default:
Takashi Iwai99b359b2005-10-20 18:26:44 +02001267 snd_printk(KERN_WARNING "unknown bitrate %d, assuming 44.1kHz!\n", bitrate);
Andreas Mohr02330fb2008-05-16 12:18:29 +02001268 /* fall-through */
Andreas Mohrc9ba3742011-01-25 06:46:31 +01001269 case AZF_FREQ_44100: freq = SOUNDFORMAT_FREQ_44100; break;
1270 case AZF_FREQ_48000: freq = SOUNDFORMAT_FREQ_48000; break;
1271 case AZF_FREQ_66200: freq = SOUNDFORMAT_FREQ_SUSPECTED_66200; break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001272 }
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001273 /* val = 0xff07; 3m27.993s (65301Hz; -> 64000Hz???) hmm, 66120, 65967, 66123 */
1274 /* val = 0xff09; 17m15.098s (13123,478Hz; -> 12000Hz???) hmm, 13237.2Hz? */
1275 /* val = 0xff0a; 47m30.599s (4764,891Hz; -> 4800Hz???) yup, 4803Hz */
1276 /* val = 0xff0c; 57m0.510s (4010,263Hz; -> 4000Hz???) yup, 4003Hz */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001277 /* val = 0xff05; 5m11.556s (... -> 44100Hz) */
1278 /* val = 0xff03; 10m21.529s (21872,463Hz; -> 22050Hz???) */
1279 /* val = 0xff0f; 20m41.883s (10937,993Hz; -> 11025Hz???) */
1280 /* val = 0xff0d; 41m23.135s (5523,600Hz; -> 5512Hz???) */
1281 /* val = 0xff0e; 28m30.777s (8017Hz; -> 8000Hz???) */
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001282
Andreas Mohr8d9a1142010-12-27 21:16:49 +01001283 val |= freq;
1284
Linus Torvalds1da177e2005-04-16 15:20:36 -07001285 if (channels == 2)
1286 val |= SOUNDFORMAT_FLAG_2CHANNELS;
1287
1288 if (format_width == 16)
1289 val |= SOUNDFORMAT_FLAG_16BIT;
1290
Andreas Mohrda237f32010-12-27 21:17:26 +01001291 spin_lock_irqsave(codec->lock, flags);
Andreas Mohr02330fb2008-05-16 12:18:29 +02001292
Linus Torvalds1da177e2005-04-16 15:20:36 -07001293 /* set bitrate/format */
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001294 snd_azf3328_codec_outw(codec, IDX_IO_CODEC_SOUNDFORMAT, val);
Andreas Mohr02330fb2008-05-16 12:18:29 +02001295
Linus Torvalds1da177e2005-04-16 15:20:36 -07001296 /* changing the bitrate/format settings switches off the
1297 * audio output with an annoying click in case of 8/16bit format change
1298 * (maybe shutting down DAC/ADC?), thus immediately
1299 * do some tweaking to reenable it and get rid of the clicking
1300 * (FIXME: yes, it works, but what exactly am I doing here?? :)
1301 * FIXME: does this have some side effects for full-duplex
1302 * or other dramatic side effects? */
Andreas Mohradf59312010-12-27 21:16:43 +01001303 /* do it for non-capture codecs only */
Andreas Mohrda237f32010-12-27 21:17:26 +01001304 if (codec->type != AZF_CODEC_CAPTURE)
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001305 snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS,
1306 snd_azf3328_codec_inw(codec, IDX_IO_CODEC_DMA_FLAGS) |
1307 DMA_RUN_SOMETHING1 |
1308 DMA_RUN_SOMETHING2 |
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001309 SOMETHING_ALMOST_ALWAYS_SET |
1310 DMA_EPILOGUE_SOMETHING |
1311 DMA_SOMETHING_ELSE
1312 );
Linus Torvalds1da177e2005-04-16 15:20:36 -07001313
Andreas Mohrda237f32010-12-27 21:17:26 +01001314 spin_unlock_irqrestore(codec->lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001315}
1316
Andreas Mohr02330fb2008-05-16 12:18:29 +02001317static inline void
Andreas Mohrda237f32010-12-27 21:17:26 +01001318snd_azf3328_codec_setfmt_lowpower(struct snd_azf3328_codec_data *codec
Andreas Mohr02330fb2008-05-16 12:18:29 +02001319)
1320{
1321 /* choose lowest frequency for low power consumption.
1322 * While this will cause louder noise due to rather coarse frequency,
1323 * it should never matter since output should always
1324 * get disabled properly when idle anyway. */
Andreas Mohrda237f32010-12-27 21:17:26 +01001325 snd_azf3328_codec_setfmt(codec, AZF_FREQ_4000, 8, 1);
Andreas Mohr02330fb2008-05-16 12:18:29 +02001326}
1327
Andreas Mohr627d3e72008-06-23 11:50:47 +02001328static void
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001329snd_azf3328_ctrl_reg_6AH_update(struct snd_azf3328 *chip,
Andreas Mohr627d3e72008-06-23 11:50:47 +02001330 unsigned bitmask,
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001331 bool enable
Andreas Mohr627d3e72008-06-23 11:50:47 +02001332)
1333{
Andreas Mohr78df6172009-07-12 22:17:54 +02001334 bool do_mask = !enable;
1335 if (do_mask)
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001336 chip->shadow_reg_ctrl_6AH |= bitmask;
Andreas Mohr78df6172009-07-12 22:17:54 +02001337 else
1338 chip->shadow_reg_ctrl_6AH &= ~bitmask;
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001339 dev_dbg(chip->card->dev,
1340 "6AH_update mask 0x%04x do_mask %d: val 0x%04x\n",
1341 bitmask, do_mask, chip->shadow_reg_ctrl_6AH);
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001342 snd_azf3328_ctrl_outw(chip, IDX_IO_6AH, chip->shadow_reg_ctrl_6AH);
Andreas Mohr627d3e72008-06-23 11:50:47 +02001343}
1344
Andreas Mohr02330fb2008-05-16 12:18:29 +02001345static inline void
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001346snd_azf3328_ctrl_enable_codecs(struct snd_azf3328 *chip, bool enable)
Andreas Mohr02330fb2008-05-16 12:18:29 +02001347{
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001348 dev_dbg(chip->card->dev, "codec_enable %d\n", enable);
Andreas Mohr02330fb2008-05-16 12:18:29 +02001349 /* no idea what exactly is being done here, but I strongly assume it's
1350 * PM related */
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001351 snd_azf3328_ctrl_reg_6AH_update(
Andreas Mohr627d3e72008-06-23 11:50:47 +02001352 chip, IO_6A_PAUSE_PLAYBACK_BIT8, enable
Andreas Mohr02330fb2008-05-16 12:18:29 +02001353 );
1354}
1355
1356static void
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001357snd_azf3328_ctrl_codec_activity(struct snd_azf3328 *chip,
1358 enum snd_azf3328_codec_type codec_type,
1359 bool enable
Andreas Mohr02330fb2008-05-16 12:18:29 +02001360)
1361{
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001362 struct snd_azf3328_codec_data *codec = &chip->codecs[codec_type];
1363 bool need_change = (codec->running != enable);
Andreas Mohr02330fb2008-05-16 12:18:29 +02001364
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001365 dev_dbg(chip->card->dev,
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001366 "codec_activity: %s codec, enable %d, need_change %d\n",
1367 codec->name, enable, need_change
Andreas Mohr02330fb2008-05-16 12:18:29 +02001368 );
1369 if (need_change) {
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001370 static const struct {
1371 enum snd_azf3328_codec_type other1;
1372 enum snd_azf3328_codec_type other2;
1373 } peer_codecs[3] =
1374 { { AZF_CODEC_CAPTURE, AZF_CODEC_I2S_OUT },
1375 { AZF_CODEC_PLAYBACK, AZF_CODEC_I2S_OUT },
1376 { AZF_CODEC_PLAYBACK, AZF_CODEC_CAPTURE } };
1377 bool call_function;
1378
1379 if (enable)
1380 /* if enable codec, call enable_codecs func
1381 to enable codec supply... */
1382 call_function = 1;
1383 else {
1384 /* ...otherwise call enable_codecs func
1385 (which globally shuts down operation of codecs)
1386 only in case the other codecs are currently
1387 not active either! */
Andreas Mohr78df6172009-07-12 22:17:54 +02001388 call_function =
1389 ((!chip->codecs[peer_codecs[codec_type].other1]
1390 .running)
1391 && (!chip->codecs[peer_codecs[codec_type].other2]
1392 .running));
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001393 }
1394 if (call_function)
1395 snd_azf3328_ctrl_enable_codecs(chip, enable);
Andreas Mohr02330fb2008-05-16 12:18:29 +02001396
1397 /* ...and adjust clock, too
1398 * (reduce noise and power consumption) */
1399 if (!enable)
Andreas Mohrda237f32010-12-27 21:17:26 +01001400 snd_azf3328_codec_setfmt_lowpower(codec);
Andreas Mohr78df6172009-07-12 22:17:54 +02001401 codec->running = enable;
Andreas Mohr02330fb2008-05-16 12:18:29 +02001402 }
Andreas Mohr02330fb2008-05-16 12:18:29 +02001403}
1404
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001405static void
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001406snd_azf3328_codec_setdmaa(struct snd_azf3328 *chip,
1407 struct snd_azf3328_codec_data *codec,
1408 unsigned long addr,
1409 unsigned int period_bytes,
1410 unsigned int buffer_bytes
Andreas Mohr02330fb2008-05-16 12:18:29 +02001411)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001412{
Andreas Mohr689c6912010-12-27 21:17:35 +01001413 WARN_ONCE(period_bytes & 1, "odd period length!?\n");
1414 WARN_ONCE(buffer_bytes != 2 * period_bytes,
1415 "missed our input expectations! %u vs. %u\n",
1416 buffer_bytes, period_bytes);
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001417 if (!codec->running) {
1418 /* AZF3328 uses a two buffer pointer DMA transfer approach */
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001419
Andreas Mohr689c6912010-12-27 21:17:35 +01001420 unsigned long flags;
Andreas Mohr02330fb2008-05-16 12:18:29 +02001421
1422 /* width 32bit (prevent overflow): */
Andreas Mohr689c6912010-12-27 21:17:35 +01001423 u32 area_length;
1424 struct codec_setup_io {
1425 u32 dma_start_1;
1426 u32 dma_start_2;
1427 u32 dma_lengths;
1428 } __attribute__((packed)) setup_io;
Andreas Mohr02330fb2008-05-16 12:18:29 +02001429
Andreas Mohr689c6912010-12-27 21:17:35 +01001430 area_length = buffer_bytes/2;
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001431
Andreas Mohr689c6912010-12-27 21:17:35 +01001432 setup_io.dma_start_1 = addr;
1433 setup_io.dma_start_2 = addr+area_length;
1434
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001435 dev_dbg(chip->card->dev,
Andreas Mohr689c6912010-12-27 21:17:35 +01001436 "setdma: buffers %08x[%u] / %08x[%u], %u, %u\n",
1437 setup_io.dma_start_1, area_length,
1438 setup_io.dma_start_2, area_length,
1439 period_bytes, buffer_bytes);
1440
1441 /* Hmm, are we really supposed to decrement this by 1??
1442 Most definitely certainly not: configuring full length does
1443 work properly (i.e. likely better), and BTW we
1444 violated possibly differing frame sizes with this...
1445
1446 area_length--; |* max. index *|
1447 */
Andreas Mohr79741502010-11-21 12:09:32 +01001448
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001449 /* build combined I/O buffer length word */
Andreas Mohr689c6912010-12-27 21:17:35 +01001450 setup_io.dma_lengths = (area_length << 16) | (area_length);
1451
Andreas Mohrda237f32010-12-27 21:17:26 +01001452 spin_lock_irqsave(codec->lock, flags);
Andreas Mohr689c6912010-12-27 21:17:35 +01001453 snd_azf3328_codec_outl_multi(
1454 codec, IDX_IO_CODEC_DMA_START_1, &setup_io, 3
1455 );
Andreas Mohrda237f32010-12-27 21:17:26 +01001456 spin_unlock_irqrestore(codec->lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001457 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001458}
1459
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001460static int
Andreas Mohrda237f32010-12-27 21:17:26 +01001461snd_azf3328_pcm_prepare(struct snd_pcm_substream *substream)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001462{
Takashi Iwai95de7762005-11-17 15:02:42 +01001463 struct snd_pcm_runtime *runtime = substream->runtime;
Andreas Mohrda237f32010-12-27 21:17:26 +01001464 struct snd_azf3328_codec_data *codec = runtime->private_data;
Andreas Mohr34585592010-12-27 21:17:11 +01001465#if 0
Linus Torvalds1da177e2005-04-16 15:20:36 -07001466 unsigned int size = snd_pcm_lib_buffer_bytes(substream);
1467 unsigned int count = snd_pcm_lib_period_bytes(substream);
1468#endif
1469
Andreas Mohr34585592010-12-27 21:17:11 +01001470 codec->dma_base = runtime->dma_addr;
1471
Linus Torvalds1da177e2005-04-16 15:20:36 -07001472#if 0
Andreas Mohrda237f32010-12-27 21:17:26 +01001473 snd_azf3328_codec_setfmt(codec,
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001474 runtime->rate,
1475 snd_pcm_format_width(runtime->format),
1476 runtime->channels);
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001477 snd_azf3328_codec_setdmaa(chip, codec,
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001478 runtime->dma_addr, count, size);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001479#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001480 return 0;
1481}
1482
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001483static int
Andreas Mohrda237f32010-12-27 21:17:26 +01001484snd_azf3328_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001485{
Takashi Iwai95de7762005-11-17 15:02:42 +01001486 struct snd_azf3328 *chip = snd_pcm_substream_chip(substream);
1487 struct snd_pcm_runtime *runtime = substream->runtime;
Andreas Mohrda237f32010-12-27 21:17:26 +01001488 struct snd_azf3328_codec_data *codec = runtime->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001489 int result = 0;
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001490 u16 flags1;
Peter Senna Tschudine0f17c72013-09-22 20:44:12 +02001491 bool previously_muted = false;
Andreas Mohrda237f32010-12-27 21:17:26 +01001492 bool is_main_mixer_playback_codec = (AZF_CODEC_PLAYBACK == codec->type);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001493
Linus Torvalds1da177e2005-04-16 15:20:36 -07001494 switch (cmd) {
1495 case SNDRV_PCM_TRIGGER_START:
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001496 dev_dbg(chip->card->dev, "START PCM %s\n", codec->name);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001497
Andreas Mohrda237f32010-12-27 21:17:26 +01001498 if (is_main_mixer_playback_codec) {
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001499 /* mute WaveOut (avoid clicking during setup) */
1500 previously_muted =
Andreas Mohrb5dc20c2011-02-19 00:49:32 +01001501 snd_azf3328_mixer_mute_control_pcm(
1502 chip, 1
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001503 );
1504 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001505
Andreas Mohrda237f32010-12-27 21:17:26 +01001506 snd_azf3328_codec_setfmt(codec,
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001507 runtime->rate,
1508 snd_pcm_format_width(runtime->format),
1509 runtime->channels);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001510
Andreas Mohrda237f32010-12-27 21:17:26 +01001511 spin_lock(codec->lock);
Andreas Mohr02330fb2008-05-16 12:18:29 +02001512 /* first, remember current value: */
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001513 flags1 = snd_azf3328_codec_inw(codec, IDX_IO_CODEC_DMA_FLAGS);
Andreas Mohr02330fb2008-05-16 12:18:29 +02001514
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001515 /* stop transfer */
1516 flags1 &= ~DMA_RESUME;
1517 snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, flags1);
Andreas Mohr02330fb2008-05-16 12:18:29 +02001518
Linus Torvalds1da177e2005-04-16 15:20:36 -07001519 /* FIXME: clear interrupts or what??? */
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001520 snd_azf3328_codec_outw(codec, IDX_IO_CODEC_IRQTYPE, 0xffff);
Andreas Mohrda237f32010-12-27 21:17:26 +01001521 spin_unlock(codec->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001522
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001523 snd_azf3328_codec_setdmaa(chip, codec, runtime->dma_addr,
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001524 snd_pcm_lib_period_bytes(substream),
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001525 snd_pcm_lib_buffer_bytes(substream)
1526 );
Linus Torvalds1da177e2005-04-16 15:20:36 -07001527
Andreas Mohrda237f32010-12-27 21:17:26 +01001528 spin_lock(codec->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001529#ifdef WIN9X
1530 /* FIXME: enable playback/recording??? */
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001531 flags1 |= DMA_RUN_SOMETHING1 | DMA_RUN_SOMETHING2;
1532 snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, flags1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001533
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001534 /* start transfer again */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001535 /* FIXME: what is this value (0x0010)??? */
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001536 flags1 |= DMA_RESUME | DMA_EPILOGUE_SOMETHING;
1537 snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, flags1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001538#else /* NT4 */
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001539 snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS,
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001540 0x0000);
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001541 snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS,
1542 DMA_RUN_SOMETHING1);
1543 snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS,
1544 DMA_RUN_SOMETHING1 |
1545 DMA_RUN_SOMETHING2);
1546 snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS,
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001547 DMA_RESUME |
1548 SOMETHING_ALMOST_ALWAYS_SET |
1549 DMA_EPILOGUE_SOMETHING |
1550 DMA_SOMETHING_ELSE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001551#endif
Andreas Mohrda237f32010-12-27 21:17:26 +01001552 spin_unlock(codec->lock);
1553 snd_azf3328_ctrl_codec_activity(chip, codec->type, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001554
Andreas Mohrda237f32010-12-27 21:17:26 +01001555 if (is_main_mixer_playback_codec) {
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001556 /* now unmute WaveOut */
1557 if (!previously_muted)
Andreas Mohrb5dc20c2011-02-19 00:49:32 +01001558 snd_azf3328_mixer_mute_control_pcm(
1559 chip, 0
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001560 );
1561 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001562
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001563 dev_dbg(chip->card->dev, "PCM STARTED %s\n", codec->name);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001564 break;
Andreas Mohrca54bde2006-05-17 11:02:24 +02001565 case SNDRV_PCM_TRIGGER_RESUME:
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001566 dev_dbg(chip->card->dev, "PCM RESUME %s\n", codec->name);
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001567 /* resume codec if we were active */
Andreas Mohrda237f32010-12-27 21:17:26 +01001568 spin_lock(codec->lock);
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001569 if (codec->running)
1570 snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS,
1571 snd_azf3328_codec_inw(
1572 codec, IDX_IO_CODEC_DMA_FLAGS
1573 ) | DMA_RESUME
1574 );
Andreas Mohrda237f32010-12-27 21:17:26 +01001575 spin_unlock(codec->lock);
Andreas Mohrca54bde2006-05-17 11:02:24 +02001576 break;
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001577 case SNDRV_PCM_TRIGGER_STOP:
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001578 dev_dbg(chip->card->dev, "PCM STOP %s\n", codec->name);
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001579
Andreas Mohrda237f32010-12-27 21:17:26 +01001580 if (is_main_mixer_playback_codec) {
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001581 /* mute WaveOut (avoid clicking during setup) */
1582 previously_muted =
Andreas Mohrb5dc20c2011-02-19 00:49:32 +01001583 snd_azf3328_mixer_mute_control_pcm(
1584 chip, 1
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001585 );
1586 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001587
Andreas Mohrda237f32010-12-27 21:17:26 +01001588 spin_lock(codec->lock);
Andreas Mohr02330fb2008-05-16 12:18:29 +02001589 /* first, remember current value: */
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001590 flags1 = snd_azf3328_codec_inw(codec, IDX_IO_CODEC_DMA_FLAGS);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001591
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001592 /* stop transfer */
1593 flags1 &= ~DMA_RESUME;
1594 snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, flags1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001595
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001596 /* hmm, is this really required? we're resetting the same bit
1597 * immediately thereafter... */
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001598 flags1 |= DMA_RUN_SOMETHING1;
1599 snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, flags1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001600
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001601 flags1 &= ~DMA_RUN_SOMETHING1;
1602 snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, flags1);
Andreas Mohrda237f32010-12-27 21:17:26 +01001603 spin_unlock(codec->lock);
1604 snd_azf3328_ctrl_codec_activity(chip, codec->type, 0);
Andreas Mohr02330fb2008-05-16 12:18:29 +02001605
Andreas Mohrda237f32010-12-27 21:17:26 +01001606 if (is_main_mixer_playback_codec) {
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001607 /* now unmute WaveOut */
1608 if (!previously_muted)
Andreas Mohrb5dc20c2011-02-19 00:49:32 +01001609 snd_azf3328_mixer_mute_control_pcm(
1610 chip, 0
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001611 );
1612 }
Andreas Mohr02330fb2008-05-16 12:18:29 +02001613
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001614 dev_dbg(chip->card->dev, "PCM STOPPED %s\n", codec->name);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001615 break;
Andreas Mohrca54bde2006-05-17 11:02:24 +02001616 case SNDRV_PCM_TRIGGER_SUSPEND:
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001617 dev_dbg(chip->card->dev, "PCM SUSPEND %s\n", codec->name);
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001618 /* make sure codec is stopped */
1619 snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS,
1620 snd_azf3328_codec_inw(
1621 codec, IDX_IO_CODEC_DMA_FLAGS
1622 ) & ~DMA_RESUME
1623 );
Andreas Mohrca54bde2006-05-17 11:02:24 +02001624 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001625 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001626 WARN(1, "FIXME: SNDRV_PCM_TRIGGER_PAUSE_PUSH NIY!\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001627 break;
1628 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001629 WARN(1, "FIXME: SNDRV_PCM_TRIGGER_PAUSE_RELEASE NIY!\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001630 break;
1631 default:
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001632 WARN(1, "FIXME: unknown trigger mode!\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001633 return -EINVAL;
1634 }
Andreas Mohr02330fb2008-05-16 12:18:29 +02001635
Linus Torvalds1da177e2005-04-16 15:20:36 -07001636 return result;
1637}
1638
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001639static snd_pcm_uframes_t
Andreas Mohrda237f32010-12-27 21:17:26 +01001640snd_azf3328_pcm_pointer(struct snd_pcm_substream *substream
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001641)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001642{
Andreas Mohrda237f32010-12-27 21:17:26 +01001643 const struct snd_azf3328_codec_data *codec =
1644 substream->runtime->private_data;
Andreas Mohr34585592010-12-27 21:17:11 +01001645 unsigned long result;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001646 snd_pcm_uframes_t frmres;
1647
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001648 result = snd_azf3328_codec_inl(codec, IDX_IO_CODEC_DMA_CURRPOS);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001649
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001650 /* calculate offset */
Andreas Mohr34585592010-12-27 21:17:11 +01001651#ifdef QUERY_HARDWARE
1652 result -= snd_azf3328_codec_inl(codec, IDX_IO_CODEC_DMA_START_1);
1653#else
1654 result -= codec->dma_base;
1655#endif
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001656 frmres = bytes_to_frames( substream->runtime, result);
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001657 dev_dbg(substream->pcm->card->dev, "%08li %s @ 0x%8lx, frames %8ld\n",
1658 jiffies, codec->name, result, frmres);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001659 return frmres;
1660}
1661
Andreas Mohr02330fb2008-05-16 12:18:29 +02001662/******************************************************************/
1663
1664#ifdef SUPPORT_GAMEPORT
1665static inline void
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001666snd_azf3328_gameport_irq_enable(struct snd_azf3328 *chip,
1667 bool enable
1668)
Andreas Mohr02330fb2008-05-16 12:18:29 +02001669{
1670 snd_azf3328_io_reg_setb(
1671 chip->game_io+IDX_GAME_HWCONFIG,
1672 GAME_HWCFG_IRQ_ENABLE,
1673 enable
1674 );
1675}
1676
1677static inline void
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001678snd_azf3328_gameport_legacy_address_enable(struct snd_azf3328 *chip,
1679 bool enable
1680)
Andreas Mohr02330fb2008-05-16 12:18:29 +02001681{
1682 snd_azf3328_io_reg_setb(
1683 chip->game_io+IDX_GAME_HWCONFIG,
1684 GAME_HWCFG_LEGACY_ADDRESS_ENABLE,
1685 enable
1686 );
1687}
1688
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001689static void
1690snd_azf3328_gameport_set_counter_frequency(struct snd_azf3328 *chip,
1691 unsigned int freq_cfg
1692)
Andreas Mohr02330fb2008-05-16 12:18:29 +02001693{
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001694 snd_azf3328_io_reg_setb(
1695 chip->game_io+IDX_GAME_HWCONFIG,
1696 0x02,
1697 (freq_cfg & 1) != 0
1698 );
1699 snd_azf3328_io_reg_setb(
1700 chip->game_io+IDX_GAME_HWCONFIG,
1701 0x04,
1702 (freq_cfg & 2) != 0
1703 );
1704}
1705
1706static inline void
1707snd_azf3328_gameport_axis_circuit_enable(struct snd_azf3328 *chip, bool enable)
1708{
1709 snd_azf3328_ctrl_reg_6AH_update(
Andreas Mohr627d3e72008-06-23 11:50:47 +02001710 chip, IO_6A_SOMETHING2_GAMEPORT, enable
Andreas Mohr02330fb2008-05-16 12:18:29 +02001711 );
1712}
1713
1714static inline void
1715snd_azf3328_gameport_interrupt(struct snd_azf3328 *chip)
1716{
1717 /*
1718 * skeleton handler only
1719 * (we do not want axis reading in interrupt handler - too much load!)
1720 */
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001721 dev_dbg(chip->card->dev, "gameport irq\n");
Andreas Mohr02330fb2008-05-16 12:18:29 +02001722
1723 /* this should ACK the gameport IRQ properly, hopefully. */
1724 snd_azf3328_game_inw(chip, IDX_GAME_AXIS_VALUE);
1725}
1726
1727static int
1728snd_azf3328_gameport_open(struct gameport *gameport, int mode)
1729{
1730 struct snd_azf3328 *chip = gameport_get_port_data(gameport);
1731 int res;
1732
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001733 dev_dbg(chip->card->dev, "gameport_open, mode %d\n", mode);
Andreas Mohr02330fb2008-05-16 12:18:29 +02001734 switch (mode) {
1735 case GAMEPORT_MODE_COOKED:
1736 case GAMEPORT_MODE_RAW:
1737 res = 0;
1738 break;
1739 default:
1740 res = -1;
1741 break;
1742 }
1743
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001744 snd_azf3328_gameport_set_counter_frequency(chip,
1745 GAME_HWCFG_ADC_COUNTER_FREQ_STD);
Andreas Mohr02330fb2008-05-16 12:18:29 +02001746 snd_azf3328_gameport_axis_circuit_enable(chip, (res == 0));
1747
1748 return res;
1749}
1750
1751static void
1752snd_azf3328_gameport_close(struct gameport *gameport)
1753{
1754 struct snd_azf3328 *chip = gameport_get_port_data(gameport);
1755
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001756 dev_dbg(chip->card->dev, "gameport_close\n");
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001757 snd_azf3328_gameport_set_counter_frequency(chip,
1758 GAME_HWCFG_ADC_COUNTER_FREQ_1_200);
Andreas Mohr02330fb2008-05-16 12:18:29 +02001759 snd_azf3328_gameport_axis_circuit_enable(chip, 0);
1760}
1761
1762static int
1763snd_azf3328_gameport_cooked_read(struct gameport *gameport,
1764 int *axes,
1765 int *buttons
1766)
1767{
1768 struct snd_azf3328 *chip = gameport_get_port_data(gameport);
1769 int i;
1770 u8 val;
1771 unsigned long flags;
1772
Takashi Iwaida3cec32008-08-08 17:12:14 +02001773 if (snd_BUG_ON(!chip))
1774 return 0;
Andreas Mohr02330fb2008-05-16 12:18:29 +02001775
1776 spin_lock_irqsave(&chip->reg_lock, flags);
1777 val = snd_azf3328_game_inb(chip, IDX_GAME_LEGACY_COMPATIBLE);
1778 *buttons = (~(val) >> 4) & 0xf;
1779
1780 /* ok, this one is a bit dirty: cooked_read is being polled by a timer,
1781 * thus we're atomic and cannot actively wait in here
1782 * (which would be useful for us since it probably would be better
1783 * to trigger a measurement in here, then wait a short amount of
1784 * time until it's finished, then read values of _this_ measurement).
1785 *
1786 * Thus we simply resort to reading values if they're available already
1787 * and trigger the next measurement.
1788 */
1789
1790 val = snd_azf3328_game_inb(chip, IDX_GAME_AXES_CONFIG);
1791 if (val & GAME_AXES_SAMPLING_READY) {
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001792 for (i = 0; i < ARRAY_SIZE(chip->axes); ++i) {
Andreas Mohr02330fb2008-05-16 12:18:29 +02001793 /* configure the axis to read */
1794 val = (i << 4) | 0x0f;
1795 snd_azf3328_game_outb(chip, IDX_GAME_AXES_CONFIG, val);
1796
1797 chip->axes[i] = snd_azf3328_game_inw(
1798 chip, IDX_GAME_AXIS_VALUE
1799 );
1800 }
1801 }
1802
Andreas Mohradf59312010-12-27 21:16:43 +01001803 /* trigger next sampling of axes, to be evaluated the next time we
Andreas Mohr02330fb2008-05-16 12:18:29 +02001804 * enter this function */
1805
1806 /* for some very, very strange reason we cannot enable
1807 * Measurement Ready monitoring for all axes here,
1808 * at least not when only one joystick connected */
1809 val = 0x03; /* we're able to monitor axes 1 and 2 only */
1810 snd_azf3328_game_outb(chip, IDX_GAME_AXES_CONFIG, val);
1811
1812 snd_azf3328_game_outw(chip, IDX_GAME_AXIS_VALUE, 0xffff);
1813 spin_unlock_irqrestore(&chip->reg_lock, flags);
1814
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001815 for (i = 0; i < ARRAY_SIZE(chip->axes); i++) {
Andreas Mohr02330fb2008-05-16 12:18:29 +02001816 axes[i] = chip->axes[i];
1817 if (axes[i] == 0xffff)
1818 axes[i] = -1;
1819 }
1820
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001821 dev_dbg(chip->card->dev, "cooked_read: axes %d %d %d %d buttons %d\n",
1822 axes[0], axes[1], axes[2], axes[3], *buttons);
Andreas Mohr02330fb2008-05-16 12:18:29 +02001823
1824 return 0;
1825}
1826
Bill Pembertone23e7a12012-12-06 12:35:10 -05001827static int
Andreas Mohr02330fb2008-05-16 12:18:29 +02001828snd_azf3328_gameport(struct snd_azf3328 *chip, int dev)
1829{
1830 struct gameport *gp;
1831
Andreas Mohr02330fb2008-05-16 12:18:29 +02001832 chip->gameport = gp = gameport_allocate_port();
1833 if (!gp) {
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001834 dev_err(chip->card->dev, "cannot alloc memory for gameport\n");
Andreas Mohr02330fb2008-05-16 12:18:29 +02001835 return -ENOMEM;
1836 }
1837
1838 gameport_set_name(gp, "AZF3328 Gameport");
1839 gameport_set_phys(gp, "pci%s/gameport0", pci_name(chip->pci));
1840 gameport_set_dev_parent(gp, &chip->pci->dev);
Andreas Mohr627d3e72008-06-23 11:50:47 +02001841 gp->io = chip->game_io;
Andreas Mohr02330fb2008-05-16 12:18:29 +02001842 gameport_set_port_data(gp, chip);
1843
1844 gp->open = snd_azf3328_gameport_open;
1845 gp->close = snd_azf3328_gameport_close;
1846 gp->fuzz = 16; /* seems ok */
1847 gp->cooked_read = snd_azf3328_gameport_cooked_read;
1848
1849 /* DISABLE legacy address: we don't need it! */
1850 snd_azf3328_gameport_legacy_address_enable(chip, 0);
1851
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001852 snd_azf3328_gameport_set_counter_frequency(chip,
1853 GAME_HWCFG_ADC_COUNTER_FREQ_1_200);
Andreas Mohr02330fb2008-05-16 12:18:29 +02001854 snd_azf3328_gameport_axis_circuit_enable(chip, 0);
1855
1856 gameport_register_port(chip->gameport);
1857
1858 return 0;
1859}
1860
1861static void
1862snd_azf3328_gameport_free(struct snd_azf3328 *chip)
1863{
1864 if (chip->gameport) {
1865 gameport_unregister_port(chip->gameport);
1866 chip->gameport = NULL;
1867 }
1868 snd_azf3328_gameport_irq_enable(chip, 0);
1869}
1870#else
1871static inline int
1872snd_azf3328_gameport(struct snd_azf3328 *chip, int dev) { return -ENOSYS; }
1873static inline void
1874snd_azf3328_gameport_free(struct snd_azf3328 *chip) { }
1875static inline void
1876snd_azf3328_gameport_interrupt(struct snd_azf3328 *chip)
1877{
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001878 dev_warn(chip->card->dev, "huh, game port IRQ occurred!?\n");
Andreas Mohr02330fb2008-05-16 12:18:29 +02001879}
1880#endif /* SUPPORT_GAMEPORT */
1881
1882/******************************************************************/
1883
Andreas Mohr627d3e72008-06-23 11:50:47 +02001884static inline void
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001885snd_azf3328_irq_log_unknown_type(struct snd_azf3328 *chip, u8 which)
Andreas Mohr627d3e72008-06-23 11:50:47 +02001886{
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001887 dev_dbg(chip->card->dev,
1888 "unknown IRQ type (%x) occurred, please report!\n",
1889 which);
Andreas Mohr627d3e72008-06-23 11:50:47 +02001890}
1891
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001892static inline void
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001893snd_azf3328_pcm_interrupt(struct snd_azf3328 *chip,
1894 const struct snd_azf3328_codec_data *first_codec,
Andreas Mohrda237f32010-12-27 21:17:26 +01001895 u8 status
1896)
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001897{
1898 u8 which;
1899 enum snd_azf3328_codec_type codec_type;
Andreas Mohrda237f32010-12-27 21:17:26 +01001900 const struct snd_azf3328_codec_data *codec = first_codec;
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001901
1902 for (codec_type = AZF_CODEC_PLAYBACK;
1903 codec_type <= AZF_CODEC_I2S_OUT;
Andreas Mohrda237f32010-12-27 21:17:26 +01001904 ++codec_type, ++codec) {
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001905
1906 /* skip codec if there's no interrupt for it */
1907 if (!(status & (1 << codec_type)))
1908 continue;
1909
Andreas Mohrda237f32010-12-27 21:17:26 +01001910 spin_lock(codec->lock);
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001911 which = snd_azf3328_codec_inb(codec, IDX_IO_CODEC_IRQTYPE);
1912 /* ack all IRQ types immediately */
1913 snd_azf3328_codec_outb(codec, IDX_IO_CODEC_IRQTYPE, which);
Andreas Mohrda237f32010-12-27 21:17:26 +01001914 spin_unlock(codec->lock);
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001915
Andreas Mohrda237f32010-12-27 21:17:26 +01001916 if (codec->substream) {
Andreas Mohr78df6172009-07-12 22:17:54 +02001917 snd_pcm_period_elapsed(codec->substream);
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001918 dev_dbg(chip->card->dev, "%s period done (#%x), @ %x\n",
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001919 codec->name,
1920 which,
1921 snd_azf3328_codec_inl(
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001922 codec, IDX_IO_CODEC_DMA_CURRPOS));
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001923 } else
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001924 dev_warn(chip->card->dev, "irq handler problem!\n");
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001925 if (which & IRQ_SOMETHING)
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001926 snd_azf3328_irq_log_unknown_type(chip, which);
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001927 }
1928}
1929
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001930static irqreturn_t
David Howells7d12e782006-10-05 14:55:46 +01001931snd_azf3328_interrupt(int irq, void *dev_id)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001932{
Takashi Iwai95de7762005-11-17 15:02:42 +01001933 struct snd_azf3328 *chip = dev_id;
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001934 u8 status;
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001935 static unsigned long irq_count;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001936
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001937 status = snd_azf3328_ctrl_inb(chip, IDX_IO_IRQSTATUS);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001938
1939 /* fast path out, to ease interrupt sharing */
Andreas Mohr02330fb2008-05-16 12:18:29 +02001940 if (!(status &
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001941 (IRQ_PLAYBACK|IRQ_RECORDING|IRQ_I2S_OUT
1942 |IRQ_GAMEPORT|IRQ_MPU401|IRQ_TIMER)
Andreas Mohr02330fb2008-05-16 12:18:29 +02001943 ))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001944 return IRQ_NONE; /* must be interrupt for another device */
1945
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001946 dev_dbg(chip->card->dev,
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001947 "irq_count %ld! IDX_IO_IRQSTATUS %04x\n",
Andreas Mohr627d3e72008-06-23 11:50:47 +02001948 irq_count++ /* debug-only */,
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001949 status);
Andreas Mohr02330fb2008-05-16 12:18:29 +02001950
Andreas Mohre2f87262006-05-17 11:04:19 +02001951 if (status & IRQ_TIMER) {
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001952 /* dev_dbg(chip->card->dev, "timer %ld\n",
Andreas Mohr02330fb2008-05-16 12:18:29 +02001953 snd_azf3328_codec_inl(chip, IDX_IO_TIMER_VALUE)
1954 & TIMER_VALUE_MASK
1955 ); */
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001956 if (chip->timer)
1957 snd_timer_interrupt(chip->timer, chip->timer->sticks);
1958 /* ACK timer */
1959 spin_lock(&chip->reg_lock);
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001960 snd_azf3328_ctrl_outb(chip, IDX_IO_TIMER_VALUE + 3, 0x07);
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001961 spin_unlock(&chip->reg_lock);
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001962 dev_dbg(chip->card->dev, "timer IRQ\n");
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001963 }
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001964
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001965 if (status & (IRQ_PLAYBACK|IRQ_RECORDING|IRQ_I2S_OUT))
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001966 snd_azf3328_pcm_interrupt(chip, chip->codecs, status);
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001967
Andreas Mohr02330fb2008-05-16 12:18:29 +02001968 if (status & IRQ_GAMEPORT)
1969 snd_azf3328_gameport_interrupt(chip);
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001970
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001971 /* MPU401 has less critical IRQ requirements
1972 * than timer and playback/recording, right? */
Andreas Mohre2f87262006-05-17 11:04:19 +02001973 if (status & IRQ_MPU401) {
David Howells7d12e782006-10-05 14:55:46 +01001974 snd_mpu401_uart_interrupt(irq, chip->rmidi->private_data);
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001975
1976 /* hmm, do we have to ack the IRQ here somehow?
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001977 * If so, then I don't know how yet... */
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001978 dev_dbg(chip->card->dev, "MPU401 IRQ\n");
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001979 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001980 return IRQ_HANDLED;
1981}
1982
1983/*****************************************************************/
1984
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001985/* as long as we think we have identical snd_pcm_hardware parameters
1986 for playback, capture and i2s out, we can use the same physical struct
1987 since the struct is simply being copied into a member.
1988*/
1989static const struct snd_pcm_hardware snd_azf3328_hardware =
Linus Torvalds1da177e2005-04-16 15:20:36 -07001990{
1991 /* FIXME!! Correct? */
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001992 .info = SNDRV_PCM_INFO_MMAP |
1993 SNDRV_PCM_INFO_INTERLEAVED |
1994 SNDRV_PCM_INFO_MMAP_VALID,
1995 .formats = SNDRV_PCM_FMTBIT_S8 |
1996 SNDRV_PCM_FMTBIT_U8 |
1997 SNDRV_PCM_FMTBIT_S16_LE |
1998 SNDRV_PCM_FMTBIT_U16_LE,
1999 .rates = SNDRV_PCM_RATE_5512 |
2000 SNDRV_PCM_RATE_8000_48000 |
2001 SNDRV_PCM_RATE_KNOT,
Andreas Mohr02330fb2008-05-16 12:18:29 +02002002 .rate_min = AZF_FREQ_4000,
2003 .rate_max = AZF_FREQ_66200,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002004 .channels_min = 1,
2005 .channels_max = 2,
Andreas Mohr79741502010-11-21 12:09:32 +01002006 .buffer_bytes_max = (64*1024),
2007 .period_bytes_min = 1024,
2008 .period_bytes_max = (32*1024),
2009 /* We simply have two DMA areas (instead of a list of descriptors
2010 such as other cards); I believe that this is a fixed hardware
2011 attribute and there isn't much driver magic to be done to expand it.
2012 Thus indicate that we have at least and at most 2 periods. */
2013 .periods_min = 2,
2014 .periods_max = 2,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002015 /* FIXME: maybe that card actually has a FIFO?
2016 * Hmm, it seems newer revisions do have one, but we still don't know
2017 * its size... */
2018 .fifo_size = 0,
2019};
2020
Linus Torvalds1da177e2005-04-16 15:20:36 -07002021
2022static unsigned int snd_azf3328_fixed_rates[] = {
Andreas Mohr02330fb2008-05-16 12:18:29 +02002023 AZF_FREQ_4000,
2024 AZF_FREQ_4800,
2025 AZF_FREQ_5512,
2026 AZF_FREQ_6620,
2027 AZF_FREQ_8000,
2028 AZF_FREQ_9600,
2029 AZF_FREQ_11025,
2030 AZF_FREQ_13240,
2031 AZF_FREQ_16000,
2032 AZF_FREQ_22050,
2033 AZF_FREQ_32000,
2034 AZF_FREQ_44100,
2035 AZF_FREQ_48000,
2036 AZF_FREQ_66200
2037};
2038
Takashi Iwai95de7762005-11-17 15:02:42 +01002039static struct snd_pcm_hw_constraint_list snd_azf3328_hw_constraints_rates = {
Andreas Mohr02330fb2008-05-16 12:18:29 +02002040 .count = ARRAY_SIZE(snd_azf3328_fixed_rates),
Linus Torvalds1da177e2005-04-16 15:20:36 -07002041 .list = snd_azf3328_fixed_rates,
2042 .mask = 0,
2043};
2044
2045/*****************************************************************/
2046
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002047static int
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002048snd_azf3328_pcm_open(struct snd_pcm_substream *substream,
2049 enum snd_azf3328_codec_type codec_type
2050)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002051{
Takashi Iwai95de7762005-11-17 15:02:42 +01002052 struct snd_azf3328 *chip = snd_pcm_substream_chip(substream);
2053 struct snd_pcm_runtime *runtime = substream->runtime;
Andreas Mohrda237f32010-12-27 21:17:26 +01002054 struct snd_azf3328_codec_data *codec = &chip->codecs[codec_type];
Linus Torvalds1da177e2005-04-16 15:20:36 -07002055
Andreas Mohrda237f32010-12-27 21:17:26 +01002056 codec->substream = substream;
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002057
2058 /* same parameters for all our codecs - at least we think so... */
2059 runtime->hw = snd_azf3328_hardware;
2060
Linus Torvalds1da177e2005-04-16 15:20:36 -07002061 snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
2062 &snd_azf3328_hw_constraints_rates);
Andreas Mohrda237f32010-12-27 21:17:26 +01002063 runtime->private_data = codec;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002064 return 0;
2065}
2066
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002067static int
Andreas Mohrda237f32010-12-27 21:17:26 +01002068snd_azf3328_pcm_playback_open(struct snd_pcm_substream *substream)
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002069{
2070 return snd_azf3328_pcm_open(substream, AZF_CODEC_PLAYBACK);
2071}
2072
2073static int
Andreas Mohrda237f32010-12-27 21:17:26 +01002074snd_azf3328_pcm_capture_open(struct snd_pcm_substream *substream)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002075{
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002076 return snd_azf3328_pcm_open(substream, AZF_CODEC_CAPTURE);
2077}
2078
2079static int
Andreas Mohrda237f32010-12-27 21:17:26 +01002080snd_azf3328_pcm_i2s_out_open(struct snd_pcm_substream *substream)
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002081{
2082 return snd_azf3328_pcm_open(substream, AZF_CODEC_I2S_OUT);
2083}
2084
2085static int
Andreas Mohrda237f32010-12-27 21:17:26 +01002086snd_azf3328_pcm_close(struct snd_pcm_substream *substream
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002087)
2088{
Andreas Mohrda237f32010-12-27 21:17:26 +01002089 struct snd_azf3328_codec_data *codec =
2090 substream->runtime->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002091
Andreas Mohrda237f32010-12-27 21:17:26 +01002092 codec->substream = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002093 return 0;
2094}
2095
Linus Torvalds1da177e2005-04-16 15:20:36 -07002096/******************************************************************/
2097
Takashi Iwai95de7762005-11-17 15:02:42 +01002098static struct snd_pcm_ops snd_azf3328_playback_ops = {
Andreas Mohrda237f32010-12-27 21:17:26 +01002099 .open = snd_azf3328_pcm_playback_open,
2100 .close = snd_azf3328_pcm_close,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002101 .ioctl = snd_pcm_lib_ioctl,
2102 .hw_params = snd_azf3328_hw_params,
2103 .hw_free = snd_azf3328_hw_free,
Andreas Mohrda237f32010-12-27 21:17:26 +01002104 .prepare = snd_azf3328_pcm_prepare,
2105 .trigger = snd_azf3328_pcm_trigger,
2106 .pointer = snd_azf3328_pcm_pointer
Linus Torvalds1da177e2005-04-16 15:20:36 -07002107};
2108
Takashi Iwai95de7762005-11-17 15:02:42 +01002109static struct snd_pcm_ops snd_azf3328_capture_ops = {
Andreas Mohrda237f32010-12-27 21:17:26 +01002110 .open = snd_azf3328_pcm_capture_open,
2111 .close = snd_azf3328_pcm_close,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002112 .ioctl = snd_pcm_lib_ioctl,
2113 .hw_params = snd_azf3328_hw_params,
2114 .hw_free = snd_azf3328_hw_free,
Andreas Mohrda237f32010-12-27 21:17:26 +01002115 .prepare = snd_azf3328_pcm_prepare,
2116 .trigger = snd_azf3328_pcm_trigger,
2117 .pointer = snd_azf3328_pcm_pointer
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002118};
2119
2120static struct snd_pcm_ops snd_azf3328_i2s_out_ops = {
Andreas Mohrda237f32010-12-27 21:17:26 +01002121 .open = snd_azf3328_pcm_i2s_out_open,
2122 .close = snd_azf3328_pcm_close,
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002123 .ioctl = snd_pcm_lib_ioctl,
2124 .hw_params = snd_azf3328_hw_params,
2125 .hw_free = snd_azf3328_hw_free,
Andreas Mohrda237f32010-12-27 21:17:26 +01002126 .prepare = snd_azf3328_pcm_prepare,
2127 .trigger = snd_azf3328_pcm_trigger,
2128 .pointer = snd_azf3328_pcm_pointer
Linus Torvalds1da177e2005-04-16 15:20:36 -07002129};
2130
Bill Pembertone23e7a12012-12-06 12:35:10 -05002131static int
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002132snd_azf3328_pcm(struct snd_azf3328 *chip)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002133{
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002134enum { AZF_PCMDEV_STD, AZF_PCMDEV_I2S_OUT, NUM_AZF_PCMDEVS }; /* pcm devices */
2135
Takashi Iwai95de7762005-11-17 15:02:42 +01002136 struct snd_pcm *pcm;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002137 int err;
2138
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002139 err = snd_pcm_new(chip->card, "AZF3328 DSP", AZF_PCMDEV_STD,
2140 1, 1, &pcm);
2141 if (err < 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002142 return err;
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002143 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
2144 &snd_azf3328_playback_ops);
2145 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
2146 &snd_azf3328_capture_ops);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002147
2148 pcm->private_data = chip;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002149 pcm->info_flags = 0;
2150 strcpy(pcm->name, chip->card->shortname);
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002151 /* same pcm object for playback/capture (see snd_pcm_new() above) */
2152 chip->pcm[AZF_CODEC_PLAYBACK] = pcm;
2153 chip->pcm[AZF_CODEC_CAPTURE] = pcm;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002154
2155 snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002156 snd_dma_pci_data(chip->pci),
2157 64*1024, 64*1024);
2158
2159 err = snd_pcm_new(chip->card, "AZF3328 I2S OUT", AZF_PCMDEV_I2S_OUT,
2160 1, 0, &pcm);
2161 if (err < 0)
2162 return err;
2163 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
2164 &snd_azf3328_i2s_out_ops);
2165
2166 pcm->private_data = chip;
2167 pcm->info_flags = 0;
2168 strcpy(pcm->name, chip->card->shortname);
2169 chip->pcm[AZF_CODEC_I2S_OUT] = pcm;
2170
2171 snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
2172 snd_dma_pci_data(chip->pci),
2173 64*1024, 64*1024);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002174
Linus Torvalds1da177e2005-04-16 15:20:36 -07002175 return 0;
2176}
2177
2178/******************************************************************/
2179
Andreas Mohr02330fb2008-05-16 12:18:29 +02002180/*** NOTE: the physical timer resolution actually is 1024000 ticks per second
2181 *** (probably derived from main crystal via a divider of 24),
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002182 *** but announcing those attributes to user-space would make programs
2183 *** configure the timer to a 1 tick value, resulting in an absolutely fatal
2184 *** timer IRQ storm.
2185 *** Thus I chose to announce a down-scaled virtual timer to the outside and
2186 *** calculate real timer countdown values internally.
2187 *** (the scale factor can be set via module parameter "seqtimer_scaling").
2188 ***/
2189
2190static int
Takashi Iwai95de7762005-11-17 15:02:42 +01002191snd_azf3328_timer_start(struct snd_timer *timer)
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002192{
Takashi Iwai95de7762005-11-17 15:02:42 +01002193 struct snd_azf3328 *chip;
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002194 unsigned long flags;
2195 unsigned int delay;
2196
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002197 chip = snd_timer_chip(timer);
2198 delay = ((timer->sticks * seqtimer_scaling) - 1) & TIMER_VALUE_MASK;
Andreas Mohre2f87262006-05-17 11:04:19 +02002199 if (delay < 49) {
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002200 /* uhoh, that's not good, since user-space won't know about
2201 * this timing tweak
2202 * (we need to do it to avoid a lockup, though) */
2203
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01002204 dev_dbg(chip->card->dev, "delay was too low (%d)!\n", delay);
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002205 delay = 49; /* minimum time is 49 ticks */
2206 }
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01002207 dev_dbg(chip->card->dev, "setting timer countdown value %d\n", delay);
Andreas Mohr02330fb2008-05-16 12:18:29 +02002208 delay |= TIMER_COUNTDOWN_ENABLE | TIMER_IRQ_ENABLE;
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002209 spin_lock_irqsave(&chip->reg_lock, flags);
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002210 snd_azf3328_ctrl_outl(chip, IDX_IO_TIMER_VALUE, delay);
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002211 spin_unlock_irqrestore(&chip->reg_lock, flags);
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002212 return 0;
2213}
2214
2215static int
Takashi Iwai95de7762005-11-17 15:02:42 +01002216snd_azf3328_timer_stop(struct snd_timer *timer)
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002217{
Takashi Iwai95de7762005-11-17 15:02:42 +01002218 struct snd_azf3328 *chip;
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002219 unsigned long flags;
2220
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002221 chip = snd_timer_chip(timer);
2222 spin_lock_irqsave(&chip->reg_lock, flags);
2223 /* disable timer countdown and interrupt */
Andreas Mohr79741502010-11-21 12:09:32 +01002224 /* Hmm, should we write TIMER_IRQ_ACK here?
2225 YES indeed, otherwise a rogue timer operation - which prompts
2226 ALSA(?) to call repeated stop() in vain, but NOT start() -
2227 will never end (value 0x03 is kept shown in control byte).
2228 Simply manually poking 0x04 _once_ immediately successfully stops
2229 the hardware/ALSA interrupt activity. */
2230 snd_azf3328_ctrl_outb(chip, IDX_IO_TIMER_VALUE + 3, 0x04);
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002231 spin_unlock_irqrestore(&chip->reg_lock, flags);
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002232 return 0;
2233}
2234
2235
2236static int
Takashi Iwai95de7762005-11-17 15:02:42 +01002237snd_azf3328_timer_precise_resolution(struct snd_timer *timer,
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002238 unsigned long *num, unsigned long *den)
2239{
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002240 *num = 1;
2241 *den = 1024000 / seqtimer_scaling;
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002242 return 0;
2243}
2244
Takashi Iwai95de7762005-11-17 15:02:42 +01002245static struct snd_timer_hardware snd_azf3328_timer_hw = {
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002246 .flags = SNDRV_TIMER_HW_AUTO,
2247 .resolution = 977, /* 1000000/1024000 = 0.9765625us */
2248 .ticks = 1024000, /* max tick count, defined by the value register; actually it's not 1024000, but 1048576, but we don't care */
2249 .start = snd_azf3328_timer_start,
2250 .stop = snd_azf3328_timer_stop,
2251 .precise_resolution = snd_azf3328_timer_precise_resolution,
2252};
2253
Bill Pembertone23e7a12012-12-06 12:35:10 -05002254static int
Takashi Iwai95de7762005-11-17 15:02:42 +01002255snd_azf3328_timer(struct snd_azf3328 *chip, int device)
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002256{
Takashi Iwai95de7762005-11-17 15:02:42 +01002257 struct snd_timer *timer = NULL;
2258 struct snd_timer_id tid;
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002259 int err;
2260
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002261 tid.dev_class = SNDRV_TIMER_CLASS_CARD;
2262 tid.dev_sclass = SNDRV_TIMER_SCLASS_NONE;
2263 tid.card = chip->card->number;
2264 tid.device = device;
2265 tid.subdevice = 0;
2266
2267 snd_azf3328_timer_hw.resolution *= seqtimer_scaling;
2268 snd_azf3328_timer_hw.ticks /= seqtimer_scaling;
Andreas Mohr02330fb2008-05-16 12:18:29 +02002269
2270 err = snd_timer_new(chip->card, "AZF3328", &tid, &timer);
2271 if (err < 0)
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002272 goto out;
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002273
2274 strcpy(timer->name, "AZF3328 timer");
2275 timer->private_data = chip;
2276 timer->hw = snd_azf3328_timer_hw;
2277
2278 chip->timer = timer;
2279
Andreas Mohr02330fb2008-05-16 12:18:29 +02002280 snd_azf3328_timer_stop(timer);
2281
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002282 err = 0;
2283
2284out:
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002285 return err;
2286}
2287
2288/******************************************************************/
2289
Andreas Mohr02330fb2008-05-16 12:18:29 +02002290static int
2291snd_azf3328_free(struct snd_azf3328 *chip)
2292{
2293 if (chip->irq < 0)
2294 goto __end_hw;
2295
Andreas Mohrb5dc20c2011-02-19 00:49:32 +01002296 snd_azf3328_mixer_reset(chip);
Andreas Mohr02330fb2008-05-16 12:18:29 +02002297
2298 snd_azf3328_timer_stop(chip->timer);
2299 snd_azf3328_gameport_free(chip);
2300
2301 if (chip->irq >= 0)
2302 synchronize_irq(chip->irq);
2303__end_hw:
2304 if (chip->irq >= 0)
2305 free_irq(chip->irq, chip);
2306 pci_release_regions(chip->pci);
2307 pci_disable_device(chip->pci);
2308
2309 kfree(chip);
2310 return 0;
2311}
2312
2313static int
2314snd_azf3328_dev_free(struct snd_device *device)
2315{
2316 struct snd_azf3328 *chip = device->device_data;
2317 return snd_azf3328_free(chip);
2318}
2319
Linus Torvalds1da177e2005-04-16 15:20:36 -07002320#if 0
2321/* check whether a bit can be modified */
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002322static void
Andreas Mohr02330fb2008-05-16 12:18:29 +02002323snd_azf3328_test_bit(unsigned unsigned reg, int bit)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002324{
2325 unsigned char val, valoff, valon;
2326
2327 val = inb(reg);
2328
2329 outb(val & ~(1 << bit), reg);
2330 valoff = inb(reg);
2331
2332 outb(val|(1 << bit), reg);
2333 valon = inb(reg);
Andreas Mohr02330fb2008-05-16 12:18:29 +02002334
Linus Torvalds1da177e2005-04-16 15:20:36 -07002335 outb(val, reg);
2336
Andreas Mohr78df6172009-07-12 22:17:54 +02002337 printk(KERN_DEBUG "reg %04x bit %d: %02x %02x %02x\n",
Andreas Mohr02330fb2008-05-16 12:18:29 +02002338 reg, bit, val, valoff, valon
2339 );
Linus Torvalds1da177e2005-04-16 15:20:36 -07002340}
2341#endif
2342
Andreas Mohr02330fb2008-05-16 12:18:29 +02002343static inline void
Takashi Iwai95de7762005-11-17 15:02:42 +01002344snd_azf3328_debug_show_ports(const struct snd_azf3328 *chip)
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002345{
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002346 u16 tmp;
2347
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01002348 dev_dbg(chip->card->dev,
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002349 "ctrl_io 0x%lx, game_io 0x%lx, mpu_io 0x%lx, "
Andreas Mohr02330fb2008-05-16 12:18:29 +02002350 "opl3_io 0x%lx, mixer_io 0x%lx, irq %d\n",
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002351 chip->ctrl_io, chip->game_io, chip->mpu_io,
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01002352 chip->opl3_io, chip->mixer_io, chip->irq);
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002353
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01002354 dev_dbg(chip->card->dev,
2355 "game %02x %02x %02x %02x %02x %02x\n",
Andreas Mohr02330fb2008-05-16 12:18:29 +02002356 snd_azf3328_game_inb(chip, 0),
2357 snd_azf3328_game_inb(chip, 1),
2358 snd_azf3328_game_inb(chip, 2),
2359 snd_azf3328_game_inb(chip, 3),
2360 snd_azf3328_game_inb(chip, 4),
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01002361 snd_azf3328_game_inb(chip, 5));
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002362
Andreas Mohr02330fb2008-05-16 12:18:29 +02002363 for (tmp = 0; tmp < 0x07; tmp += 1)
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01002364 dev_dbg(chip->card->dev,
2365 "mpu_io 0x%04x\n", inb(chip->mpu_io + tmp));
Andreas Mohr02330fb2008-05-16 12:18:29 +02002366
2367 for (tmp = 0; tmp <= 0x07; tmp += 1)
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01002368 dev_dbg(chip->card->dev,
2369 "0x%02x: game200 0x%04x, game208 0x%04x\n",
Andreas Mohr02330fb2008-05-16 12:18:29 +02002370 tmp, inb(0x200 + tmp), inb(0x208 + tmp));
2371
2372 for (tmp = 0; tmp <= 0x01; tmp += 1)
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01002373 dev_dbg(chip->card->dev,
Andreas Mohr02330fb2008-05-16 12:18:29 +02002374 "0x%02x: mpu300 0x%04x, mpu310 0x%04x, mpu320 0x%04x, "
2375 "mpu330 0x%04x opl388 0x%04x opl38c 0x%04x\n",
2376 tmp,
2377 inb(0x300 + tmp),
2378 inb(0x310 + tmp),
2379 inb(0x320 + tmp),
2380 inb(0x330 + tmp),
2381 inb(0x388 + tmp),
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01002382 inb(0x38c + tmp));
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002383
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002384 for (tmp = 0; tmp < AZF_IO_SIZE_CTRL; tmp += 2)
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01002385 dev_dbg(chip->card->dev,
2386 "ctrl 0x%02x: 0x%04x\n",
2387 tmp, snd_azf3328_ctrl_inw(chip, tmp));
Andreas Mohre24a1212007-03-26 12:49:45 +02002388
2389 for (tmp = 0; tmp < AZF_IO_SIZE_MIXER; tmp += 2)
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01002390 dev_dbg(chip->card->dev,
2391 "mixer 0x%02x: 0x%04x\n",
2392 tmp, snd_azf3328_mixer_inw(chip, tmp));
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002393}
2394
Bill Pembertone23e7a12012-12-06 12:35:10 -05002395static int
Takashi Iwai95de7762005-11-17 15:02:42 +01002396snd_azf3328_create(struct snd_card *card,
Andreas Mohr02330fb2008-05-16 12:18:29 +02002397 struct pci_dev *pci,
2398 unsigned long device_type,
2399 struct snd_azf3328 **rchip)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002400{
Takashi Iwai95de7762005-11-17 15:02:42 +01002401 struct snd_azf3328 *chip;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002402 int err;
Takashi Iwai95de7762005-11-17 15:02:42 +01002403 static struct snd_device_ops ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002404 .dev_free = snd_azf3328_dev_free,
2405 };
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002406 u8 dma_init;
2407 enum snd_azf3328_codec_type codec_type;
Andreas Mohrda237f32010-12-27 21:17:26 +01002408 struct snd_azf3328_codec_data *codec_setup;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002409
2410 *rchip = NULL;
2411
Andreas Mohr02330fb2008-05-16 12:18:29 +02002412 err = pci_enable_device(pci);
2413 if (err < 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002414 return err;
2415
Takashi Iwaie560d8d2005-09-09 14:21:46 +02002416 chip = kzalloc(sizeof(*chip), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002417 if (chip == NULL) {
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002418 err = -ENOMEM;
2419 goto out_err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002420 }
2421 spin_lock_init(&chip->reg_lock);
2422 chip->card = card;
2423 chip->pci = pci;
2424 chip->irq = -1;
2425
2426 /* check if we can restrict PCI DMA transfers to 24 bits */
Yang Hongyang2f4f27d2009-04-06 19:01:18 -07002427 if (pci_set_dma_mask(pci, DMA_BIT_MASK(24)) < 0 ||
2428 pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(24)) < 0) {
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01002429 dev_err(card->dev,
2430 "architecture does not support 24bit PCI busmaster DMA\n"
Andreas Mohr02330fb2008-05-16 12:18:29 +02002431 );
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002432 err = -ENXIO;
2433 goto out_err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002434 }
2435
Andreas Mohr02330fb2008-05-16 12:18:29 +02002436 err = pci_request_regions(pci, "Aztech AZF3328");
2437 if (err < 0)
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002438 goto out_err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002439
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002440 chip->ctrl_io = pci_resource_start(pci, 0);
Andreas Mohr02330fb2008-05-16 12:18:29 +02002441 chip->game_io = pci_resource_start(pci, 1);
2442 chip->mpu_io = pci_resource_start(pci, 2);
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002443 chip->opl3_io = pci_resource_start(pci, 3);
Andreas Mohr02330fb2008-05-16 12:18:29 +02002444 chip->mixer_io = pci_resource_start(pci, 4);
2445
Andreas Mohr9fd8d362010-12-27 21:17:00 +01002446 codec_setup = &chip->codecs[AZF_CODEC_PLAYBACK];
2447 codec_setup->io_base = chip->ctrl_io + AZF_IO_OFFS_CODEC_PLAYBACK;
Andreas Mohrda237f32010-12-27 21:17:26 +01002448 codec_setup->lock = &chip->reg_lock;
2449 codec_setup->type = AZF_CODEC_PLAYBACK;
Andreas Mohr9fd8d362010-12-27 21:17:00 +01002450 codec_setup->name = "PLAYBACK";
2451
2452 codec_setup = &chip->codecs[AZF_CODEC_CAPTURE];
2453 codec_setup->io_base = chip->ctrl_io + AZF_IO_OFFS_CODEC_CAPTURE;
Andreas Mohrda237f32010-12-27 21:17:26 +01002454 codec_setup->lock = &chip->reg_lock;
2455 codec_setup->type = AZF_CODEC_CAPTURE;
Andreas Mohr9fd8d362010-12-27 21:17:00 +01002456 codec_setup->name = "CAPTURE";
2457
2458 codec_setup = &chip->codecs[AZF_CODEC_I2S_OUT];
2459 codec_setup->io_base = chip->ctrl_io + AZF_IO_OFFS_CODEC_I2S_OUT;
Andreas Mohrda237f32010-12-27 21:17:26 +01002460 codec_setup->lock = &chip->reg_lock;
2461 codec_setup->type = AZF_CODEC_I2S_OUT;
Andreas Mohr9fd8d362010-12-27 21:17:00 +01002462 codec_setup->name = "I2S_OUT";
Linus Torvalds1da177e2005-04-16 15:20:36 -07002463
Takashi Iwai437a5a42006-11-21 12:14:23 +01002464 if (request_irq(pci->irq, snd_azf3328_interrupt,
Takashi Iwai934c2b62011-06-10 16:36:37 +02002465 IRQF_SHARED, KBUILD_MODNAME, chip)) {
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01002466 dev_err(card->dev, "unable to grab IRQ %d\n", pci->irq);
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002467 err = -EBUSY;
2468 goto out_err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002469 }
2470 chip->irq = pci->irq;
2471 pci_set_master(pci);
2472 synchronize_irq(chip->irq);
2473
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002474 snd_azf3328_debug_show_ports(chip);
Andreas Mohr02330fb2008-05-16 12:18:29 +02002475
2476 err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
2477 if (err < 0)
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002478 goto out_err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002479
2480 /* create mixer interface & switches */
Andreas Mohr02330fb2008-05-16 12:18:29 +02002481 err = snd_azf3328_mixer_new(chip);
2482 if (err < 0)
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002483 goto out_err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002484
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002485 /* standard codec init stuff */
2486 /* default DMA init value */
2487 dma_init = DMA_RUN_SOMETHING2|DMA_EPILOGUE_SOMETHING|DMA_SOMETHING_ELSE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002488
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002489 for (codec_type = AZF_CODEC_PLAYBACK;
2490 codec_type <= AZF_CODEC_I2S_OUT; ++codec_type) {
2491 struct snd_azf3328_codec_data *codec =
2492 &chip->codecs[codec_type];
Linus Torvalds1da177e2005-04-16 15:20:36 -07002493
Andreas Mohradf59312010-12-27 21:16:43 +01002494 /* shutdown codecs to reduce power / noise */
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002495 /* have ...ctrl_codec_activity() act properly */
2496 codec->running = 1;
2497 snd_azf3328_ctrl_codec_activity(chip, codec_type, 0);
2498
Andreas Mohrda237f32010-12-27 21:17:26 +01002499 spin_lock_irq(codec->lock);
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002500 snd_azf3328_codec_outb(codec, IDX_IO_CODEC_DMA_FLAGS,
2501 dma_init);
Andreas Mohrda237f32010-12-27 21:17:26 +01002502 spin_unlock_irq(codec->lock);
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002503 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002504
Linus Torvalds1da177e2005-04-16 15:20:36 -07002505 *rchip = chip;
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002506
2507 err = 0;
2508 goto out;
2509
2510out_err:
2511 if (chip)
2512 snd_azf3328_free(chip);
2513 pci_disable_device(pci);
2514
2515out:
2516 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002517}
2518
Bill Pembertone23e7a12012-12-06 12:35:10 -05002519static int
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002520snd_azf3328_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002521{
2522 static int dev;
Takashi Iwai95de7762005-11-17 15:02:42 +01002523 struct snd_card *card;
2524 struct snd_azf3328 *chip;
2525 struct snd_opl3 *opl3;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002526 int err;
2527
Julia Lawalla5a39732011-08-10 11:49:04 +02002528 if (dev >= SNDRV_CARDS) {
2529 err = -ENODEV;
2530 goto out;
2531 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002532 if (!enable[dev]) {
2533 dev++;
Julia Lawalla5a39732011-08-10 11:49:04 +02002534 err = -ENOENT;
2535 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002536 }
2537
Takashi Iwai60c57722014-01-29 14:20:19 +01002538 err = snd_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE,
2539 0, &card);
Takashi Iwaie58de7b2008-12-28 16:44:30 +01002540 if (err < 0)
Julia Lawalla5a39732011-08-10 11:49:04 +02002541 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002542
2543 strcpy(card->driver, "AZF3328");
2544 strcpy(card->shortname, "Aztech AZF3328 (PCI168)");
2545
Andreas Mohr02330fb2008-05-16 12:18:29 +02002546 err = snd_azf3328_create(card, pci, pci_id->driver_data, &chip);
2547 if (err < 0)
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002548 goto out_err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002549
Andreas Mohrca54bde2006-05-17 11:02:24 +02002550 card->private_data = chip;
2551
Andreas Mohr78df6172009-07-12 22:17:54 +02002552 /* chose to use MPU401_HW_AZT2320 ID instead of MPU401_HW_MPU401,
2553 since our hardware ought to be similar, thus use same ID. */
Andreas Mohr02330fb2008-05-16 12:18:29 +02002554 err = snd_mpu401_uart_new(
Andreas Mohr78df6172009-07-12 22:17:54 +02002555 card, 0,
Clemens Ladischdba8b462011-09-13 11:24:41 +02002556 MPU401_HW_AZT2320, chip->mpu_io,
2557 MPU401_INFO_INTEGRATED | MPU401_INFO_IRQ_HOOK,
2558 -1, &chip->rmidi
Andreas Mohr02330fb2008-05-16 12:18:29 +02002559 );
2560 if (err < 0) {
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01002561 dev_err(card->dev, "no MPU-401 device at 0x%lx?\n",
Andreas Mohr02330fb2008-05-16 12:18:29 +02002562 chip->mpu_io
2563 );
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002564 goto out_err;
2565 }
2566
Andreas Mohr02330fb2008-05-16 12:18:29 +02002567 err = snd_azf3328_timer(chip, 0);
2568 if (err < 0)
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002569 goto out_err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002570
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002571 err = snd_azf3328_pcm(chip);
Andreas Mohr02330fb2008-05-16 12:18:29 +02002572 if (err < 0)
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002573 goto out_err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002574
Andreas Mohr02330fb2008-05-16 12:18:29 +02002575 if (snd_opl3_create(card, chip->opl3_io, chip->opl3_io+2,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002576 OPL3_HW_AUTO, 1, &opl3) < 0) {
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01002577 dev_err(card->dev, "no OPL3 device at 0x%lx-0x%lx?\n",
Andreas Mohr02330fb2008-05-16 12:18:29 +02002578 chip->opl3_io, chip->opl3_io+2
2579 );
Linus Torvalds1da177e2005-04-16 15:20:36 -07002580 } else {
Andreas Mohr02330fb2008-05-16 12:18:29 +02002581 /* need to use IDs 1, 2 since ID 0 is snd_azf3328_timer above */
2582 err = snd_opl3_timer_new(opl3, 1, 2);
2583 if (err < 0)
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002584 goto out_err;
Andreas Mohr02330fb2008-05-16 12:18:29 +02002585 err = snd_opl3_hwdep_new(opl3, 0, 1, NULL);
2586 if (err < 0)
2587 goto out_err;
Alban Bedel87c9e7d2012-02-25 16:15:57 +01002588 opl3->private_data = chip;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002589 }
2590
Linus Torvalds1da177e2005-04-16 15:20:36 -07002591 sprintf(card->longname, "%s at 0x%lx, irq %i",
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002592 card->shortname, chip->ctrl_io, chip->irq);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002593
Andreas Mohr02330fb2008-05-16 12:18:29 +02002594 err = snd_card_register(card);
2595 if (err < 0)
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002596 goto out_err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002597
2598#ifdef MODULE
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01002599 dev_info(card->dev,
2600 "Sound driver for Aztech AZF3328-based soundcards such as PCI168.\n");
2601 dev_info(card->dev,
2602 "Hardware was completely undocumented, unfortunately.\n");
2603 dev_info(card->dev,
2604 "Feel free to contact andi AT lisas.de for bug reports etc.!\n");
2605 dev_info(card->dev,
2606 "User-scalable sequencer timer set to %dHz (1024000Hz / %d).\n",
2607 1024000 / seqtimer_scaling, seqtimer_scaling);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002608#endif
2609
Andreas Mohr02330fb2008-05-16 12:18:29 +02002610 snd_azf3328_gameport(chip, dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002611
2612 pci_set_drvdata(pci, card);
2613 dev++;
2614
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002615 err = 0;
2616 goto out;
Andreas Mohr02330fb2008-05-16 12:18:29 +02002617
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002618out_err:
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01002619 dev_err(card->dev, "something failed, exiting\n");
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002620 snd_card_free(card);
Andreas Mohr02330fb2008-05-16 12:18:29 +02002621
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002622out:
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002623 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002624}
2625
Bill Pembertone23e7a12012-12-06 12:35:10 -05002626static void
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002627snd_azf3328_remove(struct pci_dev *pci)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002628{
Linus Torvalds1da177e2005-04-16 15:20:36 -07002629 snd_card_free(pci_get_drvdata(pci));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002630}
2631
Takashi Iwaic7561cd2012-08-14 18:12:04 +02002632#ifdef CONFIG_PM_SLEEP
Andreas Mohr78df6172009-07-12 22:17:54 +02002633static inline void
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01002634snd_azf3328_suspend_regs(const struct snd_azf3328 *chip,
2635 unsigned long io_addr, unsigned count, u32 *saved_regs)
Andreas Mohr78df6172009-07-12 22:17:54 +02002636{
2637 unsigned reg;
2638
2639 for (reg = 0; reg < count; ++reg) {
2640 *saved_regs = inl(io_addr);
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01002641 dev_dbg(chip->card->dev, "suspend: io 0x%04lx: 0x%08x\n",
Andreas Mohr78df6172009-07-12 22:17:54 +02002642 io_addr, *saved_regs);
2643 ++saved_regs;
2644 io_addr += sizeof(*saved_regs);
2645 }
2646}
2647
Andreas Mohrb5dc20c2011-02-19 00:49:32 +01002648static inline void
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01002649snd_azf3328_resume_regs(const struct snd_azf3328 *chip,
2650 const u32 *saved_regs,
Andreas Mohrb5dc20c2011-02-19 00:49:32 +01002651 unsigned long io_addr,
2652 unsigned count
2653)
2654{
2655 unsigned reg;
2656
2657 for (reg = 0; reg < count; ++reg) {
2658 outl(*saved_regs, io_addr);
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01002659 dev_dbg(chip->card->dev,
2660 "resume: io 0x%04lx: 0x%08x --> 0x%08x\n",
Andreas Mohrb5dc20c2011-02-19 00:49:32 +01002661 io_addr, *saved_regs, inl(io_addr));
2662 ++saved_regs;
2663 io_addr += sizeof(*saved_regs);
2664 }
2665}
2666
2667static inline void
2668snd_azf3328_suspend_ac97(struct snd_azf3328 *chip)
2669{
2670#ifdef AZF_USE_AC97_LAYER
2671 snd_ac97_suspend(chip->ac97);
2672#else
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01002673 snd_azf3328_suspend_regs(chip, chip->mixer_io,
Andreas Mohrb5dc20c2011-02-19 00:49:32 +01002674 ARRAY_SIZE(chip->saved_regs_mixer), chip->saved_regs_mixer);
2675
2676 /* make sure to disable master volume etc. to prevent looping sound */
2677 snd_azf3328_mixer_mute_control_master(chip, 1);
2678 snd_azf3328_mixer_mute_control_pcm(chip, 1);
2679#endif /* AZF_USE_AC97_LAYER */
2680}
2681
2682static inline void
2683snd_azf3328_resume_ac97(const struct snd_azf3328 *chip)
2684{
2685#ifdef AZF_USE_AC97_LAYER
2686 snd_ac97_resume(chip->ac97);
2687#else
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01002688 snd_azf3328_resume_regs(chip, chip->saved_regs_mixer, chip->mixer_io,
Andreas Mohrb5dc20c2011-02-19 00:49:32 +01002689 ARRAY_SIZE(chip->saved_regs_mixer));
2690
2691 /* unfortunately with 32bit transfers, IDX_MIXER_PLAY_MASTER (0x02)
2692 and IDX_MIXER_RESET (offset 0x00) get touched at the same time,
2693 resulting in a mixer reset condition persisting until _after_
2694 master vol was restored. Thus master vol needs an extra restore. */
2695 outw(((u16 *)chip->saved_regs_mixer)[1], chip->mixer_io + 2);
2696#endif /* AZF_USE_AC97_LAYER */
2697}
2698
Andreas Mohrca54bde2006-05-17 11:02:24 +02002699static int
Takashi Iwai68cb2b52012-07-02 15:20:37 +02002700snd_azf3328_suspend(struct device *dev)
Andreas Mohrca54bde2006-05-17 11:02:24 +02002701{
Takashi Iwai68cb2b52012-07-02 15:20:37 +02002702 struct pci_dev *pci = to_pci_dev(dev);
2703 struct snd_card *card = dev_get_drvdata(dev);
Andreas Mohrca54bde2006-05-17 11:02:24 +02002704 struct snd_azf3328 *chip = card->private_data;
Andreas Mohr78df6172009-07-12 22:17:54 +02002705 u16 *saved_regs_ctrl_u16;
Andreas Mohrca54bde2006-05-17 11:02:24 +02002706
2707 snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
Andreas Mohr02330fb2008-05-16 12:18:29 +02002708
Andreas Mohradf59312010-12-27 21:16:43 +01002709 /* same pcm object for playback/capture */
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002710 snd_pcm_suspend_all(chip->pcm[AZF_CODEC_PLAYBACK]);
2711 snd_pcm_suspend_all(chip->pcm[AZF_CODEC_I2S_OUT]);
Andreas Mohrca54bde2006-05-17 11:02:24 +02002712
Andreas Mohrb5dc20c2011-02-19 00:49:32 +01002713 snd_azf3328_suspend_ac97(chip);
Andreas Mohr02330fb2008-05-16 12:18:29 +02002714
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01002715 snd_azf3328_suspend_regs(chip, chip->ctrl_io,
Andreas Mohr78df6172009-07-12 22:17:54 +02002716 ARRAY_SIZE(chip->saved_regs_ctrl), chip->saved_regs_ctrl);
Andreas Mohr627d3e72008-06-23 11:50:47 +02002717
2718 /* manually store the one currently relevant write-only reg, too */
Andreas Mohr78df6172009-07-12 22:17:54 +02002719 saved_regs_ctrl_u16 = (u16 *)chip->saved_regs_ctrl;
2720 saved_regs_ctrl_u16[IDX_IO_6AH / 2] = chip->shadow_reg_ctrl_6AH;
Andreas Mohr627d3e72008-06-23 11:50:47 +02002721
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01002722 snd_azf3328_suspend_regs(chip, chip->game_io,
Andreas Mohr78df6172009-07-12 22:17:54 +02002723 ARRAY_SIZE(chip->saved_regs_game), chip->saved_regs_game);
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01002724 snd_azf3328_suspend_regs(chip, chip->mpu_io,
Andreas Mohr78df6172009-07-12 22:17:54 +02002725 ARRAY_SIZE(chip->saved_regs_mpu), chip->saved_regs_mpu);
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01002726 snd_azf3328_suspend_regs(chip, chip->opl3_io,
Andreas Mohr78df6172009-07-12 22:17:54 +02002727 ARRAY_SIZE(chip->saved_regs_opl3), chip->saved_regs_opl3);
Andreas Mohrca54bde2006-05-17 11:02:24 +02002728
Andreas Mohrca54bde2006-05-17 11:02:24 +02002729 pci_disable_device(pci);
2730 pci_save_state(pci);
Takashi Iwai68cb2b52012-07-02 15:20:37 +02002731 pci_set_power_state(pci, PCI_D3hot);
Andreas Mohrca54bde2006-05-17 11:02:24 +02002732 return 0;
2733}
2734
2735static int
Takashi Iwai68cb2b52012-07-02 15:20:37 +02002736snd_azf3328_resume(struct device *dev)
Andreas Mohrca54bde2006-05-17 11:02:24 +02002737{
Takashi Iwai68cb2b52012-07-02 15:20:37 +02002738 struct pci_dev *pci = to_pci_dev(dev);
2739 struct snd_card *card = dev_get_drvdata(dev);
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002740 const struct snd_azf3328 *chip = card->private_data;
Andreas Mohrca54bde2006-05-17 11:02:24 +02002741
Andreas Mohrca54bde2006-05-17 11:02:24 +02002742 pci_set_power_state(pci, PCI_D0);
Takashi Iwai30b35392006-10-11 18:52:53 +02002743 pci_restore_state(pci);
2744 if (pci_enable_device(pci) < 0) {
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01002745 dev_err(dev, "pci_enable_device failed, disabling device\n");
Takashi Iwai30b35392006-10-11 18:52:53 +02002746 snd_card_disconnect(card);
2747 return -EIO;
2748 }
Andreas Mohrca54bde2006-05-17 11:02:24 +02002749 pci_set_master(pci);
2750
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01002751 snd_azf3328_resume_regs(chip, chip->saved_regs_game, chip->game_io,
Andreas Mohr78df6172009-07-12 22:17:54 +02002752 ARRAY_SIZE(chip->saved_regs_game));
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01002753 snd_azf3328_resume_regs(chip, chip->saved_regs_mpu, chip->mpu_io,
Andreas Mohr78df6172009-07-12 22:17:54 +02002754 ARRAY_SIZE(chip->saved_regs_mpu));
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01002755 snd_azf3328_resume_regs(chip, chip->saved_regs_opl3, chip->opl3_io,
Andreas Mohr78df6172009-07-12 22:17:54 +02002756 ARRAY_SIZE(chip->saved_regs_opl3));
2757
Andreas Mohrb5dc20c2011-02-19 00:49:32 +01002758 snd_azf3328_resume_ac97(chip);
Andreas Mohr78df6172009-07-12 22:17:54 +02002759
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01002760 snd_azf3328_resume_regs(chip, chip->saved_regs_ctrl, chip->ctrl_io,
Andreas Mohr78df6172009-07-12 22:17:54 +02002761 ARRAY_SIZE(chip->saved_regs_ctrl));
Andreas Mohrca54bde2006-05-17 11:02:24 +02002762
2763 snd_power_change_state(card, SNDRV_CTL_POWER_D0);
2764 return 0;
2765}
Andreas Mohrca54bde2006-05-17 11:02:24 +02002766
Takashi Iwai68cb2b52012-07-02 15:20:37 +02002767static SIMPLE_DEV_PM_OPS(snd_azf3328_pm, snd_azf3328_suspend, snd_azf3328_resume);
2768#define SND_AZF3328_PM_OPS &snd_azf3328_pm
2769#else
2770#define SND_AZF3328_PM_OPS NULL
Takashi Iwaic7561cd2012-08-14 18:12:04 +02002771#endif /* CONFIG_PM_SLEEP */
Andreas Mohrca54bde2006-05-17 11:02:24 +02002772
Takashi Iwaie9f66d92012-04-24 12:25:00 +02002773static struct pci_driver azf3328_driver = {
Takashi Iwai3733e422011-06-10 16:20:20 +02002774 .name = KBUILD_MODNAME,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002775 .id_table = snd_azf3328_ids,
2776 .probe = snd_azf3328_probe,
Bill Pembertone23e7a12012-12-06 12:35:10 -05002777 .remove = snd_azf3328_remove,
Takashi Iwai68cb2b52012-07-02 15:20:37 +02002778 .driver = {
2779 .pm = SND_AZF3328_PM_OPS,
2780 },
Linus Torvalds1da177e2005-04-16 15:20:36 -07002781};
2782
Takashi Iwaie9f66d92012-04-24 12:25:00 +02002783module_pci_driver(azf3328_driver);