blob: 80c4a4456197954e801590ed52989a9901f8f6ba [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
Takashi Iwai6cbbfe12015-01-28 16:49:33 +0100182#include <linux/io.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -0700183#include <linux/init.h>
Andreas Mohr689c6912010-12-27 21:17:35 +0100184#include <linux/bug.h> /* WARN_ONCE */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700185#include <linux/pci.h>
186#include <linux/delay.h>
187#include <linux/slab.h>
188#include <linux/gameport.h>
Paul Gortmaker65a77212011-07-15 13:13:37 -0400189#include <linux/module.h>
Matthias Gehre910638a2006-03-28 01:56:48 -0800190#include <linux/dma-mapping.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -0700191#include <sound/core.h>
192#include <sound/control.h>
193#include <sound/pcm.h>
194#include <sound/rawmidi.h>
195#include <sound/mpu401.h>
196#include <sound/opl3.h>
197#include <sound/initval.h>
Andreas Mohrb5dc20c2011-02-19 00:49:32 +0100198/*
199 * Config switch, to use ALSA's AC97 layer instead of old custom mixer crap.
200 * If the AC97 compatibility parts we needed to implement locally turn out
201 * to work nicely, then remove the old implementation eventually.
202 */
203#define AZF_USE_AC97_LAYER 1
204
205#ifdef AZF_USE_AC97_LAYER
206#include <sound/ac97_codec.h>
207#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700208#include "azt3328.h"
209
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200210MODULE_AUTHOR("Andreas Mohr <andi AT lisas.de>");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700211MODULE_DESCRIPTION("Aztech AZF3328 (PCI168)");
212MODULE_LICENSE("GPL");
213MODULE_SUPPORTED_DEVICE("{{Aztech,AZF3328}}");
214
215#if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE))
Andreas Mohr02330fb2008-05-16 12:18:29 +0200216#define SUPPORT_GAMEPORT 1
Linus Torvalds1da177e2005-04-16 15:20:36 -0700217#endif
218
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200219/* === Debug settings ===
220 Further diagnostic functionality than the settings below
Andreas Mohradf59312010-12-27 21:16:43 +0100221 does not need to be provided, since one can easily write a POSIX shell script
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200222 to dump the card's I/O ports (those listed in lspci -v -v):
Andreas Mohradf59312010-12-27 21:16:43 +0100223 dump()
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200224 {
225 local descr=$1; local addr=$2; local count=$3
226
227 echo "${descr}: ${count} @ ${addr}:"
Andreas Mohradf59312010-12-27 21:16:43 +0100228 dd if=/dev/port skip=`printf %d ${addr}` count=${count} bs=1 \
229 2>/dev/null| hexdump -C
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200230 }
231 and then use something like
232 "dump joy200 0x200 8", "dump mpu388 0x388 4", "dump joy 0xb400 8",
233 "dump codec00 0xa800 32", "dump mixer 0xb800 64", "dump synth 0xbc00 8",
234 possibly within a "while true; do ... sleep 1; done" loop.
235 Tweaking ports could be done using
236 VALSTRING="`printf "%02x" $value`"
Andreas Mohradf59312010-12-27 21:16:43 +0100237 printf "\x""$VALSTRING"|dd of=/dev/port seek=`printf %d ${addr}` bs=1 \
238 2>/dev/null
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200239*/
240
Linus Torvalds1da177e2005-04-16 15:20:36 -0700241static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
242module_param_array(index, int, NULL, 0444);
243MODULE_PARM_DESC(index, "Index value for AZF3328 soundcard.");
244
245static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
246module_param_array(id, charp, NULL, 0444);
247MODULE_PARM_DESC(id, "ID string for AZF3328 soundcard.");
248
Rusty Russella67ff6a2011-12-15 13:49:36 +1030249static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700250module_param_array(enable, bool, NULL, 0444);
251MODULE_PARM_DESC(enable, "Enable AZF3328 soundcard.");
252
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200253static int seqtimer_scaling = 128;
254module_param(seqtimer_scaling, int, 0444);
255MODULE_PARM_DESC(seqtimer_scaling, "Set 1024000Hz sequencer timer scale factor (lockup danger!). Default 128.");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700256
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200257enum snd_azf3328_codec_type {
Andreas Mohradf59312010-12-27 21:16:43 +0100258 /* warning: fixed indices (also used for bitmask checks!) */
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200259 AZF_CODEC_PLAYBACK = 0,
260 AZF_CODEC_CAPTURE = 1,
261 AZF_CODEC_I2S_OUT = 2,
Andreas Mohr02330fb2008-05-16 12:18:29 +0200262};
263
Andreas Mohrda237f32010-12-27 21:17:26 +0100264struct snd_azf3328_codec_data {
265 unsigned long io_base; /* keep first! (avoid offset calc) */
266 unsigned int dma_base; /* helper to avoid an indirection in hotpath */
267 spinlock_t *lock; /* TODO: convert to our own per-codec lock member */
268 struct snd_pcm_substream *substream;
269 bool running;
270 enum snd_azf3328_codec_type type;
271 const char *name;
272};
273
Takashi Iwai95de7762005-11-17 15:02:42 +0100274struct snd_azf3328 {
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200275 /* often-used fields towards beginning, then grouped */
Andreas Mohr02330fb2008-05-16 12:18:29 +0200276
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200277 unsigned long ctrl_io; /* usually 0xb000, size 128 */
Andreas Mohr02330fb2008-05-16 12:18:29 +0200278 unsigned long game_io; /* usually 0xb400, size 8 */
279 unsigned long mpu_io; /* usually 0xb800, size 4 */
280 unsigned long opl3_io; /* usually 0xbc00, size 8 */
281 unsigned long mixer_io; /* usually 0xc000, size 64 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700282
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200283 spinlock_t reg_lock;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700284
Takashi Iwai95de7762005-11-17 15:02:42 +0100285 struct snd_timer *timer;
Andreas Mohr02330fb2008-05-16 12:18:29 +0200286
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200287 struct snd_pcm *pcm[3];
288
289 /* playback, recording and I2S out codecs */
290 struct snd_azf3328_codec_data codecs[3];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700291
Andreas Mohrb5dc20c2011-02-19 00:49:32 +0100292#ifdef AZF_USE_AC97_LAYER
293 struct snd_ac97 *ac97;
294#endif
295
Takashi Iwai95de7762005-11-17 15:02:42 +0100296 struct snd_card *card;
297 struct snd_rawmidi *rmidi;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700298
Andreas Mohr02330fb2008-05-16 12:18:29 +0200299#ifdef SUPPORT_GAMEPORT
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200300 struct gameport *gameport;
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200301 u16 axes[4];
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200302#endif
303
304 struct pci_dev *pci;
305 int irq;
Andreas Mohrca54bde2006-05-17 11:02:24 +0200306
Andreas Mohr627d3e72008-06-23 11:50:47 +0200307 /* register 0x6a is write-only, thus need to remember setting.
308 * If we need to add more registers here, then we might try to fold this
309 * into some transparent combined shadow register handling with
310 * CONFIG_PM register storage below, but that's slightly difficult. */
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200311 u16 shadow_reg_ctrl_6AH;
Andreas Mohr627d3e72008-06-23 11:50:47 +0200312
Takashi Iwaic7561cd2012-08-14 18:12:04 +0200313#ifdef CONFIG_PM_SLEEP
Andreas Mohrca54bde2006-05-17 11:02:24 +0200314 /* register value containers for power management
Andreas Mohr78df6172009-07-12 22:17:54 +0200315 * Note: not always full I/O range preserved (similar to Win driver!) */
316 u32 saved_regs_ctrl[AZF_ALIGN(AZF_IO_SIZE_CTRL_PM) / 4];
317 u32 saved_regs_game[AZF_ALIGN(AZF_IO_SIZE_GAME_PM) / 4];
318 u32 saved_regs_mpu[AZF_ALIGN(AZF_IO_SIZE_MPU_PM) / 4];
319 u32 saved_regs_opl3[AZF_ALIGN(AZF_IO_SIZE_OPL3_PM) / 4];
320 u32 saved_regs_mixer[AZF_ALIGN(AZF_IO_SIZE_MIXER_PM) / 4];
Andreas Mohrca54bde2006-05-17 11:02:24 +0200321#endif
Takashi Iwai95de7762005-11-17 15:02:42 +0100322};
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200323
Benoit Taine9baa3c32014-08-08 15:56:03 +0200324static const struct pci_device_id snd_azf3328_ids[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700325 { 0x122D, 0x50DC, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* PCI168/3328 */
326 { 0x122D, 0x80DA, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* 3328 */
327 { 0, }
328};
329
330MODULE_DEVICE_TABLE(pci, snd_azf3328_ids);
331
Andreas Mohr02330fb2008-05-16 12:18:29 +0200332
333static int
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200334snd_azf3328_io_reg_setb(unsigned reg, u8 mask, bool do_set)
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200335{
Andreas Mohradf59312010-12-27 21:16:43 +0100336 /* Well, strictly spoken, the inb/outb sequence isn't atomic
337 and would need locking. However we currently don't care
338 since it potentially complicates matters. */
Andreas Mohr02330fb2008-05-16 12:18:29 +0200339 u8 prev = inb(reg), new;
340
341 new = (do_set) ? (prev|mask) : (prev & ~mask);
342 /* we need to always write the new value no matter whether it differs
343 * or not, since some register bits don't indicate their setting */
344 outb(new, reg);
345 if (new != prev)
346 return 1;
347
348 return 0;
349}
350
Andreas Mohr02330fb2008-05-16 12:18:29 +0200351static inline void
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200352snd_azf3328_codec_outb(const struct snd_azf3328_codec_data *codec,
353 unsigned reg,
354 u8 value
355)
Andreas Mohr02330fb2008-05-16 12:18:29 +0200356{
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200357 outb(value, codec->io_base + reg);
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200358}
359
360static inline u8
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200361snd_azf3328_codec_inb(const struct snd_azf3328_codec_data *codec, unsigned reg)
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200362{
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200363 return inb(codec->io_base + reg);
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200364}
365
366static inline void
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200367snd_azf3328_codec_outw(const struct snd_azf3328_codec_data *codec,
368 unsigned reg,
369 u16 value
370)
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200371{
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200372 outw(value, codec->io_base + reg);
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200373}
374
375static inline u16
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200376snd_azf3328_codec_inw(const struct snd_azf3328_codec_data *codec, unsigned reg)
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200377{
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200378 return inw(codec->io_base + reg);
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200379}
380
381static inline void
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200382snd_azf3328_codec_outl(const struct snd_azf3328_codec_data *codec,
383 unsigned reg,
384 u32 value
385)
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200386{
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200387 outl(value, codec->io_base + reg);
Andreas Mohr02330fb2008-05-16 12:18:29 +0200388}
389
Andreas Mohr689c6912010-12-27 21:17:35 +0100390static inline void
391snd_azf3328_codec_outl_multi(const struct snd_azf3328_codec_data *codec,
392 unsigned reg, const void *buffer, int count
393)
394{
395 unsigned long addr = codec->io_base + reg;
396 if (count) {
397 const u32 *buf = buffer;
398 do {
399 outl(*buf++, addr);
400 addr += 4;
401 } while (--count);
402 }
403}
404
Andreas Mohr02330fb2008-05-16 12:18:29 +0200405static inline u32
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200406snd_azf3328_codec_inl(const struct snd_azf3328_codec_data *codec, unsigned reg)
Andreas Mohr02330fb2008-05-16 12:18:29 +0200407{
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200408 return inl(codec->io_base + reg);
409}
410
411static inline void
412snd_azf3328_ctrl_outb(const struct snd_azf3328 *chip, unsigned reg, u8 value)
413{
414 outb(value, chip->ctrl_io + reg);
415}
416
417static inline u8
418snd_azf3328_ctrl_inb(const struct snd_azf3328 *chip, unsigned reg)
419{
420 return inb(chip->ctrl_io + reg);
421}
422
Takashi Iwai4a8d9d72014-02-25 14:04:46 +0100423static inline u16
424snd_azf3328_ctrl_inw(const struct snd_azf3328 *chip, unsigned reg)
425{
426 return inw(chip->ctrl_io + reg);
427}
428
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200429static inline void
430snd_azf3328_ctrl_outw(const struct snd_azf3328 *chip, unsigned reg, u16 value)
431{
432 outw(value, chip->ctrl_io + reg);
433}
434
435static inline void
436snd_azf3328_ctrl_outl(const struct snd_azf3328 *chip, unsigned reg, u32 value)
437{
438 outl(value, chip->ctrl_io + reg);
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200439}
440
441static inline void
Andreas Mohr02330fb2008-05-16 12:18:29 +0200442snd_azf3328_game_outb(const struct snd_azf3328 *chip, unsigned reg, u8 value)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700443{
Andreas Mohr02330fb2008-05-16 12:18:29 +0200444 outb(value, chip->game_io + reg);
445}
446
447static inline void
448snd_azf3328_game_outw(const struct snd_azf3328 *chip, unsigned reg, u16 value)
449{
450 outw(value, chip->game_io + reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700451}
452
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200453static inline u8
Andreas Mohr02330fb2008-05-16 12:18:29 +0200454snd_azf3328_game_inb(const struct snd_azf3328 *chip, unsigned reg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700455{
Andreas Mohr02330fb2008-05-16 12:18:29 +0200456 return inb(chip->game_io + reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700457}
458
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200459static inline u16
Andreas Mohr02330fb2008-05-16 12:18:29 +0200460snd_azf3328_game_inw(const struct snd_azf3328 *chip, unsigned reg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700461{
Andreas Mohr02330fb2008-05-16 12:18:29 +0200462 return inw(chip->game_io + reg);
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200463}
464
Andreas Mohr02330fb2008-05-16 12:18:29 +0200465static inline void
466snd_azf3328_mixer_outw(const struct snd_azf3328 *chip, unsigned reg, u16 value)
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200467{
Andreas Mohr02330fb2008-05-16 12:18:29 +0200468 outw(value, chip->mixer_io + reg);
469}
470
471static inline u16
472snd_azf3328_mixer_inw(const struct snd_azf3328 *chip, unsigned reg)
473{
474 return inw(chip->mixer_io + reg);
475}
476
477#define AZF_MUTE_BIT 0x80
478
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200479static bool
Andreas Mohrb5dc20c2011-02-19 00:49:32 +0100480snd_azf3328_mixer_mute_control(const struct snd_azf3328 *chip,
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200481 unsigned reg, bool do_mute
Andreas Mohr02330fb2008-05-16 12:18:29 +0200482)
483{
484 unsigned long portbase = chip->mixer_io + reg + 1;
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200485 bool updated;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700486
487 /* the mute bit is on the *second* (i.e. right) register of a
488 * left/right channel setting */
Andreas Mohr02330fb2008-05-16 12:18:29 +0200489 updated = snd_azf3328_io_reg_setb(portbase, AZF_MUTE_BIT, do_mute);
490
491 /* indicate whether it was muted before */
492 return (do_mute) ? !updated : updated;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700493}
494
Andreas Mohrb5dc20c2011-02-19 00:49:32 +0100495static inline bool
496snd_azf3328_mixer_mute_control_master(const struct snd_azf3328 *chip,
497 bool do_mute
498)
499{
500 return snd_azf3328_mixer_mute_control(
501 chip,
502 IDX_MIXER_PLAY_MASTER,
503 do_mute
504 );
505}
506
507static inline bool
508snd_azf3328_mixer_mute_control_pcm(const struct snd_azf3328 *chip,
509 bool do_mute
510)
511{
512 return snd_azf3328_mixer_mute_control(
513 chip,
514 IDX_MIXER_WAVEOUT,
515 do_mute
516 );
517}
518
519static inline void
520snd_azf3328_mixer_reset(const struct snd_azf3328 *chip)
521{
522 /* reset (close) mixer:
523 * first mute master volume, then reset
524 */
525 snd_azf3328_mixer_mute_control_master(chip, 1);
526 snd_azf3328_mixer_outw(chip, IDX_MIXER_RESET, 0x0000);
527}
528
529#ifdef AZF_USE_AC97_LAYER
530
531static inline void
Takashi Iwai4a8d9d72014-02-25 14:04:46 +0100532snd_azf3328_mixer_ac97_map_unsupported(const struct snd_azf3328 *chip,
533 unsigned short reg, const char *mode)
Andreas Mohrb5dc20c2011-02-19 00:49:32 +0100534{
535 /* need to add some more or less clever emulation? */
Takashi Iwai4a8d9d72014-02-25 14:04:46 +0100536 dev_warn(chip->card->dev,
537 "missing %s emulation for AC97 register 0x%02x!\n",
Andreas Mohrb5dc20c2011-02-19 00:49:32 +0100538 mode, reg);
539}
540
541/*
542 * Need to have _special_ AC97 mixer hardware register index mapper,
543 * to compensate for the issue of a rather AC97-incompatible hardware layout.
544 */
545#define AZF_REG_MASK 0x3f
546#define AZF_AC97_REG_UNSUPPORTED 0x8000
547#define AZF_AC97_REG_REAL_IO_READ 0x4000
548#define AZF_AC97_REG_REAL_IO_WRITE 0x2000
549#define AZF_AC97_REG_REAL_IO_RW \
550 (AZF_AC97_REG_REAL_IO_READ | AZF_AC97_REG_REAL_IO_WRITE)
551#define AZF_AC97_REG_EMU_IO_READ 0x0400
552#define AZF_AC97_REG_EMU_IO_WRITE 0x0200
553#define AZF_AC97_REG_EMU_IO_RW \
554 (AZF_AC97_REG_EMU_IO_READ | AZF_AC97_REG_EMU_IO_WRITE)
555static unsigned short
556snd_azf3328_mixer_ac97_map_reg_idx(unsigned short reg)
557{
558 static const struct {
559 unsigned short azf_reg;
560 } azf_reg_mapper[] = {
561 /* Especially when taking into consideration
562 * mono/stereo-based sequence of azf vs. AC97 control series,
563 * it's quite obvious that azf simply got rid
564 * of the AC97_HEADPHONE control at its intended offset,
565 * thus shifted _all_ controls by one,
566 * and _then_ simply added it as an FMSYNTH control at the end,
567 * to make up for the offset.
568 * This means we'll have to translate indices here as
569 * needed and then do some tiny AC97 patch action
570 * (snd_ac97_rename_vol_ctl() etc.) - that's it.
571 */
572 { /* AC97_RESET */ IDX_MIXER_RESET
573 | AZF_AC97_REG_REAL_IO_WRITE
574 | AZF_AC97_REG_EMU_IO_READ },
575 { /* AC97_MASTER */ IDX_MIXER_PLAY_MASTER },
576 /* note large shift: AC97_HEADPHONE to IDX_MIXER_FMSYNTH! */
577 { /* AC97_HEADPHONE */ IDX_MIXER_FMSYNTH },
578 { /* AC97_MASTER_MONO */ IDX_MIXER_MODEMOUT },
579 { /* AC97_MASTER_TONE */ IDX_MIXER_BASSTREBLE },
580 { /* AC97_PC_BEEP */ IDX_MIXER_PCBEEP },
581 { /* AC97_PHONE */ IDX_MIXER_MODEMIN },
582 { /* AC97_MIC */ IDX_MIXER_MIC },
583 { /* AC97_LINE */ IDX_MIXER_LINEIN },
584 { /* AC97_CD */ IDX_MIXER_CDAUDIO },
585 { /* AC97_VIDEO */ IDX_MIXER_VIDEO },
586 { /* AC97_AUX */ IDX_MIXER_AUX },
587 { /* AC97_PCM */ IDX_MIXER_WAVEOUT },
588 { /* AC97_REC_SEL */ IDX_MIXER_REC_SELECT },
589 { /* AC97_REC_GAIN */ IDX_MIXER_REC_VOLUME },
590 { /* AC97_REC_GAIN_MIC */ AZF_AC97_REG_EMU_IO_RW },
591 { /* AC97_GENERAL_PURPOSE */ IDX_MIXER_ADVCTL2 },
592 { /* AC97_3D_CONTROL */ IDX_MIXER_ADVCTL1 },
593 };
594
595 unsigned short reg_azf = AZF_AC97_REG_UNSUPPORTED;
596
597 /* azf3328 supports the low-numbered and low-spec:ed range
598 of AC97 regs only */
599 if (reg <= AC97_3D_CONTROL) {
600 unsigned short reg_idx = reg / 2;
601 reg_azf = azf_reg_mapper[reg_idx].azf_reg;
602 /* a translation-only entry means it's real read/write: */
603 if (!(reg_azf & ~AZF_REG_MASK))
604 reg_azf |= AZF_AC97_REG_REAL_IO_RW;
605 } else {
606 switch (reg) {
607 case AC97_POWERDOWN:
608 reg_azf = AZF_AC97_REG_EMU_IO_RW;
609 break;
610 case AC97_EXTENDED_ID:
611 reg_azf = AZF_AC97_REG_EMU_IO_READ;
612 break;
613 case AC97_EXTENDED_STATUS:
614 /* I don't know what the h*ll AC97 layer
615 * would consult this _extended_ register for
616 * given a base-AC97-advertised card,
617 * but let's just emulate it anyway :-P
618 */
619 reg_azf = AZF_AC97_REG_EMU_IO_RW;
620 break;
621 case AC97_VENDOR_ID1:
622 case AC97_VENDOR_ID2:
623 reg_azf = AZF_AC97_REG_EMU_IO_READ;
624 break;
625 }
626 }
627 return reg_azf;
628}
629
630static const unsigned short
631azf_emulated_ac97_caps =
632 AC97_BC_DEDICATED_MIC |
633 AC97_BC_BASS_TREBLE |
634 /* Headphone is an FM Synth control here */
635 AC97_BC_HEADPHONE |
636 /* no AC97_BC_LOUDNESS! */
637 /* mask 0x7c00 is
638 vendor-specific 3D enhancement
639 vendor indicator.
640 Since there actually _is_ an
641 entry for Aztech Labs
642 (13), make damn sure
643 to indicate it. */
644 (13 << 10);
645
646static const unsigned short
647azf_emulated_ac97_powerdown =
648 /* pretend everything to be active */
649 AC97_PD_ADC_STATUS |
650 AC97_PD_DAC_STATUS |
651 AC97_PD_MIXER_STATUS |
652 AC97_PD_VREF_STATUS;
653
654/*
655 * Emulated, _inofficial_ vendor ID
656 * (there might be some devices such as the MR 2800-W
657 * which could reveal the real Aztech AC97 ID).
658 * We choose to use "AZT" prefix, and then use 1 to indicate PCI168
659 * (better don't use 0x68 since there's a PCI368 as well).
660 */
661static const unsigned int
662azf_emulated_ac97_vendor_id = 0x415a5401;
663
664static unsigned short
665snd_azf3328_mixer_ac97_read(struct snd_ac97 *ac97, unsigned short reg_ac97)
666{
667 const struct snd_azf3328 *chip = ac97->private_data;
668 unsigned short reg_azf = snd_azf3328_mixer_ac97_map_reg_idx(reg_ac97);
669 unsigned short reg_val = 0;
Peter Senna Tschudine0f17c72013-09-22 20:44:12 +0200670 bool unsupported = false;
Andreas Mohrb5dc20c2011-02-19 00:49:32 +0100671
Takashi Iwai4a8d9d72014-02-25 14:04:46 +0100672 dev_dbg(chip->card->dev, "snd_azf3328_mixer_ac97_read reg_ac97 %u\n",
673 reg_ac97);
Andreas Mohrb5dc20c2011-02-19 00:49:32 +0100674 if (reg_azf & AZF_AC97_REG_UNSUPPORTED)
Peter Senna Tschudine0f17c72013-09-22 20:44:12 +0200675 unsupported = true;
Andreas Mohrb5dc20c2011-02-19 00:49:32 +0100676 else {
677 if (reg_azf & AZF_AC97_REG_REAL_IO_READ)
678 reg_val = snd_azf3328_mixer_inw(chip,
679 reg_azf & AZF_REG_MASK);
680 else {
681 /*
682 * Proceed with dummy I/O read,
683 * to ensure compatible timing where this may matter.
684 * (ALSA AC97 layer usually doesn't call I/O functions
685 * due to intelligent I/O caching anyway)
686 * Choose a mixer register that's thoroughly unrelated
687 * to common audio (try to minimize distortion).
688 */
689 snd_azf3328_mixer_inw(chip, IDX_MIXER_SOMETHING30H);
690 }
691
692 if (reg_azf & AZF_AC97_REG_EMU_IO_READ) {
693 switch (reg_ac97) {
694 case AC97_RESET:
695 reg_val |= azf_emulated_ac97_caps;
696 break;
697 case AC97_POWERDOWN:
698 reg_val |= azf_emulated_ac97_powerdown;
699 break;
700 case AC97_EXTENDED_ID:
701 case AC97_EXTENDED_STATUS:
702 /* AFAICS we simply can't support anything: */
703 reg_val |= 0;
704 break;
705 case AC97_VENDOR_ID1:
706 reg_val = azf_emulated_ac97_vendor_id >> 16;
707 break;
708 case AC97_VENDOR_ID2:
709 reg_val = azf_emulated_ac97_vendor_id & 0xffff;
710 break;
711 default:
Peter Senna Tschudine0f17c72013-09-22 20:44:12 +0200712 unsupported = true;
Andreas Mohrb5dc20c2011-02-19 00:49:32 +0100713 break;
714 }
715 }
716 }
717 if (unsupported)
Takashi Iwai4a8d9d72014-02-25 14:04:46 +0100718 snd_azf3328_mixer_ac97_map_unsupported(chip, reg_ac97, "read");
Andreas Mohrb5dc20c2011-02-19 00:49:32 +0100719
720 return reg_val;
721}
722
723static void
724snd_azf3328_mixer_ac97_write(struct snd_ac97 *ac97,
725 unsigned short reg_ac97, unsigned short val)
726{
727 const struct snd_azf3328 *chip = ac97->private_data;
728 unsigned short reg_azf = snd_azf3328_mixer_ac97_map_reg_idx(reg_ac97);
Peter Senna Tschudine0f17c72013-09-22 20:44:12 +0200729 bool unsupported = false;
Andreas Mohrb5dc20c2011-02-19 00:49:32 +0100730
Takashi Iwai4a8d9d72014-02-25 14:04:46 +0100731 dev_dbg(chip->card->dev,
Andreas Mohrb5dc20c2011-02-19 00:49:32 +0100732 "snd_azf3328_mixer_ac97_write reg_ac97 %u val %u\n",
Takashi Iwai4a8d9d72014-02-25 14:04:46 +0100733 reg_ac97, val);
Andreas Mohrb5dc20c2011-02-19 00:49:32 +0100734 if (reg_azf & AZF_AC97_REG_UNSUPPORTED)
Peter Senna Tschudine0f17c72013-09-22 20:44:12 +0200735 unsupported = true;
Andreas Mohrb5dc20c2011-02-19 00:49:32 +0100736 else {
737 if (reg_azf & AZF_AC97_REG_REAL_IO_WRITE)
738 snd_azf3328_mixer_outw(
739 chip,
740 reg_azf & AZF_REG_MASK,
741 val
742 );
743 else
744 if (reg_azf & AZF_AC97_REG_EMU_IO_WRITE) {
745 switch (reg_ac97) {
746 case AC97_REC_GAIN_MIC:
747 case AC97_POWERDOWN:
748 case AC97_EXTENDED_STATUS:
749 /*
750 * Silently swallow these writes.
751 * Since for most registers our card doesn't
752 * actually support a comparable feature,
753 * this is exactly what we should do here.
754 * The AC97 layer's I/O caching probably
755 * automatically takes care of all the rest...
756 * (remembers written values etc.)
757 */
758 break;
759 default:
Peter Senna Tschudine0f17c72013-09-22 20:44:12 +0200760 unsupported = true;
Andreas Mohrb5dc20c2011-02-19 00:49:32 +0100761 break;
762 }
763 }
764 }
765 if (unsupported)
Takashi Iwai4a8d9d72014-02-25 14:04:46 +0100766 snd_azf3328_mixer_ac97_map_unsupported(chip, reg_ac97, "write");
Andreas Mohrb5dc20c2011-02-19 00:49:32 +0100767}
768
Bill Pembertone23e7a12012-12-06 12:35:10 -0500769static int
Andreas Mohrb5dc20c2011-02-19 00:49:32 +0100770snd_azf3328_mixer_new(struct snd_azf3328 *chip)
771{
772 struct snd_ac97_bus *bus;
773 struct snd_ac97_template ac97;
774 static struct snd_ac97_bus_ops ops = {
775 .write = snd_azf3328_mixer_ac97_write,
776 .read = snd_azf3328_mixer_ac97_read,
777 };
778 int rc;
779
780 memset(&ac97, 0, sizeof(ac97));
781 ac97.scaps = AC97_SCAP_SKIP_MODEM
782 | AC97_SCAP_AUDIO /* we support audio! */
783 | AC97_SCAP_NO_SPDIF;
784 ac97.private_data = chip;
785 ac97.pci = chip->pci;
786
787 /*
788 * ALSA's AC97 layer has terrible init crackling issues,
789 * unfortunately, and since it makes use of AC97_RESET,
790 * there's no use trying to mute Master Playback proactively.
791 */
792
793 rc = snd_ac97_bus(chip->card, 0, &ops, NULL, &bus);
794 if (!rc)
795 rc = snd_ac97_mixer(bus, &ac97, &chip->ac97);
796 /*
797 * Make sure to complain loudly in case of AC97 init failure,
798 * since failure may happen quite often,
799 * due to this card being a very quirky AC97 "lookalike".
800 */
801 if (rc)
Takashi Iwai4a8d9d72014-02-25 14:04:46 +0100802 dev_err(chip->card->dev, "AC97 init failed, err %d!\n", rc);
Andreas Mohrb5dc20c2011-02-19 00:49:32 +0100803
804 /* If we return an error here, then snd_card_free() should
805 * free up any ac97 codecs that got created, as well as the bus.
806 */
807 return rc;
808}
809#else /* AZF_USE_AC97_LAYER */
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200810static void
Andreas Mohr02330fb2008-05-16 12:18:29 +0200811snd_azf3328_mixer_write_volume_gradually(const struct snd_azf3328 *chip,
812 unsigned reg,
813 unsigned char dst_vol_left,
814 unsigned char dst_vol_right,
815 int chan_sel, int delay
816)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700817{
Andreas Mohr02330fb2008-05-16 12:18:29 +0200818 unsigned long portbase = chip->mixer_io + reg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700819 unsigned char curr_vol_left = 0, curr_vol_right = 0;
Andreas Mohr02330fb2008-05-16 12:18:29 +0200820 int left_change = 0, right_change = 0;
821
Andreas Mohr02330fb2008-05-16 12:18:29 +0200822 if (chan_sel & SET_CHAN_LEFT) {
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200823 curr_vol_left = inb(portbase + 1);
Andreas Mohr02330fb2008-05-16 12:18:29 +0200824
825 /* take care of muting flag contained in left channel */
826 if (curr_vol_left & AZF_MUTE_BIT)
827 dst_vol_left |= AZF_MUTE_BIT;
828 else
829 dst_vol_left &= ~AZF_MUTE_BIT;
830
831 left_change = (curr_vol_left > dst_vol_left) ? -1 : 1;
832 }
833
834 if (chan_sel & SET_CHAN_RIGHT) {
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200835 curr_vol_right = inb(portbase + 0);
Andreas Mohr02330fb2008-05-16 12:18:29 +0200836
837 right_change = (curr_vol_right > dst_vol_right) ? -1 : 1;
838 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700839
Andreas Mohre2f87262006-05-17 11:04:19 +0200840 do {
Andreas Mohr02330fb2008-05-16 12:18:29 +0200841 if (left_change) {
842 if (curr_vol_left != dst_vol_left) {
843 curr_vol_left += left_change;
844 outb(curr_vol_left, portbase + 1);
845 } else
846 left_change = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700847 }
Andreas Mohr02330fb2008-05-16 12:18:29 +0200848 if (right_change) {
849 if (curr_vol_right != dst_vol_right) {
850 curr_vol_right += right_change;
851
Linus Torvalds1da177e2005-04-16 15:20:36 -0700852 /* during volume change, the right channel is crackling
853 * somewhat more than the left channel, unfortunately.
854 * This seems to be a hardware issue. */
Andreas Mohr02330fb2008-05-16 12:18:29 +0200855 outb(curr_vol_right, portbase + 0);
856 } else
857 right_change = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700858 }
859 if (delay)
860 mdelay(delay);
Andreas Mohr02330fb2008-05-16 12:18:29 +0200861 } while ((left_change) || (right_change));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700862}
863
864/*
865 * general mixer element
866 */
Takashi Iwai95de7762005-11-17 15:02:42 +0100867struct azf3328_mixer_reg {
Andreas Mohr02330fb2008-05-16 12:18:29 +0200868 unsigned reg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700869 unsigned int lchan_shift, rchan_shift;
870 unsigned int mask;
871 unsigned int invert: 1;
872 unsigned int stereo: 1;
873 unsigned int enum_c: 4;
Takashi Iwai95de7762005-11-17 15:02:42 +0100874};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700875
876#define COMPOSE_MIXER_REG(reg,lchan_shift,rchan_shift,mask,invert,stereo,enum_c) \
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200877 ((reg) | (lchan_shift << 8) | (rchan_shift << 12) | \
878 (mask << 16) | \
879 (invert << 24) | \
880 (stereo << 25) | \
881 (enum_c << 26))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700882
Takashi Iwai95de7762005-11-17 15:02:42 +0100883static void snd_azf3328_mixer_reg_decode(struct azf3328_mixer_reg *r, unsigned long val)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700884{
885 r->reg = val & 0xff;
886 r->lchan_shift = (val >> 8) & 0x0f;
887 r->rchan_shift = (val >> 12) & 0x0f;
888 r->mask = (val >> 16) & 0xff;
889 r->invert = (val >> 24) & 1;
890 r->stereo = (val >> 25) & 1;
891 r->enum_c = (val >> 26) & 0x0f;
892}
893
894/*
895 * mixer switches/volumes
896 */
897
898#define AZF3328_MIXER_SWITCH(xname, reg, shift, invert) \
899{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
900 .info = snd_azf3328_info_mixer, \
901 .get = snd_azf3328_get_mixer, .put = snd_azf3328_put_mixer, \
902 .private_value = COMPOSE_MIXER_REG(reg, shift, 0, 0x1, invert, 0, 0), \
903}
904
905#define AZF3328_MIXER_VOL_STEREO(xname, reg, mask, invert) \
906{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
907 .info = snd_azf3328_info_mixer, \
908 .get = snd_azf3328_get_mixer, .put = snd_azf3328_put_mixer, \
909 .private_value = COMPOSE_MIXER_REG(reg, 8, 0, mask, invert, 1, 0), \
910}
911
912#define AZF3328_MIXER_VOL_MONO(xname, reg, mask, is_right_chan) \
913{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
914 .info = snd_azf3328_info_mixer, \
915 .get = snd_azf3328_get_mixer, .put = snd_azf3328_put_mixer, \
916 .private_value = COMPOSE_MIXER_REG(reg, is_right_chan ? 0 : 8, 0, mask, 1, 0, 0), \
917}
918
919#define AZF3328_MIXER_VOL_SPECIAL(xname, reg, mask, shift, invert) \
920{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
921 .info = snd_azf3328_info_mixer, \
922 .get = snd_azf3328_get_mixer, .put = snd_azf3328_put_mixer, \
923 .private_value = COMPOSE_MIXER_REG(reg, shift, 0, mask, invert, 0, 0), \
924}
925
926#define AZF3328_MIXER_ENUM(xname, reg, enum_c, shift) \
927{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
928 .info = snd_azf3328_info_mixer_enum, \
929 .get = snd_azf3328_get_mixer_enum, .put = snd_azf3328_put_mixer_enum, \
930 .private_value = COMPOSE_MIXER_REG(reg, shift, 0, 0, 0, 0, enum_c), \
931}
932
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200933static int
Takashi Iwai95de7762005-11-17 15:02:42 +0100934snd_azf3328_info_mixer(struct snd_kcontrol *kcontrol,
935 struct snd_ctl_elem_info *uinfo)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700936{
Takashi Iwai95de7762005-11-17 15:02:42 +0100937 struct azf3328_mixer_reg reg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700938
Linus Torvalds1da177e2005-04-16 15:20:36 -0700939 snd_azf3328_mixer_reg_decode(&reg, kcontrol->private_value);
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200940 uinfo->type = reg.mask == 1 ?
941 SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700942 uinfo->count = reg.stereo + 1;
943 uinfo->value.integer.min = 0;
944 uinfo->value.integer.max = reg.mask;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700945 return 0;
946}
947
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200948static int
Takashi Iwai95de7762005-11-17 15:02:42 +0100949snd_azf3328_get_mixer(struct snd_kcontrol *kcontrol,
950 struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700951{
Takashi Iwai95de7762005-11-17 15:02:42 +0100952 struct snd_azf3328 *chip = snd_kcontrol_chip(kcontrol);
953 struct azf3328_mixer_reg reg;
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200954 u16 oreg, val;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700955
Linus Torvalds1da177e2005-04-16 15:20:36 -0700956 snd_azf3328_mixer_reg_decode(&reg, kcontrol->private_value);
957
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200958 oreg = snd_azf3328_mixer_inw(chip, reg.reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700959 val = (oreg >> reg.lchan_shift) & reg.mask;
960 if (reg.invert)
961 val = reg.mask - val;
962 ucontrol->value.integer.value[0] = val;
963 if (reg.stereo) {
964 val = (oreg >> reg.rchan_shift) & reg.mask;
965 if (reg.invert)
966 val = reg.mask - val;
967 ucontrol->value.integer.value[1] = val;
968 }
Takashi Iwai4a8d9d72014-02-25 14:04:46 +0100969 dev_dbg(chip->card->dev,
970 "get: %02x is %04x -> vol %02lx|%02lx (shift %02d|%02d, mask %02x, inv. %d, stereo %d)\n",
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200971 reg.reg, oreg,
972 ucontrol->value.integer.value[0], ucontrol->value.integer.value[1],
973 reg.lchan_shift, reg.rchan_shift, reg.mask, reg.invert, reg.stereo);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700974 return 0;
975}
976
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200977static int
Takashi Iwai95de7762005-11-17 15:02:42 +0100978snd_azf3328_put_mixer(struct snd_kcontrol *kcontrol,
979 struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700980{
Takashi Iwai95de7762005-11-17 15:02:42 +0100981 struct snd_azf3328 *chip = snd_kcontrol_chip(kcontrol);
982 struct azf3328_mixer_reg reg;
Andreas Mohrdfbf9512009-07-05 13:55:46 +0200983 u16 oreg, nreg, val;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700984
Linus Torvalds1da177e2005-04-16 15:20:36 -0700985 snd_azf3328_mixer_reg_decode(&reg, kcontrol->private_value);
Andreas Mohrd91c64c2005-10-25 11:17:45 +0200986 oreg = snd_azf3328_mixer_inw(chip, reg.reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700987 val = ucontrol->value.integer.value[0] & reg.mask;
988 if (reg.invert)
989 val = reg.mask - val;
990 nreg = oreg & ~(reg.mask << reg.lchan_shift);
991 nreg |= (val << reg.lchan_shift);
992 if (reg.stereo) {
993 val = ucontrol->value.integer.value[1] & reg.mask;
994 if (reg.invert)
995 val = reg.mask - val;
996 nreg &= ~(reg.mask << reg.rchan_shift);
997 nreg |= (val << reg.rchan_shift);
998 }
999 if (reg.mask >= 0x07) /* it's a volume control, so better take care */
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001000 snd_azf3328_mixer_write_volume_gradually(
1001 chip, reg.reg, nreg >> 8, nreg & 0xff,
1002 /* just set both channels, doesn't matter */
1003 SET_CHAN_LEFT|SET_CHAN_RIGHT,
1004 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001005 else
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001006 snd_azf3328_mixer_outw(chip, reg.reg, nreg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001007
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001008 dev_dbg(chip->card->dev,
1009 "put: %02x to %02lx|%02lx, oreg %04x; shift %02d|%02d -> nreg %04x; after: %04x\n",
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001010 reg.reg, ucontrol->value.integer.value[0], ucontrol->value.integer.value[1],
1011 oreg, reg.lchan_shift, reg.rchan_shift,
1012 nreg, snd_azf3328_mixer_inw(chip, reg.reg));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001013 return (nreg != oreg);
1014}
1015
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001016static int
Takashi Iwai95de7762005-11-17 15:02:42 +01001017snd_azf3328_info_mixer_enum(struct snd_kcontrol *kcontrol,
1018 struct snd_ctl_elem_info *uinfo)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001019{
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001020 static const char * const texts1[] = {
Andreas Mohr13769e32006-05-17 11:03:16 +02001021 "Mic1", "Mic2"
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001022 };
1023 static const char * const texts2[] = {
Andreas Mohr13769e32006-05-17 11:03:16 +02001024 "Mix", "Mic"
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001025 };
1026 static const char * const texts3[] = {
Andreas Mohr02330fb2008-05-16 12:18:29 +02001027 "Mic", "CD", "Video", "Aux",
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001028 "Line", "Mix", "Mix Mono", "Phone"
Linus Torvalds1da177e2005-04-16 15:20:36 -07001029 };
Andreas Mohr13769e32006-05-17 11:03:16 +02001030 static const char * const texts4[] = {
1031 "pre 3D", "post 3D"
1032 };
Takashi Iwai95de7762005-11-17 15:02:42 +01001033 struct azf3328_mixer_reg reg;
Andreas Mohr627d3e72008-06-23 11:50:47 +02001034 const char * const *p = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001035
1036 snd_azf3328_mixer_reg_decode(&reg, kcontrol->private_value);
Andreas Mohre2f87262006-05-17 11:04:19 +02001037 if (reg.reg == IDX_MIXER_ADVCTL2) {
Andreas Mohr13769e32006-05-17 11:03:16 +02001038 switch(reg.lchan_shift) {
1039 case 8: /* modem out sel */
Andreas Mohr627d3e72008-06-23 11:50:47 +02001040 p = texts1;
Andreas Mohr13769e32006-05-17 11:03:16 +02001041 break;
1042 case 9: /* mono sel source */
Andreas Mohr627d3e72008-06-23 11:50:47 +02001043 p = texts2;
Andreas Mohr13769e32006-05-17 11:03:16 +02001044 break;
1045 case 15: /* PCM Out Path */
Andreas Mohr627d3e72008-06-23 11:50:47 +02001046 p = texts4;
Andreas Mohr13769e32006-05-17 11:03:16 +02001047 break;
1048 }
Takashi Iwai9b311a02014-10-20 18:16:13 +02001049 } else if (reg.reg == IDX_MIXER_REC_SELECT)
Andreas Mohr627d3e72008-06-23 11:50:47 +02001050 p = texts3;
Andreas Mohr02330fb2008-05-16 12:18:29 +02001051
Takashi Iwai9b311a02014-10-20 18:16:13 +02001052 return snd_ctl_enum_info(uinfo,
1053 (reg.reg == IDX_MIXER_REC_SELECT) ? 2 : 1,
1054 reg.enum_c, p);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001055}
1056
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001057static int
Takashi Iwai95de7762005-11-17 15:02:42 +01001058snd_azf3328_get_mixer_enum(struct snd_kcontrol *kcontrol,
1059 struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001060{
Takashi Iwai95de7762005-11-17 15:02:42 +01001061 struct snd_azf3328 *chip = snd_kcontrol_chip(kcontrol);
1062 struct azf3328_mixer_reg reg;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001063 unsigned short val;
Andreas Mohr02330fb2008-05-16 12:18:29 +02001064
Linus Torvalds1da177e2005-04-16 15:20:36 -07001065 snd_azf3328_mixer_reg_decode(&reg, kcontrol->private_value);
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001066 val = snd_azf3328_mixer_inw(chip, reg.reg);
Andreas Mohre2f87262006-05-17 11:04:19 +02001067 if (reg.reg == IDX_MIXER_REC_SELECT) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001068 ucontrol->value.enumerated.item[0] = (val >> 8) & (reg.enum_c - 1);
1069 ucontrol->value.enumerated.item[1] = (val >> 0) & (reg.enum_c - 1);
Andreas Mohre2f87262006-05-17 11:04:19 +02001070 } else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001071 ucontrol->value.enumerated.item[0] = (val >> reg.lchan_shift) & (reg.enum_c - 1);
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001072
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001073 dev_dbg(chip->card->dev,
1074 "get_enum: %02x is %04x -> %d|%d (shift %02d, enum_c %d)\n",
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001075 reg.reg, val, ucontrol->value.enumerated.item[0], ucontrol->value.enumerated.item[1],
1076 reg.lchan_shift, reg.enum_c);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001077 return 0;
1078}
1079
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001080static int
Takashi Iwai95de7762005-11-17 15:02:42 +01001081snd_azf3328_put_mixer_enum(struct snd_kcontrol *kcontrol,
1082 struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001083{
Takashi Iwai95de7762005-11-17 15:02:42 +01001084 struct snd_azf3328 *chip = snd_kcontrol_chip(kcontrol);
1085 struct azf3328_mixer_reg reg;
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001086 u16 oreg, nreg, val;
Andreas Mohr02330fb2008-05-16 12:18:29 +02001087
Linus Torvalds1da177e2005-04-16 15:20:36 -07001088 snd_azf3328_mixer_reg_decode(&reg, kcontrol->private_value);
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001089 oreg = snd_azf3328_mixer_inw(chip, reg.reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001090 val = oreg;
Andreas Mohre2f87262006-05-17 11:04:19 +02001091 if (reg.reg == IDX_MIXER_REC_SELECT) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001092 if (ucontrol->value.enumerated.item[0] > reg.enum_c - 1U ||
1093 ucontrol->value.enumerated.item[1] > reg.enum_c - 1U)
1094 return -EINVAL;
1095 val = (ucontrol->value.enumerated.item[0] << 8) |
1096 (ucontrol->value.enumerated.item[1] << 0);
Andreas Mohre2f87262006-05-17 11:04:19 +02001097 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001098 if (ucontrol->value.enumerated.item[0] > reg.enum_c - 1U)
1099 return -EINVAL;
1100 val &= ~((reg.enum_c - 1) << reg.lchan_shift);
1101 val |= (ucontrol->value.enumerated.item[0] << reg.lchan_shift);
1102 }
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001103 snd_azf3328_mixer_outw(chip, reg.reg, val);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001104 nreg = val;
1105
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001106 dev_dbg(chip->card->dev,
1107 "put_enum: %02x to %04x, oreg %04x\n", reg.reg, val, oreg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001108 return (nreg != oreg);
1109}
1110
Bill Pembertone23e7a12012-12-06 12:35:10 -05001111static struct snd_kcontrol_new snd_azf3328_mixer_controls[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001112 AZF3328_MIXER_SWITCH("Master Playback Switch", IDX_MIXER_PLAY_MASTER, 15, 1),
1113 AZF3328_MIXER_VOL_STEREO("Master Playback Volume", IDX_MIXER_PLAY_MASTER, 0x1f, 1),
Andreas Mohr627d3e72008-06-23 11:50:47 +02001114 AZF3328_MIXER_SWITCH("PCM Playback Switch", IDX_MIXER_WAVEOUT, 15, 1),
1115 AZF3328_MIXER_VOL_STEREO("PCM Playback Volume",
1116 IDX_MIXER_WAVEOUT, 0x1f, 1),
1117 AZF3328_MIXER_SWITCH("PCM 3D Bypass Playback Switch",
1118 IDX_MIXER_ADVCTL2, 7, 1),
Linus Torvalds1da177e2005-04-16 15:20:36 -07001119 AZF3328_MIXER_SWITCH("FM Playback Switch", IDX_MIXER_FMSYNTH, 15, 1),
1120 AZF3328_MIXER_VOL_STEREO("FM Playback Volume", IDX_MIXER_FMSYNTH, 0x1f, 1),
1121 AZF3328_MIXER_SWITCH("CD Playback Switch", IDX_MIXER_CDAUDIO, 15, 1),
1122 AZF3328_MIXER_VOL_STEREO("CD Playback Volume", IDX_MIXER_CDAUDIO, 0x1f, 1),
1123 AZF3328_MIXER_SWITCH("Capture Switch", IDX_MIXER_REC_VOLUME, 15, 1),
1124 AZF3328_MIXER_VOL_STEREO("Capture Volume", IDX_MIXER_REC_VOLUME, 0x0f, 0),
1125 AZF3328_MIXER_ENUM("Capture Source", IDX_MIXER_REC_SELECT, 8, 0),
1126 AZF3328_MIXER_SWITCH("Mic Playback Switch", IDX_MIXER_MIC, 15, 1),
1127 AZF3328_MIXER_VOL_MONO("Mic Playback Volume", IDX_MIXER_MIC, 0x1f, 1),
1128 AZF3328_MIXER_SWITCH("Mic Boost (+20dB)", IDX_MIXER_MIC, 6, 0),
1129 AZF3328_MIXER_SWITCH("Line Playback Switch", IDX_MIXER_LINEIN, 15, 1),
1130 AZF3328_MIXER_VOL_STEREO("Line Playback Volume", IDX_MIXER_LINEIN, 0x1f, 1),
Jaroslav Kyselad355c82a2009-11-03 15:47:25 +01001131 AZF3328_MIXER_SWITCH("Beep Playback Switch", IDX_MIXER_PCBEEP, 15, 1),
1132 AZF3328_MIXER_VOL_SPECIAL("Beep Playback Volume", IDX_MIXER_PCBEEP, 0x0f, 1, 1),
Linus Torvalds1da177e2005-04-16 15:20:36 -07001133 AZF3328_MIXER_SWITCH("Video Playback Switch", IDX_MIXER_VIDEO, 15, 1),
1134 AZF3328_MIXER_VOL_STEREO("Video Playback Volume", IDX_MIXER_VIDEO, 0x1f, 1),
1135 AZF3328_MIXER_SWITCH("Aux Playback Switch", IDX_MIXER_AUX, 15, 1),
1136 AZF3328_MIXER_VOL_STEREO("Aux Playback Volume", IDX_MIXER_AUX, 0x1f, 1),
1137 AZF3328_MIXER_SWITCH("Modem Playback Switch", IDX_MIXER_MODEMOUT, 15, 1),
1138 AZF3328_MIXER_VOL_MONO("Modem Playback Volume", IDX_MIXER_MODEMOUT, 0x1f, 1),
1139 AZF3328_MIXER_SWITCH("Modem Capture Switch", IDX_MIXER_MODEMIN, 15, 1),
1140 AZF3328_MIXER_VOL_MONO("Modem Capture Volume", IDX_MIXER_MODEMIN, 0x1f, 1),
Andreas Mohr13769e32006-05-17 11:03:16 +02001141 AZF3328_MIXER_ENUM("Mic Select", IDX_MIXER_ADVCTL2, 2, 8),
1142 AZF3328_MIXER_ENUM("Mono Output Select", IDX_MIXER_ADVCTL2, 2, 9),
Andreas Mohre24a1212007-03-26 12:49:45 +02001143 AZF3328_MIXER_ENUM("PCM Output Route", IDX_MIXER_ADVCTL2, 2, 15), /* PCM Out Path, place in front since it controls *both* 3D and Bass/Treble! */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001144 AZF3328_MIXER_VOL_SPECIAL("Tone Control - Treble", IDX_MIXER_BASSTREBLE, 0x07, 1, 0),
1145 AZF3328_MIXER_VOL_SPECIAL("Tone Control - Bass", IDX_MIXER_BASSTREBLE, 0x07, 9, 0),
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001146 AZF3328_MIXER_SWITCH("3D Control - Switch", IDX_MIXER_ADVCTL2, 13, 0),
Andreas Mohr13769e32006-05-17 11:03:16 +02001147 AZF3328_MIXER_VOL_SPECIAL("3D Control - Width", IDX_MIXER_ADVCTL1, 0x07, 1, 0), /* "3D Width" */
1148 AZF3328_MIXER_VOL_SPECIAL("3D Control - Depth", IDX_MIXER_ADVCTL1, 0x03, 8, 0), /* "Hifi 3D" */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001149#if MIXER_TESTING
1150 AZF3328_MIXER_SWITCH("0", IDX_MIXER_ADVCTL2, 0, 0),
1151 AZF3328_MIXER_SWITCH("1", IDX_MIXER_ADVCTL2, 1, 0),
1152 AZF3328_MIXER_SWITCH("2", IDX_MIXER_ADVCTL2, 2, 0),
1153 AZF3328_MIXER_SWITCH("3", IDX_MIXER_ADVCTL2, 3, 0),
1154 AZF3328_MIXER_SWITCH("4", IDX_MIXER_ADVCTL2, 4, 0),
1155 AZF3328_MIXER_SWITCH("5", IDX_MIXER_ADVCTL2, 5, 0),
1156 AZF3328_MIXER_SWITCH("6", IDX_MIXER_ADVCTL2, 6, 0),
1157 AZF3328_MIXER_SWITCH("7", IDX_MIXER_ADVCTL2, 7, 0),
1158 AZF3328_MIXER_SWITCH("8", IDX_MIXER_ADVCTL2, 8, 0),
1159 AZF3328_MIXER_SWITCH("9", IDX_MIXER_ADVCTL2, 9, 0),
1160 AZF3328_MIXER_SWITCH("10", IDX_MIXER_ADVCTL2, 10, 0),
1161 AZF3328_MIXER_SWITCH("11", IDX_MIXER_ADVCTL2, 11, 0),
1162 AZF3328_MIXER_SWITCH("12", IDX_MIXER_ADVCTL2, 12, 0),
1163 AZF3328_MIXER_SWITCH("13", IDX_MIXER_ADVCTL2, 13, 0),
1164 AZF3328_MIXER_SWITCH("14", IDX_MIXER_ADVCTL2, 14, 0),
1165 AZF3328_MIXER_SWITCH("15", IDX_MIXER_ADVCTL2, 15, 0),
1166#endif
1167};
1168
Bill Pembertone23e7a12012-12-06 12:35:10 -05001169static u16 snd_azf3328_init_values[][2] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001170 { IDX_MIXER_PLAY_MASTER, MIXER_MUTE_MASK|0x1f1f },
1171 { IDX_MIXER_MODEMOUT, MIXER_MUTE_MASK|0x1f1f },
1172 { IDX_MIXER_BASSTREBLE, 0x0000 },
1173 { IDX_MIXER_PCBEEP, MIXER_MUTE_MASK|0x1f1f },
1174 { IDX_MIXER_MODEMIN, MIXER_MUTE_MASK|0x1f1f },
1175 { IDX_MIXER_MIC, MIXER_MUTE_MASK|0x001f },
1176 { IDX_MIXER_LINEIN, MIXER_MUTE_MASK|0x1f1f },
1177 { IDX_MIXER_CDAUDIO, MIXER_MUTE_MASK|0x1f1f },
1178 { IDX_MIXER_VIDEO, MIXER_MUTE_MASK|0x1f1f },
1179 { IDX_MIXER_AUX, MIXER_MUTE_MASK|0x1f1f },
1180 { IDX_MIXER_WAVEOUT, MIXER_MUTE_MASK|0x1f1f },
1181 { IDX_MIXER_FMSYNTH, MIXER_MUTE_MASK|0x1f1f },
1182 { IDX_MIXER_REC_VOLUME, MIXER_MUTE_MASK|0x0707 },
1183};
1184
Bill Pembertone23e7a12012-12-06 12:35:10 -05001185static int
Takashi Iwai95de7762005-11-17 15:02:42 +01001186snd_azf3328_mixer_new(struct snd_azf3328 *chip)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001187{
Takashi Iwai95de7762005-11-17 15:02:42 +01001188 struct snd_card *card;
1189 const struct snd_kcontrol_new *sw;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001190 unsigned int idx;
1191 int err;
1192
Takashi Iwaida3cec32008-08-08 17:12:14 +02001193 if (snd_BUG_ON(!chip || !chip->card))
1194 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001195
1196 card = chip->card;
1197
1198 /* mixer reset */
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001199 snd_azf3328_mixer_outw(chip, IDX_MIXER_RESET, 0x0000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001200
1201 /* mute and zero volume channels */
Andreas Mohr02330fb2008-05-16 12:18:29 +02001202 for (idx = 0; idx < ARRAY_SIZE(snd_azf3328_init_values); ++idx) {
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001203 snd_azf3328_mixer_outw(chip,
1204 snd_azf3328_init_values[idx][0],
1205 snd_azf3328_init_values[idx][1]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001206 }
Andreas Mohr02330fb2008-05-16 12:18:29 +02001207
Linus Torvalds1da177e2005-04-16 15:20:36 -07001208 /* add mixer controls */
1209 sw = snd_azf3328_mixer_controls;
Andreas Mohr02330fb2008-05-16 12:18:29 +02001210 for (idx = 0; idx < ARRAY_SIZE(snd_azf3328_mixer_controls);
1211 ++idx, ++sw) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001212 if ((err = snd_ctl_add(chip->card, snd_ctl_new1(sw, chip))) < 0)
1213 return err;
1214 }
1215 snd_component_add(card, "AZF3328 mixer");
1216 strcpy(card->mixername, "AZF3328 mixer");
1217
Linus Torvalds1da177e2005-04-16 15:20:36 -07001218 return 0;
1219}
Andreas Mohrb5dc20c2011-02-19 00:49:32 +01001220#endif /* AZF_USE_AC97_LAYER */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001221
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001222static int
Takashi Iwai95de7762005-11-17 15:02:42 +01001223snd_azf3328_hw_params(struct snd_pcm_substream *substream,
1224 struct snd_pcm_hw_params *hw_params)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001225{
Takashi Iwai4162cd32014-02-25 12:59:05 +01001226 return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001227}
1228
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001229static int
Takashi Iwai95de7762005-11-17 15:02:42 +01001230snd_azf3328_hw_free(struct snd_pcm_substream *substream)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001231{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001232 snd_pcm_lib_free_pages(substream);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001233 return 0;
1234}
1235
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001236static void
Andreas Mohrda237f32010-12-27 21:17:26 +01001237snd_azf3328_codec_setfmt(struct snd_azf3328_codec_data *codec,
Andreas Mohr627d3e72008-06-23 11:50:47 +02001238 enum azf_freq_t bitrate,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001239 unsigned int format_width,
1240 unsigned int channels
1241)
1242{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001243 unsigned long flags;
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001244 u16 val = 0xff00;
Andreas Mohr8d9a1142010-12-27 21:16:49 +01001245 u8 freq = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001246
Linus Torvalds1da177e2005-04-16 15:20:36 -07001247 switch (bitrate) {
Andreas Mohrc9ba3742011-01-25 06:46:31 +01001248 case AZF_FREQ_4000: freq = SOUNDFORMAT_FREQ_SUSPECTED_4000; break;
1249 case AZF_FREQ_4800: freq = SOUNDFORMAT_FREQ_SUSPECTED_4800; break;
1250 case AZF_FREQ_5512:
1251 /* the AZF3328 names it "5510" for some strange reason */
1252 freq = SOUNDFORMAT_FREQ_5510; break;
1253 case AZF_FREQ_6620: freq = SOUNDFORMAT_FREQ_6620; break;
1254 case AZF_FREQ_8000: freq = SOUNDFORMAT_FREQ_8000; break;
1255 case AZF_FREQ_9600: freq = SOUNDFORMAT_FREQ_9600; break;
1256 case AZF_FREQ_11025: freq = SOUNDFORMAT_FREQ_11025; break;
1257 case AZF_FREQ_13240: freq = SOUNDFORMAT_FREQ_SUSPECTED_13240; break;
1258 case AZF_FREQ_16000: freq = SOUNDFORMAT_FREQ_16000; break;
1259 case AZF_FREQ_22050: freq = SOUNDFORMAT_FREQ_22050; break;
1260 case AZF_FREQ_32000: freq = SOUNDFORMAT_FREQ_32000; break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001261 default:
Takashi Iwai99b359b2005-10-20 18:26:44 +02001262 snd_printk(KERN_WARNING "unknown bitrate %d, assuming 44.1kHz!\n", bitrate);
Andreas Mohr02330fb2008-05-16 12:18:29 +02001263 /* fall-through */
Andreas Mohrc9ba3742011-01-25 06:46:31 +01001264 case AZF_FREQ_44100: freq = SOUNDFORMAT_FREQ_44100; break;
1265 case AZF_FREQ_48000: freq = SOUNDFORMAT_FREQ_48000; break;
1266 case AZF_FREQ_66200: freq = SOUNDFORMAT_FREQ_SUSPECTED_66200; break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001267 }
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001268 /* val = 0xff07; 3m27.993s (65301Hz; -> 64000Hz???) hmm, 66120, 65967, 66123 */
1269 /* val = 0xff09; 17m15.098s (13123,478Hz; -> 12000Hz???) hmm, 13237.2Hz? */
1270 /* val = 0xff0a; 47m30.599s (4764,891Hz; -> 4800Hz???) yup, 4803Hz */
1271 /* val = 0xff0c; 57m0.510s (4010,263Hz; -> 4000Hz???) yup, 4003Hz */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001272 /* val = 0xff05; 5m11.556s (... -> 44100Hz) */
1273 /* val = 0xff03; 10m21.529s (21872,463Hz; -> 22050Hz???) */
1274 /* val = 0xff0f; 20m41.883s (10937,993Hz; -> 11025Hz???) */
1275 /* val = 0xff0d; 41m23.135s (5523,600Hz; -> 5512Hz???) */
1276 /* val = 0xff0e; 28m30.777s (8017Hz; -> 8000Hz???) */
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001277
Andreas Mohr8d9a1142010-12-27 21:16:49 +01001278 val |= freq;
1279
Linus Torvalds1da177e2005-04-16 15:20:36 -07001280 if (channels == 2)
1281 val |= SOUNDFORMAT_FLAG_2CHANNELS;
1282
1283 if (format_width == 16)
1284 val |= SOUNDFORMAT_FLAG_16BIT;
1285
Andreas Mohrda237f32010-12-27 21:17:26 +01001286 spin_lock_irqsave(codec->lock, flags);
Andreas Mohr02330fb2008-05-16 12:18:29 +02001287
Linus Torvalds1da177e2005-04-16 15:20:36 -07001288 /* set bitrate/format */
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001289 snd_azf3328_codec_outw(codec, IDX_IO_CODEC_SOUNDFORMAT, val);
Andreas Mohr02330fb2008-05-16 12:18:29 +02001290
Linus Torvalds1da177e2005-04-16 15:20:36 -07001291 /* changing the bitrate/format settings switches off the
1292 * audio output with an annoying click in case of 8/16bit format change
1293 * (maybe shutting down DAC/ADC?), thus immediately
1294 * do some tweaking to reenable it and get rid of the clicking
1295 * (FIXME: yes, it works, but what exactly am I doing here?? :)
1296 * FIXME: does this have some side effects for full-duplex
1297 * or other dramatic side effects? */
Andreas Mohradf59312010-12-27 21:16:43 +01001298 /* do it for non-capture codecs only */
Andreas Mohrda237f32010-12-27 21:17:26 +01001299 if (codec->type != AZF_CODEC_CAPTURE)
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001300 snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS,
1301 snd_azf3328_codec_inw(codec, IDX_IO_CODEC_DMA_FLAGS) |
1302 DMA_RUN_SOMETHING1 |
1303 DMA_RUN_SOMETHING2 |
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001304 SOMETHING_ALMOST_ALWAYS_SET |
1305 DMA_EPILOGUE_SOMETHING |
1306 DMA_SOMETHING_ELSE
1307 );
Linus Torvalds1da177e2005-04-16 15:20:36 -07001308
Andreas Mohrda237f32010-12-27 21:17:26 +01001309 spin_unlock_irqrestore(codec->lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001310}
1311
Andreas Mohr02330fb2008-05-16 12:18:29 +02001312static inline void
Andreas Mohrda237f32010-12-27 21:17:26 +01001313snd_azf3328_codec_setfmt_lowpower(struct snd_azf3328_codec_data *codec
Andreas Mohr02330fb2008-05-16 12:18:29 +02001314)
1315{
1316 /* choose lowest frequency for low power consumption.
1317 * While this will cause louder noise due to rather coarse frequency,
1318 * it should never matter since output should always
1319 * get disabled properly when idle anyway. */
Andreas Mohrda237f32010-12-27 21:17:26 +01001320 snd_azf3328_codec_setfmt(codec, AZF_FREQ_4000, 8, 1);
Andreas Mohr02330fb2008-05-16 12:18:29 +02001321}
1322
Andreas Mohr627d3e72008-06-23 11:50:47 +02001323static void
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001324snd_azf3328_ctrl_reg_6AH_update(struct snd_azf3328 *chip,
Andreas Mohr627d3e72008-06-23 11:50:47 +02001325 unsigned bitmask,
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001326 bool enable
Andreas Mohr627d3e72008-06-23 11:50:47 +02001327)
1328{
Andreas Mohr78df6172009-07-12 22:17:54 +02001329 bool do_mask = !enable;
1330 if (do_mask)
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001331 chip->shadow_reg_ctrl_6AH |= bitmask;
Andreas Mohr78df6172009-07-12 22:17:54 +02001332 else
1333 chip->shadow_reg_ctrl_6AH &= ~bitmask;
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001334 dev_dbg(chip->card->dev,
1335 "6AH_update mask 0x%04x do_mask %d: val 0x%04x\n",
1336 bitmask, do_mask, chip->shadow_reg_ctrl_6AH);
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001337 snd_azf3328_ctrl_outw(chip, IDX_IO_6AH, chip->shadow_reg_ctrl_6AH);
Andreas Mohr627d3e72008-06-23 11:50:47 +02001338}
1339
Andreas Mohr02330fb2008-05-16 12:18:29 +02001340static inline void
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001341snd_azf3328_ctrl_enable_codecs(struct snd_azf3328 *chip, bool enable)
Andreas Mohr02330fb2008-05-16 12:18:29 +02001342{
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001343 dev_dbg(chip->card->dev, "codec_enable %d\n", enable);
Andreas Mohr02330fb2008-05-16 12:18:29 +02001344 /* no idea what exactly is being done here, but I strongly assume it's
1345 * PM related */
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001346 snd_azf3328_ctrl_reg_6AH_update(
Andreas Mohr627d3e72008-06-23 11:50:47 +02001347 chip, IO_6A_PAUSE_PLAYBACK_BIT8, enable
Andreas Mohr02330fb2008-05-16 12:18:29 +02001348 );
1349}
1350
1351static void
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001352snd_azf3328_ctrl_codec_activity(struct snd_azf3328 *chip,
1353 enum snd_azf3328_codec_type codec_type,
1354 bool enable
Andreas Mohr02330fb2008-05-16 12:18:29 +02001355)
1356{
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001357 struct snd_azf3328_codec_data *codec = &chip->codecs[codec_type];
1358 bool need_change = (codec->running != enable);
Andreas Mohr02330fb2008-05-16 12:18:29 +02001359
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001360 dev_dbg(chip->card->dev,
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001361 "codec_activity: %s codec, enable %d, need_change %d\n",
1362 codec->name, enable, need_change
Andreas Mohr02330fb2008-05-16 12:18:29 +02001363 );
1364 if (need_change) {
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001365 static const struct {
1366 enum snd_azf3328_codec_type other1;
1367 enum snd_azf3328_codec_type other2;
1368 } peer_codecs[3] =
1369 { { AZF_CODEC_CAPTURE, AZF_CODEC_I2S_OUT },
1370 { AZF_CODEC_PLAYBACK, AZF_CODEC_I2S_OUT },
1371 { AZF_CODEC_PLAYBACK, AZF_CODEC_CAPTURE } };
1372 bool call_function;
1373
1374 if (enable)
1375 /* if enable codec, call enable_codecs func
1376 to enable codec supply... */
1377 call_function = 1;
1378 else {
1379 /* ...otherwise call enable_codecs func
1380 (which globally shuts down operation of codecs)
1381 only in case the other codecs are currently
1382 not active either! */
Andreas Mohr78df6172009-07-12 22:17:54 +02001383 call_function =
1384 ((!chip->codecs[peer_codecs[codec_type].other1]
1385 .running)
1386 && (!chip->codecs[peer_codecs[codec_type].other2]
1387 .running));
Dan Carpenter7f788e02015-02-25 16:32:45 +03001388 }
1389 if (call_function)
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001390 snd_azf3328_ctrl_enable_codecs(chip, enable);
Andreas Mohr02330fb2008-05-16 12:18:29 +02001391
1392 /* ...and adjust clock, too
1393 * (reduce noise and power consumption) */
1394 if (!enable)
Andreas Mohrda237f32010-12-27 21:17:26 +01001395 snd_azf3328_codec_setfmt_lowpower(codec);
Andreas Mohr78df6172009-07-12 22:17:54 +02001396 codec->running = enable;
Andreas Mohr02330fb2008-05-16 12:18:29 +02001397 }
Andreas Mohr02330fb2008-05-16 12:18:29 +02001398}
1399
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001400static void
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001401snd_azf3328_codec_setdmaa(struct snd_azf3328 *chip,
1402 struct snd_azf3328_codec_data *codec,
1403 unsigned long addr,
1404 unsigned int period_bytes,
1405 unsigned int buffer_bytes
Andreas Mohr02330fb2008-05-16 12:18:29 +02001406)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001407{
Andreas Mohr689c6912010-12-27 21:17:35 +01001408 WARN_ONCE(period_bytes & 1, "odd period length!?\n");
1409 WARN_ONCE(buffer_bytes != 2 * period_bytes,
1410 "missed our input expectations! %u vs. %u\n",
1411 buffer_bytes, period_bytes);
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001412 if (!codec->running) {
1413 /* AZF3328 uses a two buffer pointer DMA transfer approach */
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001414
Andreas Mohr689c6912010-12-27 21:17:35 +01001415 unsigned long flags;
Andreas Mohr02330fb2008-05-16 12:18:29 +02001416
1417 /* width 32bit (prevent overflow): */
Andreas Mohr689c6912010-12-27 21:17:35 +01001418 u32 area_length;
1419 struct codec_setup_io {
1420 u32 dma_start_1;
1421 u32 dma_start_2;
1422 u32 dma_lengths;
1423 } __attribute__((packed)) setup_io;
Andreas Mohr02330fb2008-05-16 12:18:29 +02001424
Andreas Mohr689c6912010-12-27 21:17:35 +01001425 area_length = buffer_bytes/2;
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001426
Andreas Mohr689c6912010-12-27 21:17:35 +01001427 setup_io.dma_start_1 = addr;
1428 setup_io.dma_start_2 = addr+area_length;
1429
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001430 dev_dbg(chip->card->dev,
Andreas Mohr689c6912010-12-27 21:17:35 +01001431 "setdma: buffers %08x[%u] / %08x[%u], %u, %u\n",
1432 setup_io.dma_start_1, area_length,
1433 setup_io.dma_start_2, area_length,
1434 period_bytes, buffer_bytes);
1435
1436 /* Hmm, are we really supposed to decrement this by 1??
1437 Most definitely certainly not: configuring full length does
1438 work properly (i.e. likely better), and BTW we
1439 violated possibly differing frame sizes with this...
1440
1441 area_length--; |* max. index *|
1442 */
Andreas Mohr79741502010-11-21 12:09:32 +01001443
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001444 /* build combined I/O buffer length word */
Andreas Mohr689c6912010-12-27 21:17:35 +01001445 setup_io.dma_lengths = (area_length << 16) | (area_length);
1446
Andreas Mohrda237f32010-12-27 21:17:26 +01001447 spin_lock_irqsave(codec->lock, flags);
Andreas Mohr689c6912010-12-27 21:17:35 +01001448 snd_azf3328_codec_outl_multi(
1449 codec, IDX_IO_CODEC_DMA_START_1, &setup_io, 3
1450 );
Andreas Mohrda237f32010-12-27 21:17:26 +01001451 spin_unlock_irqrestore(codec->lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001452 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001453}
1454
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001455static int
Andreas Mohrda237f32010-12-27 21:17:26 +01001456snd_azf3328_pcm_prepare(struct snd_pcm_substream *substream)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001457{
Takashi Iwai95de7762005-11-17 15:02:42 +01001458 struct snd_pcm_runtime *runtime = substream->runtime;
Andreas Mohrda237f32010-12-27 21:17:26 +01001459 struct snd_azf3328_codec_data *codec = runtime->private_data;
Andreas Mohr34585592010-12-27 21:17:11 +01001460#if 0
Linus Torvalds1da177e2005-04-16 15:20:36 -07001461 unsigned int size = snd_pcm_lib_buffer_bytes(substream);
1462 unsigned int count = snd_pcm_lib_period_bytes(substream);
1463#endif
1464
Andreas Mohr34585592010-12-27 21:17:11 +01001465 codec->dma_base = runtime->dma_addr;
1466
Linus Torvalds1da177e2005-04-16 15:20:36 -07001467#if 0
Andreas Mohrda237f32010-12-27 21:17:26 +01001468 snd_azf3328_codec_setfmt(codec,
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001469 runtime->rate,
1470 snd_pcm_format_width(runtime->format),
1471 runtime->channels);
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001472 snd_azf3328_codec_setdmaa(chip, codec,
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001473 runtime->dma_addr, count, size);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001474#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001475 return 0;
1476}
1477
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001478static int
Andreas Mohrda237f32010-12-27 21:17:26 +01001479snd_azf3328_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001480{
Takashi Iwai95de7762005-11-17 15:02:42 +01001481 struct snd_azf3328 *chip = snd_pcm_substream_chip(substream);
1482 struct snd_pcm_runtime *runtime = substream->runtime;
Andreas Mohrda237f32010-12-27 21:17:26 +01001483 struct snd_azf3328_codec_data *codec = runtime->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001484 int result = 0;
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001485 u16 flags1;
Peter Senna Tschudine0f17c72013-09-22 20:44:12 +02001486 bool previously_muted = false;
Andreas Mohrda237f32010-12-27 21:17:26 +01001487 bool is_main_mixer_playback_codec = (AZF_CODEC_PLAYBACK == codec->type);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001488
Linus Torvalds1da177e2005-04-16 15:20:36 -07001489 switch (cmd) {
1490 case SNDRV_PCM_TRIGGER_START:
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001491 dev_dbg(chip->card->dev, "START PCM %s\n", codec->name);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001492
Andreas Mohrda237f32010-12-27 21:17:26 +01001493 if (is_main_mixer_playback_codec) {
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001494 /* mute WaveOut (avoid clicking during setup) */
1495 previously_muted =
Andreas Mohrb5dc20c2011-02-19 00:49:32 +01001496 snd_azf3328_mixer_mute_control_pcm(
1497 chip, 1
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001498 );
1499 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001500
Andreas Mohrda237f32010-12-27 21:17:26 +01001501 snd_azf3328_codec_setfmt(codec,
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001502 runtime->rate,
1503 snd_pcm_format_width(runtime->format),
1504 runtime->channels);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001505
Andreas Mohrda237f32010-12-27 21:17:26 +01001506 spin_lock(codec->lock);
Andreas Mohr02330fb2008-05-16 12:18:29 +02001507 /* first, remember current value: */
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001508 flags1 = snd_azf3328_codec_inw(codec, IDX_IO_CODEC_DMA_FLAGS);
Andreas Mohr02330fb2008-05-16 12:18:29 +02001509
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001510 /* stop transfer */
1511 flags1 &= ~DMA_RESUME;
1512 snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, flags1);
Andreas Mohr02330fb2008-05-16 12:18:29 +02001513
Linus Torvalds1da177e2005-04-16 15:20:36 -07001514 /* FIXME: clear interrupts or what??? */
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001515 snd_azf3328_codec_outw(codec, IDX_IO_CODEC_IRQTYPE, 0xffff);
Andreas Mohrda237f32010-12-27 21:17:26 +01001516 spin_unlock(codec->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001517
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001518 snd_azf3328_codec_setdmaa(chip, codec, runtime->dma_addr,
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001519 snd_pcm_lib_period_bytes(substream),
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001520 snd_pcm_lib_buffer_bytes(substream)
1521 );
Linus Torvalds1da177e2005-04-16 15:20:36 -07001522
Andreas Mohrda237f32010-12-27 21:17:26 +01001523 spin_lock(codec->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001524#ifdef WIN9X
1525 /* FIXME: enable playback/recording??? */
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001526 flags1 |= DMA_RUN_SOMETHING1 | DMA_RUN_SOMETHING2;
1527 snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, flags1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001528
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001529 /* start transfer again */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001530 /* FIXME: what is this value (0x0010)??? */
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001531 flags1 |= DMA_RESUME | DMA_EPILOGUE_SOMETHING;
1532 snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, flags1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001533#else /* NT4 */
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001534 snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS,
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001535 0x0000);
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001536 snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS,
1537 DMA_RUN_SOMETHING1);
1538 snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS,
1539 DMA_RUN_SOMETHING1 |
1540 DMA_RUN_SOMETHING2);
1541 snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS,
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001542 DMA_RESUME |
1543 SOMETHING_ALMOST_ALWAYS_SET |
1544 DMA_EPILOGUE_SOMETHING |
1545 DMA_SOMETHING_ELSE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001546#endif
Andreas Mohrda237f32010-12-27 21:17:26 +01001547 spin_unlock(codec->lock);
1548 snd_azf3328_ctrl_codec_activity(chip, codec->type, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001549
Andreas Mohrda237f32010-12-27 21:17:26 +01001550 if (is_main_mixer_playback_codec) {
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001551 /* now unmute WaveOut */
1552 if (!previously_muted)
Andreas Mohrb5dc20c2011-02-19 00:49:32 +01001553 snd_azf3328_mixer_mute_control_pcm(
1554 chip, 0
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001555 );
1556 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001557
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001558 dev_dbg(chip->card->dev, "PCM STARTED %s\n", codec->name);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001559 break;
Andreas Mohrca54bde2006-05-17 11:02:24 +02001560 case SNDRV_PCM_TRIGGER_RESUME:
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001561 dev_dbg(chip->card->dev, "PCM RESUME %s\n", codec->name);
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001562 /* resume codec if we were active */
Andreas Mohrda237f32010-12-27 21:17:26 +01001563 spin_lock(codec->lock);
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001564 if (codec->running)
1565 snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS,
1566 snd_azf3328_codec_inw(
1567 codec, IDX_IO_CODEC_DMA_FLAGS
1568 ) | DMA_RESUME
1569 );
Andreas Mohrda237f32010-12-27 21:17:26 +01001570 spin_unlock(codec->lock);
Andreas Mohrca54bde2006-05-17 11:02:24 +02001571 break;
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001572 case SNDRV_PCM_TRIGGER_STOP:
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001573 dev_dbg(chip->card->dev, "PCM STOP %s\n", codec->name);
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001574
Andreas Mohrda237f32010-12-27 21:17:26 +01001575 if (is_main_mixer_playback_codec) {
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001576 /* mute WaveOut (avoid clicking during setup) */
1577 previously_muted =
Andreas Mohrb5dc20c2011-02-19 00:49:32 +01001578 snd_azf3328_mixer_mute_control_pcm(
1579 chip, 1
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001580 );
1581 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001582
Andreas Mohrda237f32010-12-27 21:17:26 +01001583 spin_lock(codec->lock);
Andreas Mohr02330fb2008-05-16 12:18:29 +02001584 /* first, remember current value: */
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001585 flags1 = snd_azf3328_codec_inw(codec, IDX_IO_CODEC_DMA_FLAGS);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001586
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001587 /* stop transfer */
1588 flags1 &= ~DMA_RESUME;
1589 snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, flags1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001590
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001591 /* hmm, is this really required? we're resetting the same bit
1592 * immediately thereafter... */
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001593 flags1 |= DMA_RUN_SOMETHING1;
1594 snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, flags1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001595
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001596 flags1 &= ~DMA_RUN_SOMETHING1;
1597 snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, flags1);
Andreas Mohrda237f32010-12-27 21:17:26 +01001598 spin_unlock(codec->lock);
1599 snd_azf3328_ctrl_codec_activity(chip, codec->type, 0);
Andreas Mohr02330fb2008-05-16 12:18:29 +02001600
Andreas Mohrda237f32010-12-27 21:17:26 +01001601 if (is_main_mixer_playback_codec) {
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001602 /* now unmute WaveOut */
1603 if (!previously_muted)
Andreas Mohrb5dc20c2011-02-19 00:49:32 +01001604 snd_azf3328_mixer_mute_control_pcm(
1605 chip, 0
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001606 );
1607 }
Andreas Mohr02330fb2008-05-16 12:18:29 +02001608
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001609 dev_dbg(chip->card->dev, "PCM STOPPED %s\n", codec->name);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001610 break;
Andreas Mohrca54bde2006-05-17 11:02:24 +02001611 case SNDRV_PCM_TRIGGER_SUSPEND:
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001612 dev_dbg(chip->card->dev, "PCM SUSPEND %s\n", codec->name);
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001613 /* make sure codec is stopped */
1614 snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS,
1615 snd_azf3328_codec_inw(
1616 codec, IDX_IO_CODEC_DMA_FLAGS
1617 ) & ~DMA_RESUME
1618 );
Andreas Mohrca54bde2006-05-17 11:02:24 +02001619 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001620 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001621 WARN(1, "FIXME: SNDRV_PCM_TRIGGER_PAUSE_PUSH NIY!\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001622 break;
1623 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001624 WARN(1, "FIXME: SNDRV_PCM_TRIGGER_PAUSE_RELEASE NIY!\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001625 break;
1626 default:
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001627 WARN(1, "FIXME: unknown trigger mode!\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001628 return -EINVAL;
1629 }
Andreas Mohr02330fb2008-05-16 12:18:29 +02001630
Linus Torvalds1da177e2005-04-16 15:20:36 -07001631 return result;
1632}
1633
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001634static snd_pcm_uframes_t
Andreas Mohrda237f32010-12-27 21:17:26 +01001635snd_azf3328_pcm_pointer(struct snd_pcm_substream *substream
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001636)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001637{
Andreas Mohrda237f32010-12-27 21:17:26 +01001638 const struct snd_azf3328_codec_data *codec =
1639 substream->runtime->private_data;
Andreas Mohr34585592010-12-27 21:17:11 +01001640 unsigned long result;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001641 snd_pcm_uframes_t frmres;
1642
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001643 result = snd_azf3328_codec_inl(codec, IDX_IO_CODEC_DMA_CURRPOS);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001644
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001645 /* calculate offset */
Andreas Mohr34585592010-12-27 21:17:11 +01001646#ifdef QUERY_HARDWARE
1647 result -= snd_azf3328_codec_inl(codec, IDX_IO_CODEC_DMA_START_1);
1648#else
1649 result -= codec->dma_base;
1650#endif
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001651 frmres = bytes_to_frames( substream->runtime, result);
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001652 dev_dbg(substream->pcm->card->dev, "%08li %s @ 0x%8lx, frames %8ld\n",
1653 jiffies, codec->name, result, frmres);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001654 return frmres;
1655}
1656
Andreas Mohr02330fb2008-05-16 12:18:29 +02001657/******************************************************************/
1658
1659#ifdef SUPPORT_GAMEPORT
1660static inline void
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001661snd_azf3328_gameport_irq_enable(struct snd_azf3328 *chip,
1662 bool enable
1663)
Andreas Mohr02330fb2008-05-16 12:18:29 +02001664{
1665 snd_azf3328_io_reg_setb(
1666 chip->game_io+IDX_GAME_HWCONFIG,
1667 GAME_HWCFG_IRQ_ENABLE,
1668 enable
1669 );
1670}
1671
1672static inline void
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001673snd_azf3328_gameport_legacy_address_enable(struct snd_azf3328 *chip,
1674 bool enable
1675)
Andreas Mohr02330fb2008-05-16 12:18:29 +02001676{
1677 snd_azf3328_io_reg_setb(
1678 chip->game_io+IDX_GAME_HWCONFIG,
1679 GAME_HWCFG_LEGACY_ADDRESS_ENABLE,
1680 enable
1681 );
1682}
1683
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001684static void
1685snd_azf3328_gameport_set_counter_frequency(struct snd_azf3328 *chip,
1686 unsigned int freq_cfg
1687)
Andreas Mohr02330fb2008-05-16 12:18:29 +02001688{
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001689 snd_azf3328_io_reg_setb(
1690 chip->game_io+IDX_GAME_HWCONFIG,
1691 0x02,
1692 (freq_cfg & 1) != 0
1693 );
1694 snd_azf3328_io_reg_setb(
1695 chip->game_io+IDX_GAME_HWCONFIG,
1696 0x04,
1697 (freq_cfg & 2) != 0
1698 );
1699}
1700
1701static inline void
1702snd_azf3328_gameport_axis_circuit_enable(struct snd_azf3328 *chip, bool enable)
1703{
1704 snd_azf3328_ctrl_reg_6AH_update(
Andreas Mohr627d3e72008-06-23 11:50:47 +02001705 chip, IO_6A_SOMETHING2_GAMEPORT, enable
Andreas Mohr02330fb2008-05-16 12:18:29 +02001706 );
1707}
1708
1709static inline void
1710snd_azf3328_gameport_interrupt(struct snd_azf3328 *chip)
1711{
1712 /*
1713 * skeleton handler only
1714 * (we do not want axis reading in interrupt handler - too much load!)
1715 */
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001716 dev_dbg(chip->card->dev, "gameport irq\n");
Andreas Mohr02330fb2008-05-16 12:18:29 +02001717
1718 /* this should ACK the gameport IRQ properly, hopefully. */
1719 snd_azf3328_game_inw(chip, IDX_GAME_AXIS_VALUE);
1720}
1721
1722static int
1723snd_azf3328_gameport_open(struct gameport *gameport, int mode)
1724{
1725 struct snd_azf3328 *chip = gameport_get_port_data(gameport);
1726 int res;
1727
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001728 dev_dbg(chip->card->dev, "gameport_open, mode %d\n", mode);
Andreas Mohr02330fb2008-05-16 12:18:29 +02001729 switch (mode) {
1730 case GAMEPORT_MODE_COOKED:
1731 case GAMEPORT_MODE_RAW:
1732 res = 0;
1733 break;
1734 default:
1735 res = -1;
1736 break;
1737 }
1738
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001739 snd_azf3328_gameport_set_counter_frequency(chip,
1740 GAME_HWCFG_ADC_COUNTER_FREQ_STD);
Andreas Mohr02330fb2008-05-16 12:18:29 +02001741 snd_azf3328_gameport_axis_circuit_enable(chip, (res == 0));
1742
1743 return res;
1744}
1745
1746static void
1747snd_azf3328_gameport_close(struct gameport *gameport)
1748{
1749 struct snd_azf3328 *chip = gameport_get_port_data(gameport);
1750
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001751 dev_dbg(chip->card->dev, "gameport_close\n");
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001752 snd_azf3328_gameport_set_counter_frequency(chip,
1753 GAME_HWCFG_ADC_COUNTER_FREQ_1_200);
Andreas Mohr02330fb2008-05-16 12:18:29 +02001754 snd_azf3328_gameport_axis_circuit_enable(chip, 0);
1755}
1756
1757static int
1758snd_azf3328_gameport_cooked_read(struct gameport *gameport,
1759 int *axes,
1760 int *buttons
1761)
1762{
1763 struct snd_azf3328 *chip = gameport_get_port_data(gameport);
1764 int i;
1765 u8 val;
1766 unsigned long flags;
1767
Takashi Iwaida3cec32008-08-08 17:12:14 +02001768 if (snd_BUG_ON(!chip))
1769 return 0;
Andreas Mohr02330fb2008-05-16 12:18:29 +02001770
1771 spin_lock_irqsave(&chip->reg_lock, flags);
1772 val = snd_azf3328_game_inb(chip, IDX_GAME_LEGACY_COMPATIBLE);
1773 *buttons = (~(val) >> 4) & 0xf;
1774
1775 /* ok, this one is a bit dirty: cooked_read is being polled by a timer,
1776 * thus we're atomic and cannot actively wait in here
1777 * (which would be useful for us since it probably would be better
1778 * to trigger a measurement in here, then wait a short amount of
1779 * time until it's finished, then read values of _this_ measurement).
1780 *
1781 * Thus we simply resort to reading values if they're available already
1782 * and trigger the next measurement.
1783 */
1784
1785 val = snd_azf3328_game_inb(chip, IDX_GAME_AXES_CONFIG);
1786 if (val & GAME_AXES_SAMPLING_READY) {
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001787 for (i = 0; i < ARRAY_SIZE(chip->axes); ++i) {
Andreas Mohr02330fb2008-05-16 12:18:29 +02001788 /* configure the axis to read */
1789 val = (i << 4) | 0x0f;
1790 snd_azf3328_game_outb(chip, IDX_GAME_AXES_CONFIG, val);
1791
1792 chip->axes[i] = snd_azf3328_game_inw(
1793 chip, IDX_GAME_AXIS_VALUE
1794 );
1795 }
1796 }
1797
Andreas Mohradf59312010-12-27 21:16:43 +01001798 /* trigger next sampling of axes, to be evaluated the next time we
Andreas Mohr02330fb2008-05-16 12:18:29 +02001799 * enter this function */
1800
1801 /* for some very, very strange reason we cannot enable
1802 * Measurement Ready monitoring for all axes here,
1803 * at least not when only one joystick connected */
1804 val = 0x03; /* we're able to monitor axes 1 and 2 only */
1805 snd_azf3328_game_outb(chip, IDX_GAME_AXES_CONFIG, val);
1806
1807 snd_azf3328_game_outw(chip, IDX_GAME_AXIS_VALUE, 0xffff);
1808 spin_unlock_irqrestore(&chip->reg_lock, flags);
1809
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001810 for (i = 0; i < ARRAY_SIZE(chip->axes); i++) {
Andreas Mohr02330fb2008-05-16 12:18:29 +02001811 axes[i] = chip->axes[i];
1812 if (axes[i] == 0xffff)
1813 axes[i] = -1;
1814 }
1815
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001816 dev_dbg(chip->card->dev, "cooked_read: axes %d %d %d %d buttons %d\n",
1817 axes[0], axes[1], axes[2], axes[3], *buttons);
Andreas Mohr02330fb2008-05-16 12:18:29 +02001818
1819 return 0;
1820}
1821
Bill Pembertone23e7a12012-12-06 12:35:10 -05001822static int
Andreas Mohr02330fb2008-05-16 12:18:29 +02001823snd_azf3328_gameport(struct snd_azf3328 *chip, int dev)
1824{
1825 struct gameport *gp;
1826
Andreas Mohr02330fb2008-05-16 12:18:29 +02001827 chip->gameport = gp = gameport_allocate_port();
1828 if (!gp) {
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001829 dev_err(chip->card->dev, "cannot alloc memory for gameport\n");
Andreas Mohr02330fb2008-05-16 12:18:29 +02001830 return -ENOMEM;
1831 }
1832
1833 gameport_set_name(gp, "AZF3328 Gameport");
1834 gameport_set_phys(gp, "pci%s/gameport0", pci_name(chip->pci));
1835 gameport_set_dev_parent(gp, &chip->pci->dev);
Andreas Mohr627d3e72008-06-23 11:50:47 +02001836 gp->io = chip->game_io;
Andreas Mohr02330fb2008-05-16 12:18:29 +02001837 gameport_set_port_data(gp, chip);
1838
1839 gp->open = snd_azf3328_gameport_open;
1840 gp->close = snd_azf3328_gameport_close;
1841 gp->fuzz = 16; /* seems ok */
1842 gp->cooked_read = snd_azf3328_gameport_cooked_read;
1843
1844 /* DISABLE legacy address: we don't need it! */
1845 snd_azf3328_gameport_legacy_address_enable(chip, 0);
1846
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001847 snd_azf3328_gameport_set_counter_frequency(chip,
1848 GAME_HWCFG_ADC_COUNTER_FREQ_1_200);
Andreas Mohr02330fb2008-05-16 12:18:29 +02001849 snd_azf3328_gameport_axis_circuit_enable(chip, 0);
1850
1851 gameport_register_port(chip->gameport);
1852
1853 return 0;
1854}
1855
1856static void
1857snd_azf3328_gameport_free(struct snd_azf3328 *chip)
1858{
1859 if (chip->gameport) {
1860 gameport_unregister_port(chip->gameport);
1861 chip->gameport = NULL;
1862 }
1863 snd_azf3328_gameport_irq_enable(chip, 0);
1864}
1865#else
1866static inline int
1867snd_azf3328_gameport(struct snd_azf3328 *chip, int dev) { return -ENOSYS; }
1868static inline void
1869snd_azf3328_gameport_free(struct snd_azf3328 *chip) { }
1870static inline void
1871snd_azf3328_gameport_interrupt(struct snd_azf3328 *chip)
1872{
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001873 dev_warn(chip->card->dev, "huh, game port IRQ occurred!?\n");
Andreas Mohr02330fb2008-05-16 12:18:29 +02001874}
1875#endif /* SUPPORT_GAMEPORT */
1876
1877/******************************************************************/
1878
Andreas Mohr627d3e72008-06-23 11:50:47 +02001879static inline void
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001880snd_azf3328_irq_log_unknown_type(struct snd_azf3328 *chip, u8 which)
Andreas Mohr627d3e72008-06-23 11:50:47 +02001881{
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001882 dev_dbg(chip->card->dev,
1883 "unknown IRQ type (%x) occurred, please report!\n",
1884 which);
Andreas Mohr627d3e72008-06-23 11:50:47 +02001885}
1886
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001887static inline void
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001888snd_azf3328_pcm_interrupt(struct snd_azf3328 *chip,
1889 const struct snd_azf3328_codec_data *first_codec,
Andreas Mohrda237f32010-12-27 21:17:26 +01001890 u8 status
1891)
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001892{
1893 u8 which;
1894 enum snd_azf3328_codec_type codec_type;
Andreas Mohrda237f32010-12-27 21:17:26 +01001895 const struct snd_azf3328_codec_data *codec = first_codec;
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001896
1897 for (codec_type = AZF_CODEC_PLAYBACK;
1898 codec_type <= AZF_CODEC_I2S_OUT;
Andreas Mohrda237f32010-12-27 21:17:26 +01001899 ++codec_type, ++codec) {
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001900
1901 /* skip codec if there's no interrupt for it */
1902 if (!(status & (1 << codec_type)))
1903 continue;
1904
Andreas Mohrda237f32010-12-27 21:17:26 +01001905 spin_lock(codec->lock);
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001906 which = snd_azf3328_codec_inb(codec, IDX_IO_CODEC_IRQTYPE);
1907 /* ack all IRQ types immediately */
1908 snd_azf3328_codec_outb(codec, IDX_IO_CODEC_IRQTYPE, which);
Andreas Mohrda237f32010-12-27 21:17:26 +01001909 spin_unlock(codec->lock);
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001910
Andreas Mohrda237f32010-12-27 21:17:26 +01001911 if (codec->substream) {
Andreas Mohr78df6172009-07-12 22:17:54 +02001912 snd_pcm_period_elapsed(codec->substream);
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001913 dev_dbg(chip->card->dev, "%s period done (#%x), @ %x\n",
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001914 codec->name,
1915 which,
1916 snd_azf3328_codec_inl(
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001917 codec, IDX_IO_CODEC_DMA_CURRPOS));
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001918 } else
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001919 dev_warn(chip->card->dev, "irq handler problem!\n");
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001920 if (which & IRQ_SOMETHING)
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001921 snd_azf3328_irq_log_unknown_type(chip, which);
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001922 }
1923}
1924
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001925static irqreturn_t
David Howells7d12e782006-10-05 14:55:46 +01001926snd_azf3328_interrupt(int irq, void *dev_id)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001927{
Takashi Iwai95de7762005-11-17 15:02:42 +01001928 struct snd_azf3328 *chip = dev_id;
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001929 u8 status;
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001930 static unsigned long irq_count;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001931
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001932 status = snd_azf3328_ctrl_inb(chip, IDX_IO_IRQSTATUS);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001933
1934 /* fast path out, to ease interrupt sharing */
Andreas Mohr02330fb2008-05-16 12:18:29 +02001935 if (!(status &
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001936 (IRQ_PLAYBACK|IRQ_RECORDING|IRQ_I2S_OUT
1937 |IRQ_GAMEPORT|IRQ_MPU401|IRQ_TIMER)
Andreas Mohr02330fb2008-05-16 12:18:29 +02001938 ))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001939 return IRQ_NONE; /* must be interrupt for another device */
1940
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001941 dev_dbg(chip->card->dev,
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001942 "irq_count %ld! IDX_IO_IRQSTATUS %04x\n",
Andreas Mohr627d3e72008-06-23 11:50:47 +02001943 irq_count++ /* debug-only */,
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001944 status);
Andreas Mohr02330fb2008-05-16 12:18:29 +02001945
Andreas Mohre2f87262006-05-17 11:04:19 +02001946 if (status & IRQ_TIMER) {
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001947 /* dev_dbg(chip->card->dev, "timer %ld\n",
Andreas Mohr02330fb2008-05-16 12:18:29 +02001948 snd_azf3328_codec_inl(chip, IDX_IO_TIMER_VALUE)
1949 & TIMER_VALUE_MASK
1950 ); */
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001951 if (chip->timer)
1952 snd_timer_interrupt(chip->timer, chip->timer->sticks);
1953 /* ACK timer */
1954 spin_lock(&chip->reg_lock);
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001955 snd_azf3328_ctrl_outb(chip, IDX_IO_TIMER_VALUE + 3, 0x07);
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001956 spin_unlock(&chip->reg_lock);
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001957 dev_dbg(chip->card->dev, "timer IRQ\n");
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001958 }
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001959
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001960 if (status & (IRQ_PLAYBACK|IRQ_RECORDING|IRQ_I2S_OUT))
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001961 snd_azf3328_pcm_interrupt(chip, chip->codecs, status);
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001962
Andreas Mohr02330fb2008-05-16 12:18:29 +02001963 if (status & IRQ_GAMEPORT)
1964 snd_azf3328_gameport_interrupt(chip);
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001965
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001966 /* MPU401 has less critical IRQ requirements
1967 * than timer and playback/recording, right? */
Andreas Mohre2f87262006-05-17 11:04:19 +02001968 if (status & IRQ_MPU401) {
David Howells7d12e782006-10-05 14:55:46 +01001969 snd_mpu401_uart_interrupt(irq, chip->rmidi->private_data);
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001970
1971 /* hmm, do we have to ack the IRQ here somehow?
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001972 * If so, then I don't know how yet... */
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01001973 dev_dbg(chip->card->dev, "MPU401 IRQ\n");
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001974 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001975 return IRQ_HANDLED;
1976}
1977
1978/*****************************************************************/
1979
Andreas Mohrdfbf9512009-07-05 13:55:46 +02001980/* as long as we think we have identical snd_pcm_hardware parameters
1981 for playback, capture and i2s out, we can use the same physical struct
1982 since the struct is simply being copied into a member.
1983*/
1984static const struct snd_pcm_hardware snd_azf3328_hardware =
Linus Torvalds1da177e2005-04-16 15:20:36 -07001985{
1986 /* FIXME!! Correct? */
Andreas Mohrd91c64c2005-10-25 11:17:45 +02001987 .info = SNDRV_PCM_INFO_MMAP |
1988 SNDRV_PCM_INFO_INTERLEAVED |
1989 SNDRV_PCM_INFO_MMAP_VALID,
1990 .formats = SNDRV_PCM_FMTBIT_S8 |
1991 SNDRV_PCM_FMTBIT_U8 |
1992 SNDRV_PCM_FMTBIT_S16_LE |
1993 SNDRV_PCM_FMTBIT_U16_LE,
1994 .rates = SNDRV_PCM_RATE_5512 |
1995 SNDRV_PCM_RATE_8000_48000 |
1996 SNDRV_PCM_RATE_KNOT,
Andreas Mohr02330fb2008-05-16 12:18:29 +02001997 .rate_min = AZF_FREQ_4000,
1998 .rate_max = AZF_FREQ_66200,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001999 .channels_min = 1,
2000 .channels_max = 2,
Andreas Mohr79741502010-11-21 12:09:32 +01002001 .buffer_bytes_max = (64*1024),
2002 .period_bytes_min = 1024,
2003 .period_bytes_max = (32*1024),
2004 /* We simply have two DMA areas (instead of a list of descriptors
2005 such as other cards); I believe that this is a fixed hardware
2006 attribute and there isn't much driver magic to be done to expand it.
2007 Thus indicate that we have at least and at most 2 periods. */
2008 .periods_min = 2,
2009 .periods_max = 2,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002010 /* FIXME: maybe that card actually has a FIFO?
2011 * Hmm, it seems newer revisions do have one, but we still don't know
2012 * its size... */
2013 .fifo_size = 0,
2014};
2015
Linus Torvalds1da177e2005-04-16 15:20:36 -07002016
2017static unsigned int snd_azf3328_fixed_rates[] = {
Andreas Mohr02330fb2008-05-16 12:18:29 +02002018 AZF_FREQ_4000,
2019 AZF_FREQ_4800,
2020 AZF_FREQ_5512,
2021 AZF_FREQ_6620,
2022 AZF_FREQ_8000,
2023 AZF_FREQ_9600,
2024 AZF_FREQ_11025,
2025 AZF_FREQ_13240,
2026 AZF_FREQ_16000,
2027 AZF_FREQ_22050,
2028 AZF_FREQ_32000,
2029 AZF_FREQ_44100,
2030 AZF_FREQ_48000,
2031 AZF_FREQ_66200
2032};
2033
Takashi Iwai95de7762005-11-17 15:02:42 +01002034static struct snd_pcm_hw_constraint_list snd_azf3328_hw_constraints_rates = {
Andreas Mohr02330fb2008-05-16 12:18:29 +02002035 .count = ARRAY_SIZE(snd_azf3328_fixed_rates),
Linus Torvalds1da177e2005-04-16 15:20:36 -07002036 .list = snd_azf3328_fixed_rates,
2037 .mask = 0,
2038};
2039
2040/*****************************************************************/
2041
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002042static int
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002043snd_azf3328_pcm_open(struct snd_pcm_substream *substream,
2044 enum snd_azf3328_codec_type codec_type
2045)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002046{
Takashi Iwai95de7762005-11-17 15:02:42 +01002047 struct snd_azf3328 *chip = snd_pcm_substream_chip(substream);
2048 struct snd_pcm_runtime *runtime = substream->runtime;
Andreas Mohrda237f32010-12-27 21:17:26 +01002049 struct snd_azf3328_codec_data *codec = &chip->codecs[codec_type];
Linus Torvalds1da177e2005-04-16 15:20:36 -07002050
Andreas Mohrda237f32010-12-27 21:17:26 +01002051 codec->substream = substream;
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002052
2053 /* same parameters for all our codecs - at least we think so... */
2054 runtime->hw = snd_azf3328_hardware;
2055
Linus Torvalds1da177e2005-04-16 15:20:36 -07002056 snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
2057 &snd_azf3328_hw_constraints_rates);
Andreas Mohrda237f32010-12-27 21:17:26 +01002058 runtime->private_data = codec;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002059 return 0;
2060}
2061
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002062static int
Andreas Mohrda237f32010-12-27 21:17:26 +01002063snd_azf3328_pcm_playback_open(struct snd_pcm_substream *substream)
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002064{
2065 return snd_azf3328_pcm_open(substream, AZF_CODEC_PLAYBACK);
2066}
2067
2068static int
Andreas Mohrda237f32010-12-27 21:17:26 +01002069snd_azf3328_pcm_capture_open(struct snd_pcm_substream *substream)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002070{
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002071 return snd_azf3328_pcm_open(substream, AZF_CODEC_CAPTURE);
2072}
2073
2074static int
Andreas Mohrda237f32010-12-27 21:17:26 +01002075snd_azf3328_pcm_i2s_out_open(struct snd_pcm_substream *substream)
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002076{
2077 return snd_azf3328_pcm_open(substream, AZF_CODEC_I2S_OUT);
2078}
2079
2080static int
Andreas Mohrda237f32010-12-27 21:17:26 +01002081snd_azf3328_pcm_close(struct snd_pcm_substream *substream
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002082)
2083{
Andreas Mohrda237f32010-12-27 21:17:26 +01002084 struct snd_azf3328_codec_data *codec =
2085 substream->runtime->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002086
Andreas Mohrda237f32010-12-27 21:17:26 +01002087 codec->substream = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002088 return 0;
2089}
2090
Linus Torvalds1da177e2005-04-16 15:20:36 -07002091/******************************************************************/
2092
Julia Lawall6769e9882016-09-02 00:13:10 +02002093static const struct snd_pcm_ops snd_azf3328_playback_ops = {
Andreas Mohrda237f32010-12-27 21:17:26 +01002094 .open = snd_azf3328_pcm_playback_open,
2095 .close = snd_azf3328_pcm_close,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002096 .ioctl = snd_pcm_lib_ioctl,
2097 .hw_params = snd_azf3328_hw_params,
2098 .hw_free = snd_azf3328_hw_free,
Andreas Mohrda237f32010-12-27 21:17:26 +01002099 .prepare = snd_azf3328_pcm_prepare,
2100 .trigger = snd_azf3328_pcm_trigger,
2101 .pointer = snd_azf3328_pcm_pointer
Linus Torvalds1da177e2005-04-16 15:20:36 -07002102};
2103
Julia Lawall6769e9882016-09-02 00:13:10 +02002104static const struct snd_pcm_ops snd_azf3328_capture_ops = {
Andreas Mohrda237f32010-12-27 21:17:26 +01002105 .open = snd_azf3328_pcm_capture_open,
2106 .close = snd_azf3328_pcm_close,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002107 .ioctl = snd_pcm_lib_ioctl,
2108 .hw_params = snd_azf3328_hw_params,
2109 .hw_free = snd_azf3328_hw_free,
Andreas Mohrda237f32010-12-27 21:17:26 +01002110 .prepare = snd_azf3328_pcm_prepare,
2111 .trigger = snd_azf3328_pcm_trigger,
2112 .pointer = snd_azf3328_pcm_pointer
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002113};
2114
Julia Lawall6769e9882016-09-02 00:13:10 +02002115static const struct snd_pcm_ops snd_azf3328_i2s_out_ops = {
Andreas Mohrda237f32010-12-27 21:17:26 +01002116 .open = snd_azf3328_pcm_i2s_out_open,
2117 .close = snd_azf3328_pcm_close,
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002118 .ioctl = snd_pcm_lib_ioctl,
2119 .hw_params = snd_azf3328_hw_params,
2120 .hw_free = snd_azf3328_hw_free,
Andreas Mohrda237f32010-12-27 21:17:26 +01002121 .prepare = snd_azf3328_pcm_prepare,
2122 .trigger = snd_azf3328_pcm_trigger,
2123 .pointer = snd_azf3328_pcm_pointer
Linus Torvalds1da177e2005-04-16 15:20:36 -07002124};
2125
Bill Pembertone23e7a12012-12-06 12:35:10 -05002126static int
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002127snd_azf3328_pcm(struct snd_azf3328 *chip)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002128{
Dan Carpenter7f788e02015-02-25 16:32:45 +03002129 /* pcm devices */
2130 enum { AZF_PCMDEV_STD, AZF_PCMDEV_I2S_OUT, NUM_AZF_PCMDEVS };
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002131
Takashi Iwai95de7762005-11-17 15:02:42 +01002132 struct snd_pcm *pcm;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002133 int err;
2134
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002135 err = snd_pcm_new(chip->card, "AZF3328 DSP", AZF_PCMDEV_STD,
2136 1, 1, &pcm);
2137 if (err < 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002138 return err;
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002139 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
2140 &snd_azf3328_playback_ops);
2141 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
2142 &snd_azf3328_capture_ops);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002143
2144 pcm->private_data = chip;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002145 pcm->info_flags = 0;
2146 strcpy(pcm->name, chip->card->shortname);
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002147 /* same pcm object for playback/capture (see snd_pcm_new() above) */
2148 chip->pcm[AZF_CODEC_PLAYBACK] = pcm;
2149 chip->pcm[AZF_CODEC_CAPTURE] = pcm;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002150
2151 snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002152 snd_dma_pci_data(chip->pci),
2153 64*1024, 64*1024);
2154
2155 err = snd_pcm_new(chip->card, "AZF3328 I2S OUT", AZF_PCMDEV_I2S_OUT,
2156 1, 0, &pcm);
2157 if (err < 0)
2158 return err;
2159 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
2160 &snd_azf3328_i2s_out_ops);
2161
2162 pcm->private_data = chip;
2163 pcm->info_flags = 0;
2164 strcpy(pcm->name, chip->card->shortname);
2165 chip->pcm[AZF_CODEC_I2S_OUT] = pcm;
2166
2167 snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
2168 snd_dma_pci_data(chip->pci),
2169 64*1024, 64*1024);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002170
Linus Torvalds1da177e2005-04-16 15:20:36 -07002171 return 0;
2172}
2173
2174/******************************************************************/
2175
Andreas Mohr02330fb2008-05-16 12:18:29 +02002176/*** NOTE: the physical timer resolution actually is 1024000 ticks per second
2177 *** (probably derived from main crystal via a divider of 24),
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002178 *** but announcing those attributes to user-space would make programs
2179 *** configure the timer to a 1 tick value, resulting in an absolutely fatal
2180 *** timer IRQ storm.
2181 *** Thus I chose to announce a down-scaled virtual timer to the outside and
2182 *** calculate real timer countdown values internally.
2183 *** (the scale factor can be set via module parameter "seqtimer_scaling").
2184 ***/
2185
2186static int
Takashi Iwai95de7762005-11-17 15:02:42 +01002187snd_azf3328_timer_start(struct snd_timer *timer)
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002188{
Takashi Iwai95de7762005-11-17 15:02:42 +01002189 struct snd_azf3328 *chip;
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002190 unsigned long flags;
2191 unsigned int delay;
2192
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002193 chip = snd_timer_chip(timer);
2194 delay = ((timer->sticks * seqtimer_scaling) - 1) & TIMER_VALUE_MASK;
Andreas Mohre2f87262006-05-17 11:04:19 +02002195 if (delay < 49) {
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002196 /* uhoh, that's not good, since user-space won't know about
2197 * this timing tweak
2198 * (we need to do it to avoid a lockup, though) */
2199
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01002200 dev_dbg(chip->card->dev, "delay was too low (%d)!\n", delay);
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002201 delay = 49; /* minimum time is 49 ticks */
2202 }
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01002203 dev_dbg(chip->card->dev, "setting timer countdown value %d\n", delay);
Andreas Mohr02330fb2008-05-16 12:18:29 +02002204 delay |= TIMER_COUNTDOWN_ENABLE | TIMER_IRQ_ENABLE;
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002205 spin_lock_irqsave(&chip->reg_lock, flags);
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002206 snd_azf3328_ctrl_outl(chip, IDX_IO_TIMER_VALUE, delay);
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002207 spin_unlock_irqrestore(&chip->reg_lock, flags);
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002208 return 0;
2209}
2210
2211static int
Takashi Iwai95de7762005-11-17 15:02:42 +01002212snd_azf3328_timer_stop(struct snd_timer *timer)
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002213{
Takashi Iwai95de7762005-11-17 15:02:42 +01002214 struct snd_azf3328 *chip;
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002215 unsigned long flags;
2216
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002217 chip = snd_timer_chip(timer);
2218 spin_lock_irqsave(&chip->reg_lock, flags);
2219 /* disable timer countdown and interrupt */
Andreas Mohr79741502010-11-21 12:09:32 +01002220 /* Hmm, should we write TIMER_IRQ_ACK here?
2221 YES indeed, otherwise a rogue timer operation - which prompts
2222 ALSA(?) to call repeated stop() in vain, but NOT start() -
2223 will never end (value 0x03 is kept shown in control byte).
2224 Simply manually poking 0x04 _once_ immediately successfully stops
2225 the hardware/ALSA interrupt activity. */
2226 snd_azf3328_ctrl_outb(chip, IDX_IO_TIMER_VALUE + 3, 0x04);
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002227 spin_unlock_irqrestore(&chip->reg_lock, flags);
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002228 return 0;
2229}
2230
2231
2232static int
Takashi Iwai95de7762005-11-17 15:02:42 +01002233snd_azf3328_timer_precise_resolution(struct snd_timer *timer,
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002234 unsigned long *num, unsigned long *den)
2235{
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002236 *num = 1;
2237 *den = 1024000 / seqtimer_scaling;
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002238 return 0;
2239}
2240
Takashi Iwai95de7762005-11-17 15:02:42 +01002241static struct snd_timer_hardware snd_azf3328_timer_hw = {
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002242 .flags = SNDRV_TIMER_HW_AUTO,
2243 .resolution = 977, /* 1000000/1024000 = 0.9765625us */
2244 .ticks = 1024000, /* max tick count, defined by the value register; actually it's not 1024000, but 1048576, but we don't care */
2245 .start = snd_azf3328_timer_start,
2246 .stop = snd_azf3328_timer_stop,
2247 .precise_resolution = snd_azf3328_timer_precise_resolution,
2248};
2249
Bill Pembertone23e7a12012-12-06 12:35:10 -05002250static int
Takashi Iwai95de7762005-11-17 15:02:42 +01002251snd_azf3328_timer(struct snd_azf3328 *chip, int device)
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002252{
Takashi Iwai95de7762005-11-17 15:02:42 +01002253 struct snd_timer *timer = NULL;
2254 struct snd_timer_id tid;
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002255 int err;
2256
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002257 tid.dev_class = SNDRV_TIMER_CLASS_CARD;
2258 tid.dev_sclass = SNDRV_TIMER_SCLASS_NONE;
2259 tid.card = chip->card->number;
2260 tid.device = device;
2261 tid.subdevice = 0;
2262
2263 snd_azf3328_timer_hw.resolution *= seqtimer_scaling;
2264 snd_azf3328_timer_hw.ticks /= seqtimer_scaling;
Andreas Mohr02330fb2008-05-16 12:18:29 +02002265
2266 err = snd_timer_new(chip->card, "AZF3328", &tid, &timer);
2267 if (err < 0)
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002268 goto out;
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002269
2270 strcpy(timer->name, "AZF3328 timer");
2271 timer->private_data = chip;
2272 timer->hw = snd_azf3328_timer_hw;
2273
2274 chip->timer = timer;
2275
Andreas Mohr02330fb2008-05-16 12:18:29 +02002276 snd_azf3328_timer_stop(timer);
2277
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002278 err = 0;
2279
2280out:
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002281 return err;
2282}
2283
2284/******************************************************************/
2285
Andreas Mohr02330fb2008-05-16 12:18:29 +02002286static int
2287snd_azf3328_free(struct snd_azf3328 *chip)
2288{
2289 if (chip->irq < 0)
2290 goto __end_hw;
2291
Andreas Mohrb5dc20c2011-02-19 00:49:32 +01002292 snd_azf3328_mixer_reset(chip);
Andreas Mohr02330fb2008-05-16 12:18:29 +02002293
2294 snd_azf3328_timer_stop(chip->timer);
2295 snd_azf3328_gameport_free(chip);
2296
Andreas Mohr02330fb2008-05-16 12:18:29 +02002297__end_hw:
2298 if (chip->irq >= 0)
2299 free_irq(chip->irq, chip);
2300 pci_release_regions(chip->pci);
2301 pci_disable_device(chip->pci);
2302
2303 kfree(chip);
2304 return 0;
2305}
2306
2307static int
2308snd_azf3328_dev_free(struct snd_device *device)
2309{
2310 struct snd_azf3328 *chip = device->device_data;
2311 return snd_azf3328_free(chip);
2312}
2313
Linus Torvalds1da177e2005-04-16 15:20:36 -07002314#if 0
2315/* check whether a bit can be modified */
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002316static void
Andreas Mohr02330fb2008-05-16 12:18:29 +02002317snd_azf3328_test_bit(unsigned unsigned reg, int bit)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002318{
2319 unsigned char val, valoff, valon;
2320
2321 val = inb(reg);
2322
2323 outb(val & ~(1 << bit), reg);
2324 valoff = inb(reg);
2325
2326 outb(val|(1 << bit), reg);
2327 valon = inb(reg);
Andreas Mohr02330fb2008-05-16 12:18:29 +02002328
Linus Torvalds1da177e2005-04-16 15:20:36 -07002329 outb(val, reg);
2330
Andreas Mohr78df6172009-07-12 22:17:54 +02002331 printk(KERN_DEBUG "reg %04x bit %d: %02x %02x %02x\n",
Andreas Mohr02330fb2008-05-16 12:18:29 +02002332 reg, bit, val, valoff, valon
2333 );
Linus Torvalds1da177e2005-04-16 15:20:36 -07002334}
2335#endif
2336
Andreas Mohr02330fb2008-05-16 12:18:29 +02002337static inline void
Takashi Iwai95de7762005-11-17 15:02:42 +01002338snd_azf3328_debug_show_ports(const struct snd_azf3328 *chip)
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002339{
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002340 u16 tmp;
2341
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01002342 dev_dbg(chip->card->dev,
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002343 "ctrl_io 0x%lx, game_io 0x%lx, mpu_io 0x%lx, "
Andreas Mohr02330fb2008-05-16 12:18:29 +02002344 "opl3_io 0x%lx, mixer_io 0x%lx, irq %d\n",
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002345 chip->ctrl_io, chip->game_io, chip->mpu_io,
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01002346 chip->opl3_io, chip->mixer_io, chip->irq);
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002347
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01002348 dev_dbg(chip->card->dev,
2349 "game %02x %02x %02x %02x %02x %02x\n",
Andreas Mohr02330fb2008-05-16 12:18:29 +02002350 snd_azf3328_game_inb(chip, 0),
2351 snd_azf3328_game_inb(chip, 1),
2352 snd_azf3328_game_inb(chip, 2),
2353 snd_azf3328_game_inb(chip, 3),
2354 snd_azf3328_game_inb(chip, 4),
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01002355 snd_azf3328_game_inb(chip, 5));
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002356
Andreas Mohr02330fb2008-05-16 12:18:29 +02002357 for (tmp = 0; tmp < 0x07; tmp += 1)
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01002358 dev_dbg(chip->card->dev,
2359 "mpu_io 0x%04x\n", inb(chip->mpu_io + tmp));
Andreas Mohr02330fb2008-05-16 12:18:29 +02002360
2361 for (tmp = 0; tmp <= 0x07; tmp += 1)
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01002362 dev_dbg(chip->card->dev,
2363 "0x%02x: game200 0x%04x, game208 0x%04x\n",
Andreas Mohr02330fb2008-05-16 12:18:29 +02002364 tmp, inb(0x200 + tmp), inb(0x208 + tmp));
2365
2366 for (tmp = 0; tmp <= 0x01; tmp += 1)
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01002367 dev_dbg(chip->card->dev,
Andreas Mohr02330fb2008-05-16 12:18:29 +02002368 "0x%02x: mpu300 0x%04x, mpu310 0x%04x, mpu320 0x%04x, "
2369 "mpu330 0x%04x opl388 0x%04x opl38c 0x%04x\n",
2370 tmp,
2371 inb(0x300 + tmp),
2372 inb(0x310 + tmp),
2373 inb(0x320 + tmp),
2374 inb(0x330 + tmp),
2375 inb(0x388 + tmp),
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01002376 inb(0x38c + tmp));
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002377
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002378 for (tmp = 0; tmp < AZF_IO_SIZE_CTRL; tmp += 2)
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01002379 dev_dbg(chip->card->dev,
2380 "ctrl 0x%02x: 0x%04x\n",
2381 tmp, snd_azf3328_ctrl_inw(chip, tmp));
Andreas Mohre24a1212007-03-26 12:49:45 +02002382
2383 for (tmp = 0; tmp < AZF_IO_SIZE_MIXER; tmp += 2)
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01002384 dev_dbg(chip->card->dev,
2385 "mixer 0x%02x: 0x%04x\n",
2386 tmp, snd_azf3328_mixer_inw(chip, tmp));
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002387}
2388
Bill Pembertone23e7a12012-12-06 12:35:10 -05002389static int
Takashi Iwai95de7762005-11-17 15:02:42 +01002390snd_azf3328_create(struct snd_card *card,
Andreas Mohr02330fb2008-05-16 12:18:29 +02002391 struct pci_dev *pci,
2392 unsigned long device_type,
2393 struct snd_azf3328 **rchip)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002394{
Takashi Iwai95de7762005-11-17 15:02:42 +01002395 struct snd_azf3328 *chip;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002396 int err;
Takashi Iwai95de7762005-11-17 15:02:42 +01002397 static struct snd_device_ops ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002398 .dev_free = snd_azf3328_dev_free,
2399 };
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002400 u8 dma_init;
2401 enum snd_azf3328_codec_type codec_type;
Andreas Mohrda237f32010-12-27 21:17:26 +01002402 struct snd_azf3328_codec_data *codec_setup;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002403
2404 *rchip = NULL;
2405
Andreas Mohr02330fb2008-05-16 12:18:29 +02002406 err = pci_enable_device(pci);
2407 if (err < 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002408 return err;
2409
Takashi Iwaie560d8d2005-09-09 14:21:46 +02002410 chip = kzalloc(sizeof(*chip), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002411 if (chip == NULL) {
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002412 err = -ENOMEM;
2413 goto out_err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002414 }
2415 spin_lock_init(&chip->reg_lock);
2416 chip->card = card;
2417 chip->pci = pci;
2418 chip->irq = -1;
2419
2420 /* check if we can restrict PCI DMA transfers to 24 bits */
Quentin Lambert412b9792015-04-15 16:10:17 +02002421 if (dma_set_mask(&pci->dev, DMA_BIT_MASK(24)) < 0 ||
2422 dma_set_coherent_mask(&pci->dev, DMA_BIT_MASK(24)) < 0) {
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01002423 dev_err(card->dev,
2424 "architecture does not support 24bit PCI busmaster DMA\n"
Andreas Mohr02330fb2008-05-16 12:18:29 +02002425 );
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002426 err = -ENXIO;
2427 goto out_err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002428 }
2429
Andreas Mohr02330fb2008-05-16 12:18:29 +02002430 err = pci_request_regions(pci, "Aztech AZF3328");
2431 if (err < 0)
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002432 goto out_err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002433
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002434 chip->ctrl_io = pci_resource_start(pci, 0);
Andreas Mohr02330fb2008-05-16 12:18:29 +02002435 chip->game_io = pci_resource_start(pci, 1);
2436 chip->mpu_io = pci_resource_start(pci, 2);
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002437 chip->opl3_io = pci_resource_start(pci, 3);
Andreas Mohr02330fb2008-05-16 12:18:29 +02002438 chip->mixer_io = pci_resource_start(pci, 4);
2439
Andreas Mohr9fd8d362010-12-27 21:17:00 +01002440 codec_setup = &chip->codecs[AZF_CODEC_PLAYBACK];
2441 codec_setup->io_base = chip->ctrl_io + AZF_IO_OFFS_CODEC_PLAYBACK;
Andreas Mohrda237f32010-12-27 21:17:26 +01002442 codec_setup->lock = &chip->reg_lock;
2443 codec_setup->type = AZF_CODEC_PLAYBACK;
Andreas Mohr9fd8d362010-12-27 21:17:00 +01002444 codec_setup->name = "PLAYBACK";
2445
2446 codec_setup = &chip->codecs[AZF_CODEC_CAPTURE];
2447 codec_setup->io_base = chip->ctrl_io + AZF_IO_OFFS_CODEC_CAPTURE;
Andreas Mohrda237f32010-12-27 21:17:26 +01002448 codec_setup->lock = &chip->reg_lock;
2449 codec_setup->type = AZF_CODEC_CAPTURE;
Andreas Mohr9fd8d362010-12-27 21:17:00 +01002450 codec_setup->name = "CAPTURE";
2451
2452 codec_setup = &chip->codecs[AZF_CODEC_I2S_OUT];
2453 codec_setup->io_base = chip->ctrl_io + AZF_IO_OFFS_CODEC_I2S_OUT;
Andreas Mohrda237f32010-12-27 21:17:26 +01002454 codec_setup->lock = &chip->reg_lock;
2455 codec_setup->type = AZF_CODEC_I2S_OUT;
Andreas Mohr9fd8d362010-12-27 21:17:00 +01002456 codec_setup->name = "I2S_OUT";
Linus Torvalds1da177e2005-04-16 15:20:36 -07002457
Takashi Iwai437a5a42006-11-21 12:14:23 +01002458 if (request_irq(pci->irq, snd_azf3328_interrupt,
Takashi Iwai934c2b62011-06-10 16:36:37 +02002459 IRQF_SHARED, KBUILD_MODNAME, chip)) {
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01002460 dev_err(card->dev, "unable to grab IRQ %d\n", pci->irq);
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002461 err = -EBUSY;
2462 goto out_err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002463 }
2464 chip->irq = pci->irq;
2465 pci_set_master(pci);
2466 synchronize_irq(chip->irq);
2467
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002468 snd_azf3328_debug_show_ports(chip);
Andreas Mohr02330fb2008-05-16 12:18:29 +02002469
2470 err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
2471 if (err < 0)
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002472 goto out_err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002473
2474 /* create mixer interface & switches */
Andreas Mohr02330fb2008-05-16 12:18:29 +02002475 err = snd_azf3328_mixer_new(chip);
2476 if (err < 0)
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002477 goto out_err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002478
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002479 /* standard codec init stuff */
2480 /* default DMA init value */
2481 dma_init = DMA_RUN_SOMETHING2|DMA_EPILOGUE_SOMETHING|DMA_SOMETHING_ELSE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002482
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002483 for (codec_type = AZF_CODEC_PLAYBACK;
2484 codec_type <= AZF_CODEC_I2S_OUT; ++codec_type) {
2485 struct snd_azf3328_codec_data *codec =
2486 &chip->codecs[codec_type];
Linus Torvalds1da177e2005-04-16 15:20:36 -07002487
Andreas Mohradf59312010-12-27 21:16:43 +01002488 /* shutdown codecs to reduce power / noise */
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002489 /* have ...ctrl_codec_activity() act properly */
2490 codec->running = 1;
2491 snd_azf3328_ctrl_codec_activity(chip, codec_type, 0);
2492
Andreas Mohrda237f32010-12-27 21:17:26 +01002493 spin_lock_irq(codec->lock);
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002494 snd_azf3328_codec_outb(codec, IDX_IO_CODEC_DMA_FLAGS,
2495 dma_init);
Andreas Mohrda237f32010-12-27 21:17:26 +01002496 spin_unlock_irq(codec->lock);
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002497 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002498
Linus Torvalds1da177e2005-04-16 15:20:36 -07002499 *rchip = chip;
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002500
2501 err = 0;
2502 goto out;
2503
2504out_err:
2505 if (chip)
2506 snd_azf3328_free(chip);
2507 pci_disable_device(pci);
2508
2509out:
2510 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002511}
2512
Bill Pembertone23e7a12012-12-06 12:35:10 -05002513static int
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002514snd_azf3328_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002515{
2516 static int dev;
Takashi Iwai95de7762005-11-17 15:02:42 +01002517 struct snd_card *card;
2518 struct snd_azf3328 *chip;
2519 struct snd_opl3 *opl3;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002520 int err;
2521
Julia Lawalla5a39732011-08-10 11:49:04 +02002522 if (dev >= SNDRV_CARDS) {
2523 err = -ENODEV;
2524 goto out;
2525 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002526 if (!enable[dev]) {
2527 dev++;
Julia Lawalla5a39732011-08-10 11:49:04 +02002528 err = -ENOENT;
2529 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002530 }
2531
Takashi Iwai60c57722014-01-29 14:20:19 +01002532 err = snd_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE,
2533 0, &card);
Takashi Iwaie58de7b2008-12-28 16:44:30 +01002534 if (err < 0)
Julia Lawalla5a39732011-08-10 11:49:04 +02002535 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002536
2537 strcpy(card->driver, "AZF3328");
2538 strcpy(card->shortname, "Aztech AZF3328 (PCI168)");
2539
Andreas Mohr02330fb2008-05-16 12:18:29 +02002540 err = snd_azf3328_create(card, pci, pci_id->driver_data, &chip);
2541 if (err < 0)
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002542 goto out_err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002543
Andreas Mohrca54bde2006-05-17 11:02:24 +02002544 card->private_data = chip;
2545
Andreas Mohr78df6172009-07-12 22:17:54 +02002546 /* chose to use MPU401_HW_AZT2320 ID instead of MPU401_HW_MPU401,
2547 since our hardware ought to be similar, thus use same ID. */
Andreas Mohr02330fb2008-05-16 12:18:29 +02002548 err = snd_mpu401_uart_new(
Andreas Mohr78df6172009-07-12 22:17:54 +02002549 card, 0,
Clemens Ladischdba8b462011-09-13 11:24:41 +02002550 MPU401_HW_AZT2320, chip->mpu_io,
2551 MPU401_INFO_INTEGRATED | MPU401_INFO_IRQ_HOOK,
2552 -1, &chip->rmidi
Andreas Mohr02330fb2008-05-16 12:18:29 +02002553 );
2554 if (err < 0) {
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01002555 dev_err(card->dev, "no MPU-401 device at 0x%lx?\n",
Andreas Mohr02330fb2008-05-16 12:18:29 +02002556 chip->mpu_io
2557 );
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002558 goto out_err;
2559 }
2560
Andreas Mohr02330fb2008-05-16 12:18:29 +02002561 err = snd_azf3328_timer(chip, 0);
2562 if (err < 0)
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002563 goto out_err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002564
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002565 err = snd_azf3328_pcm(chip);
Andreas Mohr02330fb2008-05-16 12:18:29 +02002566 if (err < 0)
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002567 goto out_err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002568
Andreas Mohr02330fb2008-05-16 12:18:29 +02002569 if (snd_opl3_create(card, chip->opl3_io, chip->opl3_io+2,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002570 OPL3_HW_AUTO, 1, &opl3) < 0) {
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01002571 dev_err(card->dev, "no OPL3 device at 0x%lx-0x%lx?\n",
Andreas Mohr02330fb2008-05-16 12:18:29 +02002572 chip->opl3_io, chip->opl3_io+2
2573 );
Linus Torvalds1da177e2005-04-16 15:20:36 -07002574 } else {
Andreas Mohr02330fb2008-05-16 12:18:29 +02002575 /* need to use IDs 1, 2 since ID 0 is snd_azf3328_timer above */
2576 err = snd_opl3_timer_new(opl3, 1, 2);
2577 if (err < 0)
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002578 goto out_err;
Andreas Mohr02330fb2008-05-16 12:18:29 +02002579 err = snd_opl3_hwdep_new(opl3, 0, 1, NULL);
2580 if (err < 0)
2581 goto out_err;
Alban Bedel87c9e7d2012-02-25 16:15:57 +01002582 opl3->private_data = chip;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002583 }
2584
Linus Torvalds1da177e2005-04-16 15:20:36 -07002585 sprintf(card->longname, "%s at 0x%lx, irq %i",
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002586 card->shortname, chip->ctrl_io, chip->irq);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002587
Andreas Mohr02330fb2008-05-16 12:18:29 +02002588 err = snd_card_register(card);
2589 if (err < 0)
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002590 goto out_err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002591
2592#ifdef MODULE
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01002593 dev_info(card->dev,
2594 "Sound driver for Aztech AZF3328-based soundcards such as PCI168.\n");
2595 dev_info(card->dev,
2596 "Hardware was completely undocumented, unfortunately.\n");
2597 dev_info(card->dev,
2598 "Feel free to contact andi AT lisas.de for bug reports etc.!\n");
2599 dev_info(card->dev,
2600 "User-scalable sequencer timer set to %dHz (1024000Hz / %d).\n",
2601 1024000 / seqtimer_scaling, seqtimer_scaling);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002602#endif
2603
Andreas Mohr02330fb2008-05-16 12:18:29 +02002604 snd_azf3328_gameport(chip, dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002605
2606 pci_set_drvdata(pci, card);
2607 dev++;
2608
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002609 err = 0;
2610 goto out;
Andreas Mohr02330fb2008-05-16 12:18:29 +02002611
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002612out_err:
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01002613 dev_err(card->dev, "something failed, exiting\n");
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002614 snd_card_free(card);
Andreas Mohr02330fb2008-05-16 12:18:29 +02002615
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002616out:
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002617 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002618}
2619
Bill Pembertone23e7a12012-12-06 12:35:10 -05002620static void
Andreas Mohrd91c64c2005-10-25 11:17:45 +02002621snd_azf3328_remove(struct pci_dev *pci)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002622{
Linus Torvalds1da177e2005-04-16 15:20:36 -07002623 snd_card_free(pci_get_drvdata(pci));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002624}
2625
Takashi Iwaic7561cd2012-08-14 18:12:04 +02002626#ifdef CONFIG_PM_SLEEP
Andreas Mohr78df6172009-07-12 22:17:54 +02002627static inline void
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01002628snd_azf3328_suspend_regs(const struct snd_azf3328 *chip,
2629 unsigned long io_addr, unsigned count, u32 *saved_regs)
Andreas Mohr78df6172009-07-12 22:17:54 +02002630{
2631 unsigned reg;
2632
2633 for (reg = 0; reg < count; ++reg) {
2634 *saved_regs = inl(io_addr);
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01002635 dev_dbg(chip->card->dev, "suspend: io 0x%04lx: 0x%08x\n",
Andreas Mohr78df6172009-07-12 22:17:54 +02002636 io_addr, *saved_regs);
2637 ++saved_regs;
2638 io_addr += sizeof(*saved_regs);
2639 }
2640}
2641
Andreas Mohrb5dc20c2011-02-19 00:49:32 +01002642static inline void
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01002643snd_azf3328_resume_regs(const struct snd_azf3328 *chip,
2644 const u32 *saved_regs,
Andreas Mohrb5dc20c2011-02-19 00:49:32 +01002645 unsigned long io_addr,
2646 unsigned count
2647)
2648{
2649 unsigned reg;
2650
2651 for (reg = 0; reg < count; ++reg) {
2652 outl(*saved_regs, io_addr);
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01002653 dev_dbg(chip->card->dev,
2654 "resume: io 0x%04lx: 0x%08x --> 0x%08x\n",
Andreas Mohrb5dc20c2011-02-19 00:49:32 +01002655 io_addr, *saved_regs, inl(io_addr));
2656 ++saved_regs;
2657 io_addr += sizeof(*saved_regs);
2658 }
2659}
2660
2661static inline void
2662snd_azf3328_suspend_ac97(struct snd_azf3328 *chip)
2663{
2664#ifdef AZF_USE_AC97_LAYER
2665 snd_ac97_suspend(chip->ac97);
2666#else
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01002667 snd_azf3328_suspend_regs(chip, chip->mixer_io,
Andreas Mohrb5dc20c2011-02-19 00:49:32 +01002668 ARRAY_SIZE(chip->saved_regs_mixer), chip->saved_regs_mixer);
2669
2670 /* make sure to disable master volume etc. to prevent looping sound */
2671 snd_azf3328_mixer_mute_control_master(chip, 1);
2672 snd_azf3328_mixer_mute_control_pcm(chip, 1);
2673#endif /* AZF_USE_AC97_LAYER */
2674}
2675
2676static inline void
2677snd_azf3328_resume_ac97(const struct snd_azf3328 *chip)
2678{
2679#ifdef AZF_USE_AC97_LAYER
2680 snd_ac97_resume(chip->ac97);
2681#else
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01002682 snd_azf3328_resume_regs(chip, chip->saved_regs_mixer, chip->mixer_io,
Andreas Mohrb5dc20c2011-02-19 00:49:32 +01002683 ARRAY_SIZE(chip->saved_regs_mixer));
2684
2685 /* unfortunately with 32bit transfers, IDX_MIXER_PLAY_MASTER (0x02)
2686 and IDX_MIXER_RESET (offset 0x00) get touched at the same time,
2687 resulting in a mixer reset condition persisting until _after_
2688 master vol was restored. Thus master vol needs an extra restore. */
2689 outw(((u16 *)chip->saved_regs_mixer)[1], chip->mixer_io + 2);
2690#endif /* AZF_USE_AC97_LAYER */
2691}
2692
Andreas Mohrca54bde2006-05-17 11:02:24 +02002693static int
Takashi Iwai68cb2b52012-07-02 15:20:37 +02002694snd_azf3328_suspend(struct device *dev)
Andreas Mohrca54bde2006-05-17 11:02:24 +02002695{
Takashi Iwai68cb2b52012-07-02 15:20:37 +02002696 struct snd_card *card = dev_get_drvdata(dev);
Andreas Mohrca54bde2006-05-17 11:02:24 +02002697 struct snd_azf3328 *chip = card->private_data;
Andreas Mohr78df6172009-07-12 22:17:54 +02002698 u16 *saved_regs_ctrl_u16;
Andreas Mohrca54bde2006-05-17 11:02:24 +02002699
2700 snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
Andreas Mohr02330fb2008-05-16 12:18:29 +02002701
Andreas Mohradf59312010-12-27 21:16:43 +01002702 /* same pcm object for playback/capture */
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002703 snd_pcm_suspend_all(chip->pcm[AZF_CODEC_PLAYBACK]);
2704 snd_pcm_suspend_all(chip->pcm[AZF_CODEC_I2S_OUT]);
Andreas Mohrca54bde2006-05-17 11:02:24 +02002705
Andreas Mohrb5dc20c2011-02-19 00:49:32 +01002706 snd_azf3328_suspend_ac97(chip);
Andreas Mohr02330fb2008-05-16 12:18:29 +02002707
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01002708 snd_azf3328_suspend_regs(chip, chip->ctrl_io,
Andreas Mohr78df6172009-07-12 22:17:54 +02002709 ARRAY_SIZE(chip->saved_regs_ctrl), chip->saved_regs_ctrl);
Andreas Mohr627d3e72008-06-23 11:50:47 +02002710
2711 /* manually store the one currently relevant write-only reg, too */
Andreas Mohr78df6172009-07-12 22:17:54 +02002712 saved_regs_ctrl_u16 = (u16 *)chip->saved_regs_ctrl;
2713 saved_regs_ctrl_u16[IDX_IO_6AH / 2] = chip->shadow_reg_ctrl_6AH;
Andreas Mohr627d3e72008-06-23 11:50:47 +02002714
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01002715 snd_azf3328_suspend_regs(chip, chip->game_io,
Andreas Mohr78df6172009-07-12 22:17:54 +02002716 ARRAY_SIZE(chip->saved_regs_game), chip->saved_regs_game);
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01002717 snd_azf3328_suspend_regs(chip, chip->mpu_io,
Andreas Mohr78df6172009-07-12 22:17:54 +02002718 ARRAY_SIZE(chip->saved_regs_mpu), chip->saved_regs_mpu);
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01002719 snd_azf3328_suspend_regs(chip, chip->opl3_io,
Andreas Mohr78df6172009-07-12 22:17:54 +02002720 ARRAY_SIZE(chip->saved_regs_opl3), chip->saved_regs_opl3);
Andreas Mohrca54bde2006-05-17 11:02:24 +02002721 return 0;
2722}
2723
2724static int
Takashi Iwai68cb2b52012-07-02 15:20:37 +02002725snd_azf3328_resume(struct device *dev)
Andreas Mohrca54bde2006-05-17 11:02:24 +02002726{
Takashi Iwai68cb2b52012-07-02 15:20:37 +02002727 struct snd_card *card = dev_get_drvdata(dev);
Andreas Mohrdfbf9512009-07-05 13:55:46 +02002728 const struct snd_azf3328 *chip = card->private_data;
Andreas Mohrca54bde2006-05-17 11:02:24 +02002729
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01002730 snd_azf3328_resume_regs(chip, chip->saved_regs_game, chip->game_io,
Andreas Mohr78df6172009-07-12 22:17:54 +02002731 ARRAY_SIZE(chip->saved_regs_game));
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01002732 snd_azf3328_resume_regs(chip, chip->saved_regs_mpu, chip->mpu_io,
Andreas Mohr78df6172009-07-12 22:17:54 +02002733 ARRAY_SIZE(chip->saved_regs_mpu));
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01002734 snd_azf3328_resume_regs(chip, chip->saved_regs_opl3, chip->opl3_io,
Andreas Mohr78df6172009-07-12 22:17:54 +02002735 ARRAY_SIZE(chip->saved_regs_opl3));
2736
Andreas Mohrb5dc20c2011-02-19 00:49:32 +01002737 snd_azf3328_resume_ac97(chip);
Andreas Mohr78df6172009-07-12 22:17:54 +02002738
Takashi Iwai4a8d9d72014-02-25 14:04:46 +01002739 snd_azf3328_resume_regs(chip, chip->saved_regs_ctrl, chip->ctrl_io,
Andreas Mohr78df6172009-07-12 22:17:54 +02002740 ARRAY_SIZE(chip->saved_regs_ctrl));
Andreas Mohrca54bde2006-05-17 11:02:24 +02002741
2742 snd_power_change_state(card, SNDRV_CTL_POWER_D0);
2743 return 0;
2744}
Andreas Mohrca54bde2006-05-17 11:02:24 +02002745
Takashi Iwai68cb2b52012-07-02 15:20:37 +02002746static SIMPLE_DEV_PM_OPS(snd_azf3328_pm, snd_azf3328_suspend, snd_azf3328_resume);
2747#define SND_AZF3328_PM_OPS &snd_azf3328_pm
2748#else
2749#define SND_AZF3328_PM_OPS NULL
Takashi Iwaic7561cd2012-08-14 18:12:04 +02002750#endif /* CONFIG_PM_SLEEP */
Andreas Mohrca54bde2006-05-17 11:02:24 +02002751
Takashi Iwaie9f66d92012-04-24 12:25:00 +02002752static struct pci_driver azf3328_driver = {
Takashi Iwai3733e422011-06-10 16:20:20 +02002753 .name = KBUILD_MODNAME,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002754 .id_table = snd_azf3328_ids,
2755 .probe = snd_azf3328_probe,
Bill Pembertone23e7a12012-12-06 12:35:10 -05002756 .remove = snd_azf3328_remove,
Takashi Iwai68cb2b52012-07-02 15:20:37 +02002757 .driver = {
2758 .pm = SND_AZF3328_PM_OPS,
2759 },
Linus Torvalds1da177e2005-04-16 15:20:36 -07002760};
2761
Takashi Iwaie9f66d92012-04-24 12:25:00 +02002762module_pci_driver(azf3328_driver);