Blame view

sound/pci/azt3328.c 85.5 KB
b5dc20cd2   Andreas Mohr   ALSA: azt3328: ad...
1
2
  /*  azt3328.c - driver for Aztech AZF3328 based soundcards (e.g. PCI168).
   *  Copyright (C) 2002, 2005 - 2011 by Andreas Mohr <andi AT lisas.de>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
3
4
5
6
7
8
9
10
11
   *
   *  Framework borrowed from Bart Hartgers's als4000.c.
   *  Driver developed on PCI168 AP(W) version (PCI rev. 10, subsystem ID 1801),
   *  found in a Fujitsu-Siemens PC ("Cordant", aluminum case).
   *  Other versions are:
   *  PCI168 A(W), sub ID 1800
   *  PCI168 A/AP, sub ID 8000
   *  Please give me feedback in case you try my driver with one of these!!
   *
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
12
13
14
15
16
   *  Keywords: Windows XP Vista 168nt4-125.zip 168win95-125.zip PCI 168 download
   *  (XP/Vista do not support this card at all but every Linux distribution
   *   has very good support out of the box;
   *   just to make sure that the right people hit this and get to know that,
   *   despite the high level of Internet ignorance - as usual :-P -
78df617ac   Andreas Mohr   ALSA: azt3328: fi...
17
   *   about very good support for this card - on Linux!)
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
18
   *
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
   * GPL LICENSE
   *  This program is free software; you can redistribute it and/or modify
   *  it under the terms of the GNU General Public License as published by
   *  the Free Software Foundation; either version 2 of the License, or
   *  (at your option) any later version.
   *
   *  This program is distributed in the hope that it will be useful,
   *  but WITHOUT ANY WARRANTY; without even the implied warranty of
   *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   *  GNU General Public License for more details.
  
   *  You should have received a copy of the GNU General Public License
   *  along with this program; if not, write to the Free Software
   *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
   *
   * NOTES
   *  Since Aztech does not provide any chipset documentation,
   *  even on repeated request to various addresses,
   *  and the answer that was finally given was negative
   *  (and I was stupid enough to manage to get hold of a PCI168 soundcard
   *  in the first place >:-P}),
   *  I was forced to base this driver on reverse engineering
   *  (3 weeks' worth of evenings filled with driver work).
e2f872608   Andreas Mohr   [ALSA] azt3328.c:...
42
   *  (and no, I did NOT go the easy way: to pick up a SB PCI128 for 9 Euros)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
43
   *
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
44
45
46
   *  It is quite likely that the AZF3328 chip is the PCI cousin of the
   *  AZF3318 ("azt1020 pnp", "MM Pro 16") ISA chip, given very similar specs.
   *
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
47
   *  The AZF3328 chip (note: AZF3328, *not* AZT3328, that's just the driver name
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
48
49
50
   *  for compatibility reasons) from Azfin (joint-venture of Aztech and Fincitec,
   *  Fincitec acquired by National Semiconductor in 2002, together with the
   *  Fincitec-related company ARSmikro) has the following features:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
51
   *
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
52
53
54
55
56
57
   *  - compatibility & compliance:
   *    - Microsoft PC 97 ("PC 97 Hardware Design Guide",
   *                       http://www.microsoft.com/whdc/archive/pcguides.mspx)
   *    - Microsoft PC 98 Baseline Audio
   *    - MPU401 UART
   *    - Sound Blaster Emulation (DOS Box)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
58
   *  - builtin AC97 conformant codec (SNR over 80dB)
13769e3f2   Andreas Mohr   [ALSA] azt3328.c:...
59
60
61
62
63
64
65
66
67
   *    Note that "conformant" != "compliant"!! this chip's mixer register layout
   *    *differs* from the standard AC97 layout:
   *    they chose to not implement the headphone register (which is not a
   *    problem since it's merely optional), yet when doing this, they committed
   *    the grave sin of letting other registers follow immediately instead of
   *    keeping a headphone dummy register, thereby shifting the mixer register
   *    addresses illegally. So far unfortunately it looks like the very flexible
   *    ALSA AC97 support is still not enough to easily compensate for such a
   *    grave layout violation despite all tweaks and quirks mechanisms it offers.
b5dc20cd2   Andreas Mohr   ALSA: azt3328: ad...
68
69
70
71
72
73
74
   *    Well, not quite: now ac97 layer is much improved (bus-specific ops!),
   *    thus I was able to implement support - it's actually working quite well.
   *    An interesting item might be Aztech AMR 2800-W, since it's an AC97
   *    modem card which might reveal the Aztech-specific codec ID which
   *    we might want to pretend, too. Dito PCI168's brother, PCI368,
   *    where the advertising datasheet says it's AC97-based and has a
   *    Digital Enhanced Game Port.
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
75
   *  - builtin genuine OPL3 - verified to work fine, 20080506
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
76
   *  - full duplex 16bit playback/record at independent sampling rate
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
77
78
   *  - MPU401 (+ legacy address support, claimed by one official spec sheet)
   *    FIXME: how to enable legacy addr??
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
79
   *  - game port (legacy address support)
e24a121aa   Andreas Mohr   [ALSA] azt3328.c:...
80
   *  - builtin DirectInput support, helps reduce CPU overhead (interrupt-driven
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
81
82
83
   *    features supported). - See common term "Digital Enhanced Game Port"...
   *    (probably DirectInput 3.0 spec - confirm)
   *  - builtin 3D enhancement (said to be YAMAHA Ymersion)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
84
   *  - built-in General DirectX timer having a 20 bits counter
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
85
   *    with 1us resolution (see below!)
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
86
   *  - I2S serial output port for external DAC
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
87
   *    [FIXME: 3.3V or 5V level? maximum rate is 66.2kHz right?]
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
88
89
90
   *  - supports 33MHz PCI spec 2.1, PCI power management 1.0, compliant with ACPI
   *  - supports hardware volume control
   *  - single chip low cost solution (128 pin QFP)
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
91
   *  - supports programmable Sub-vendor and Sub-system ID [24C02 SEEPROM chip]
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
92
   *    required for Microsoft's logo compliance (FIXME: where?)
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
93
94
95
96
97
   *    At least the Trident 4D Wave DX has one bit somewhere
   *    to enable writes to PCI subsystem VID registers, that should be it.
   *    This might easily be in extended PCI reg space, since PCI168 also has
   *    some custom data starting at 0x80. What kind of config settings
   *    are located in our extended PCI space anyway??
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
98
   *  - PCI168 AP(W) card: power amplifier with 4 Watts/channel at 4 Ohms
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
99
   *    [TDA1517P chip]
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
100
   *
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
101
102
103
104
105
106
107
108
109
   *  Note that this driver now is actually *better* than the Windows driver,
   *  since it additionally supports the card's 1MHz DirectX timer - just try
   *  the following snd-seq module parameters etc.:
   *  - options snd-seq seq_default_timer_class=2 seq_default_timer_sclass=0
   *    seq_default_timer_card=0 seq_client_load=1 seq_default_timer_device=0
   *    seq_default_timer_subdevice=0 seq_default_timer_resolution=1000000
   *  - "timidity -iAv -B2,8 -Os -EFreverb=0"
   *  - "pmidi -p 128:0 jazz.mid"
   *
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
   *  OPL3 hardware playback testing, try something like:
   *  cat /proc/asound/hwdep
   *  and
   *  aconnect -o
   *  Then use
   *  sbiload -Dhw:x,y --opl3 /usr/share/sounds/opl3/std.o3 ......./drums.o3
   *  where x,y is the xx-yy number as given in hwdep.
   *  Then try
   *  pmidi -p a:b jazz.mid
   *  where a:b is the client number plus 0 usually, as given by aconnect above.
   *  Oh, and make sure to unmute the FM mixer control (doh!)
   *  NOTE: power use during OPL3 playback is _VERY_ high (70W --> 90W!)
   *  despite no CPU activity, possibly due to hindering ACPI idling somehow.
   *  Shouldn't be a problem of the AZF3328 chip itself, I'd hope.
   *  Higher PCM / FM mixer levels seem to conflict (causes crackling),
   *  at least sometimes.   Maybe even use with hardware sequencer timer above :)
   *  adplay/adplug-utils might soon offer hardware-based OPL3 playback, too.
   *
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
128
129
130
131
132
133
   *  Certain PCI versions of this card are susceptible to DMA traffic underruns
   *  in some systems (resulting in sound crackling/clicking/popping),
   *  probably because they don't have a DMA FIFO buffer or so.
   *  Overview (PCI ID/PCI subID/PCI rev.):
   *  - no DMA crackling on SiS735: 0x50DC/0x1801/16
   *  - unknown performance: 0x50DC/0x1801/10
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
134
135
   *    (well, it's not bad on an Athlon 1800 with now very optimized IRQ handler)
   *
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
136
137
138
139
140
   *  Crackling happens with VIA chipsets or, in my case, an SiS735, which is
   *  supposed to be very fast and supposed to get rid of crackling much
   *  better than a VIA, yet ironically I still get crackling, like many other
   *  people with the same chipset.
   *  Possible remedies:
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
141
142
   *  - use speaker (amplifier) output instead of headphone output
   *    (in case crackling is due to overloaded output clipping)
25985edce   Lucas De Marchi   Fix common misspe...
143
   *  - plug card into a different PCI slot, preferably one that isn't shared
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
144
145
146
147
148
149
   *    too much (this helps a lot, but not completely!)
   *  - get rid of PCI VGA card, use AGP instead
   *  - upgrade or downgrade BIOS
   *  - fiddle with PCI latency settings (setpci -v -s BUSID latency_timer=XX)
   *    Not too helpful.
   *  - Disable ACPI/power management/"Auto Detect RAM/PCI Clk" in BIOS
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
150
   *
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
151
   * BUGS
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
152
   *  - full-duplex might *still* be problematic, however a recent test was fine
e24a121aa   Andreas Mohr   [ALSA] azt3328.c:...
153
154
155
156
   *  - (non-bug) "Bass/Treble or 3D settings don't work" - they do get evaluated
   *    if you set PCM output switch to "pre 3D" instead of "post 3D".
   *    If this can't be set, then get a mixer application that Isn't Stupid (tm)
   *    (e.g. kmix, gamix) - unfortunately several are!!
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
157
158
159
160
161
162
   *  - locking is not entirely clean, especially the audio stream activity
   *    ints --> may be racy
   *  - an _unconnected_ secondary joystick at the gameport will be reported
   *    to be "active" (floating values, not precisely -1) due to the way we need
   *    to read the Digital Enhanced Game Port. Not sure whether it is fixable.
   *
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
163
   * TODO
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
164
165
166
167
   *  - use PCI_VDEVICE
   *  - verify driver status on x86_64
   *  - test multi-card driver operation
   *  - (ab)use 1MHz DirectX timer as kernel clocksource
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
168
   *  - test MPU401 MIDI playback etc.
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
169
   *  - add more power micro-management (disable various units of the card
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
170
171
172
   *    as long as they're unused, to improve audio quality and save power).
   *    However this requires more I/O ports which I haven't figured out yet
   *    and which thus might not even exist...
ca54bde36   Andreas Mohr   [ALSA] azt3328.c:...
173
174
   *    The standard suspend/resume functionality could probably make use of
   *    some improvement, too...
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
175
   *  - figure out what all unknown port bits are responsible for
13769e3f2   Andreas Mohr   [ALSA] azt3328.c:...
176
177
178
   *  - figure out some cleverly evil scheme to possibly make ALSA AC97 code
   *    fully accept our quite incompatible ""AC97"" mixer and thus save some
   *    code (but I'm not too optimistic that doing this is possible at all)
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
179
   *  - use MMIO (memory-mapped I/O)? Slightly faster access, e.g. for gameport.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
180
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
181
182
  #include <asm/io.h>
  #include <linux/init.h>
689c69120   Andreas Mohr   ALSA: azt3328: im...
183
  #include <linux/bug.h> /* WARN_ONCE */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
184
185
186
187
188
  #include <linux/pci.h>
  #include <linux/delay.h>
  #include <linux/slab.h>
  #include <linux/gameport.h>
  #include <linux/moduleparam.h>
910638ae7   Matthias Gehre   [PATCH] Replace 0...
189
  #include <linux/dma-mapping.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
190
191
192
193
194
195
196
  #include <sound/core.h>
  #include <sound/control.h>
  #include <sound/pcm.h>
  #include <sound/rawmidi.h>
  #include <sound/mpu401.h>
  #include <sound/opl3.h>
  #include <sound/initval.h>
b5dc20cd2   Andreas Mohr   ALSA: azt3328: ad...
197
198
199
200
201
202
203
204
205
206
  /*
   * Config switch, to use ALSA's AC97 layer instead of old custom mixer crap.
   * If the AC97 compatibility parts we needed to implement locally turn out
   * to work nicely, then remove the old implementation eventually.
   */
  #define AZF_USE_AC97_LAYER 1
  
  #ifdef AZF_USE_AC97_LAYER
  #include <sound/ac97_codec.h>
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
207
  #include "azt3328.h"
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
208
  MODULE_AUTHOR("Andreas Mohr <andi AT lisas.de>");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
209
210
211
212
213
  MODULE_DESCRIPTION("Aztech AZF3328 (PCI168)");
  MODULE_LICENSE("GPL");
  MODULE_SUPPORTED_DEVICE("{{Aztech,AZF3328}}");
  
  #if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE))
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
214
  #define SUPPORT_GAMEPORT 1
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
215
  #endif
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
216
217
  /* === Debug settings ===
    Further diagnostic functionality than the settings below
adf5931f8   Andreas Mohr   ALSA: azt3328: co...
218
    does not need to be provided, since one can easily write a POSIX shell script
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
219
    to dump the card's I/O ports (those listed in lspci -v -v):
adf5931f8   Andreas Mohr   ALSA: azt3328: co...
220
    dump()
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
221
222
223
224
    {
      local descr=$1; local addr=$2; local count=$3
  
      echo "${descr}: ${count} @ ${addr}:"
adf5931f8   Andreas Mohr   ALSA: azt3328: co...
225
226
      dd if=/dev/port skip=`printf %d ${addr}` count=${count} bs=1 \
        2>/dev/null| hexdump -C
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
227
228
229
230
231
232
233
    }
    and then use something like
    "dump joy200 0x200 8", "dump mpu388 0x388 4", "dump joy 0xb400 8",
    "dump codec00 0xa800 32", "dump mixer 0xb800 64", "dump synth 0xbc00 8",
    possibly within a "while true; do ... sleep 1; done" loop.
    Tweaking ports could be done using
    VALSTRING="`printf "%02x" $value`"
adf5931f8   Andreas Mohr   ALSA: azt3328: co...
234
235
    printf "\x""$VALSTRING"|dd of=/dev/port seek=`printf %d ${addr}` bs=1 \
      2>/dev/null
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
236
  */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
237
238
239
  #define DEBUG_MISC	0
  #define DEBUG_CALLS	0
  #define DEBUG_MIXER	0
78df617ac   Andreas Mohr   ALSA: azt3328: fi...
240
  #define DEBUG_CODEC	0
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
241
  #define DEBUG_TIMER	0
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
242
  #define DEBUG_GAME	0
78df617ac   Andreas Mohr   ALSA: azt3328: fi...
243
  #define DEBUG_PM	0
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
244
245
246
  #define MIXER_TESTING	0
  
  #if DEBUG_MISC
78df617ac   Andreas Mohr   ALSA: azt3328: fi...
247
  #define snd_azf3328_dbgmisc(format, args...) printk(KERN_DEBUG format, ##args)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
248
249
  #else
  #define snd_azf3328_dbgmisc(format, args...)
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
250
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
251
252
253
  
  #if DEBUG_CALLS
  #define snd_azf3328_dbgcalls(format, args...) printk(format, ##args)
78df617ac   Andreas Mohr   ALSA: azt3328: fi...
254
255
256
257
  #define snd_azf3328_dbgcallenter() printk(KERN_DEBUG "--> %s
  ", __func__)
  #define snd_azf3328_dbgcallleave() printk(KERN_DEBUG "<-- %s
  ", __func__)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
258
259
260
261
  #else
  #define snd_azf3328_dbgcalls(format, args...)
  #define snd_azf3328_dbgcallenter()
  #define snd_azf3328_dbgcallleave()
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
262
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
263
264
  
  #if DEBUG_MIXER
ee419653a   Takashi Iwai   ALSA: Fix missing...
265
  #define snd_azf3328_dbgmixer(format, args...) printk(KERN_DEBUG format, ##args)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
266
267
  #else
  #define snd_azf3328_dbgmixer(format, args...)
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
268
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
269

78df617ac   Andreas Mohr   ALSA: azt3328: fi...
270
271
  #if DEBUG_CODEC
  #define snd_azf3328_dbgcodec(format, args...) printk(KERN_DEBUG format, ##args)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
272
  #else
78df617ac   Andreas Mohr   ALSA: azt3328: fi...
273
  #define snd_azf3328_dbgcodec(format, args...)
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
274
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
275

d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
276
  #if DEBUG_MISC
ee419653a   Takashi Iwai   ALSA: Fix missing...
277
  #define snd_azf3328_dbgtimer(format, args...) printk(KERN_DEBUG format, ##args)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
278
  #else
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
279
  #define snd_azf3328_dbgtimer(format, args...)
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
280
281
282
  #endif
  
  #if DEBUG_GAME
ee419653a   Takashi Iwai   ALSA: Fix missing...
283
  #define snd_azf3328_dbggame(format, args...) printk(KERN_DEBUG format, ##args)
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
284
285
286
  #else
  #define snd_azf3328_dbggame(format, args...)
  #endif
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
287

78df617ac   Andreas Mohr   ALSA: azt3328: fi...
288
289
290
291
292
  #if DEBUG_PM
  #define snd_azf3328_dbgpm(format, args...) printk(KERN_DEBUG format, ##args)
  #else
  #define snd_azf3328_dbgpm(format, args...)
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
293
294
295
296
297
298
299
300
301
302
303
  static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */
  module_param_array(index, int, NULL, 0444);
  MODULE_PARM_DESC(index, "Index value for AZF3328 soundcard.");
  
  static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
  module_param_array(id, charp, NULL, 0444);
  MODULE_PARM_DESC(id, "ID string for AZF3328 soundcard.");
  
  static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;	/* Enable this card */
  module_param_array(enable, bool, NULL, 0444);
  MODULE_PARM_DESC(enable, "Enable AZF3328 soundcard.");
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
304
305
306
  static int seqtimer_scaling = 128;
  module_param(seqtimer_scaling, int, 0444);
  MODULE_PARM_DESC(seqtimer_scaling, "Set 1024000Hz sequencer timer scale factor (lockup danger!). Default 128.");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
307

dfbf95111   Andreas Mohr   ALSA: azt3328: la...
308
  enum snd_azf3328_codec_type {
adf5931f8   Andreas Mohr   ALSA: azt3328: co...
309
    /* warning: fixed indices (also used for bitmask checks!) */
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
310
311
312
    AZF_CODEC_PLAYBACK = 0,
    AZF_CODEC_CAPTURE = 1,
    AZF_CODEC_I2S_OUT = 2,
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
313
  };
da237f35a   Andreas Mohr   ALSA: azt3328: us...
314
315
316
317
318
319
320
321
322
  struct snd_azf3328_codec_data {
  	unsigned long io_base; /* keep first! (avoid offset calc) */
  	unsigned int dma_base; /* helper to avoid an indirection in hotpath */
  	spinlock_t *lock; /* TODO: convert to our own per-codec lock member */
  	struct snd_pcm_substream *substream;
  	bool running;
  	enum snd_azf3328_codec_type type;
  	const char *name;
  };
95de77660   Takashi Iwai   [ALSA] Remove xxx...
323
  struct snd_azf3328 {
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
324
  	/* often-used fields towards beginning, then grouped */
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
325

dfbf95111   Andreas Mohr   ALSA: azt3328: la...
326
  	unsigned long ctrl_io; /* usually 0xb000, size 128 */
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
327
328
329
330
  	unsigned long game_io;  /* usually 0xb400, size 8 */
  	unsigned long mpu_io;   /* usually 0xb800, size 4 */
  	unsigned long opl3_io; /* usually 0xbc00, size 8 */
  	unsigned long mixer_io; /* usually 0xc000, size 64 */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
331

d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
332
  	spinlock_t reg_lock;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
333

95de77660   Takashi Iwai   [ALSA] Remove xxx...
334
  	struct snd_timer *timer;
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
335

dfbf95111   Andreas Mohr   ALSA: azt3328: la...
336
337
338
339
  	struct snd_pcm *pcm[3];
  
  	/* playback, recording and I2S out codecs */
  	struct snd_azf3328_codec_data codecs[3];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
340

b5dc20cd2   Andreas Mohr   ALSA: azt3328: ad...
341
342
343
  #ifdef AZF_USE_AC97_LAYER
  	struct snd_ac97 *ac97;
  #endif
95de77660   Takashi Iwai   [ALSA] Remove xxx...
344
345
  	struct snd_card *card;
  	struct snd_rawmidi *rmidi;
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
346

02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
347
  #ifdef SUPPORT_GAMEPORT
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
348
  	struct gameport *gameport;
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
349
  	u16 axes[4];
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
350
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
351

d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
352
353
  	struct pci_dev *pci;
  	int irq;
ca54bde36   Andreas Mohr   [ALSA] azt3328.c:...
354

627d3e7ab   Andreas Mohr   ALSA: PCI168 snd-...
355
356
357
358
  	/* register 0x6a is write-only, thus need to remember setting.
  	 * If we need to add more registers here, then we might try to fold this
  	 * into some transparent combined shadow register handling with
  	 * CONFIG_PM register storage below, but that's slightly difficult. */
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
359
  	u16 shadow_reg_ctrl_6AH;
627d3e7ab   Andreas Mohr   ALSA: PCI168 snd-...
360

ca54bde36   Andreas Mohr   [ALSA] azt3328.c:...
361
362
  #ifdef CONFIG_PM
  	/* register value containers for power management
78df617ac   Andreas Mohr   ALSA: azt3328: fi...
363
364
365
366
367
368
  	 * Note: not always full I/O range preserved (similar to Win driver!) */
  	u32 saved_regs_ctrl[AZF_ALIGN(AZF_IO_SIZE_CTRL_PM) / 4];
  	u32 saved_regs_game[AZF_ALIGN(AZF_IO_SIZE_GAME_PM) / 4];
  	u32 saved_regs_mpu[AZF_ALIGN(AZF_IO_SIZE_MPU_PM) / 4];
  	u32 saved_regs_opl3[AZF_ALIGN(AZF_IO_SIZE_OPL3_PM) / 4];
  	u32 saved_regs_mixer[AZF_ALIGN(AZF_IO_SIZE_MIXER_PM) / 4];
ca54bde36   Andreas Mohr   [ALSA] azt3328.c:...
369
  #endif
95de77660   Takashi Iwai   [ALSA] Remove xxx...
370
  };
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
371

cebe41d4b   Alexey Dobriyan   sound: use DEFINE...
372
  static DEFINE_PCI_DEVICE_TABLE(snd_azf3328_ids) = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
373
374
375
376
377
378
  	{ 0x122D, 0x50DC, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },   /* PCI168/3328 */
  	{ 0x122D, 0x80DA, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },   /* 3328 */
  	{ 0, }
  };
  
  MODULE_DEVICE_TABLE(pci, snd_azf3328_ids);
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
379
380
  
  static int
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
381
  snd_azf3328_io_reg_setb(unsigned reg, u8 mask, bool do_set)
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
382
  {
adf5931f8   Andreas Mohr   ALSA: azt3328: co...
383
384
385
  	/* Well, strictly spoken, the inb/outb sequence isn't atomic
  	   and would need locking. However we currently don't care
  	   since it potentially complicates matters. */
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
386
387
388
389
390
391
392
393
394
395
396
  	u8 prev = inb(reg), new;
  
  	new = (do_set) ? (prev|mask) : (prev & ~mask);
  	/* we need to always write the new value no matter whether it differs
  	 * or not, since some register bits don't indicate their setting */
  	outb(new, reg);
  	if (new != prev)
  		return 1;
  
  	return 0;
  }
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
397
  static inline void
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
398
399
400
401
  snd_azf3328_codec_outb(const struct snd_azf3328_codec_data *codec,
  		       unsigned reg,
  		       u8 value
  )
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
402
  {
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
403
  	outb(value, codec->io_base + reg);
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
404
405
406
  }
  
  static inline u8
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
407
  snd_azf3328_codec_inb(const struct snd_azf3328_codec_data *codec, unsigned reg)
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
408
  {
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
409
  	return inb(codec->io_base + reg);
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
410
411
412
  }
  
  static inline void
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
413
414
415
416
  snd_azf3328_codec_outw(const struct snd_azf3328_codec_data *codec,
  		       unsigned reg,
  		       u16 value
  )
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
417
  {
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
418
  	outw(value, codec->io_base + reg);
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
419
420
421
  }
  
  static inline u16
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
422
  snd_azf3328_codec_inw(const struct snd_azf3328_codec_data *codec, unsigned reg)
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
423
  {
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
424
  	return inw(codec->io_base + reg);
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
425
426
427
  }
  
  static inline void
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
428
429
430
431
  snd_azf3328_codec_outl(const struct snd_azf3328_codec_data *codec,
  		       unsigned reg,
  		       u32 value
  )
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
432
  {
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
433
  	outl(value, codec->io_base + reg);
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
434
  }
689c69120   Andreas Mohr   ALSA: azt3328: im...
435
436
437
438
439
440
441
442
443
444
445
446
447
448
  static inline void
  snd_azf3328_codec_outl_multi(const struct snd_azf3328_codec_data *codec,
  			     unsigned reg, const void *buffer, int count
  )
  {
  	unsigned long addr = codec->io_base + reg;
  	if (count) {
  		const u32 *buf = buffer;
  		do {
  			outl(*buf++, addr);
  			addr += 4;
  		} while (--count);
  	}
  }
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
449
  static inline u32
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
450
451
452
453
454
455
456
  snd_azf3328_codec_inl(const struct snd_azf3328_codec_data *codec, unsigned reg)
  {
  	return inl(codec->io_base + reg);
  }
  
  static inline void
  snd_azf3328_ctrl_outb(const struct snd_azf3328 *chip, unsigned reg, u8 value)
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
457
  {
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
  	outb(value, chip->ctrl_io + reg);
  }
  
  static inline u8
  snd_azf3328_ctrl_inb(const struct snd_azf3328 *chip, unsigned reg)
  {
  	return inb(chip->ctrl_io + reg);
  }
  
  static inline void
  snd_azf3328_ctrl_outw(const struct snd_azf3328 *chip, unsigned reg, u16 value)
  {
  	outw(value, chip->ctrl_io + reg);
  }
  
  static inline void
  snd_azf3328_ctrl_outl(const struct snd_azf3328 *chip, unsigned reg, u32 value)
  {
  	outl(value, chip->ctrl_io + reg);
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
477
478
479
  }
  
  static inline void
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
480
  snd_azf3328_game_outb(const struct snd_azf3328 *chip, unsigned reg, u8 value)
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
481
  {
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
482
  	outb(value, chip->game_io + reg);
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
483
484
485
  }
  
  static inline void
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
486
  snd_azf3328_game_outw(const struct snd_azf3328 *chip, unsigned reg, u16 value)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
487
  {
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
488
  	outw(value, chip->game_io + reg);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
489
  }
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
490
  static inline u8
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
491
492
493
494
495
496
497
  snd_azf3328_game_inb(const struct snd_azf3328 *chip, unsigned reg)
  {
  	return inb(chip->game_io + reg);
  }
  
  static inline u16
  snd_azf3328_game_inw(const struct snd_azf3328 *chip, unsigned reg)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
498
  {
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
499
  	return inw(chip->game_io + reg);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
500
  }
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
501
  static inline void
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
502
  snd_azf3328_mixer_outw(const struct snd_azf3328 *chip, unsigned reg, u16 value)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
503
  {
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
504
  	outw(value, chip->mixer_io + reg);
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
505
506
507
  }
  
  static inline u16
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
508
  snd_azf3328_mixer_inw(const struct snd_azf3328 *chip, unsigned reg)
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
509
  {
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
510
  	return inw(chip->mixer_io + reg);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
511
  }
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
512
  #define AZF_MUTE_BIT 0x80
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
513
  static bool
b5dc20cd2   Andreas Mohr   ALSA: azt3328: ad...
514
  snd_azf3328_mixer_mute_control(const struct snd_azf3328 *chip,
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
515
  			   unsigned reg, bool do_mute
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
516
  )
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
517
  {
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
518
  	unsigned long portbase = chip->mixer_io + reg + 1;
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
519
  	bool updated;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
520
521
522
  
  	/* the mute bit is on the *second* (i.e. right) register of a
  	 * left/right channel setting */
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
523
524
525
526
  	updated = snd_azf3328_io_reg_setb(portbase, AZF_MUTE_BIT, do_mute);
  
  	/* indicate whether it was muted before */
  	return (do_mute) ? !updated : updated;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
527
  }
b5dc20cd2   Andreas Mohr   ALSA: azt3328: ad...
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
  static inline bool
  snd_azf3328_mixer_mute_control_master(const struct snd_azf3328 *chip,
  			   bool do_mute
  )
  {
  	return snd_azf3328_mixer_mute_control(
  		chip,
  		IDX_MIXER_PLAY_MASTER,
  		do_mute
  	);
  }
  
  static inline bool
  snd_azf3328_mixer_mute_control_pcm(const struct snd_azf3328 *chip,
  			   bool do_mute
  )
  {
  	return snd_azf3328_mixer_mute_control(
  		chip,
  		IDX_MIXER_WAVEOUT,
  		do_mute
  	);
  }
  
  static inline void
  snd_azf3328_mixer_reset(const struct snd_azf3328 *chip)
  {
  	/* reset (close) mixer:
  	 * first mute master volume, then reset
  	 */
  	snd_azf3328_mixer_mute_control_master(chip, 1);
  	snd_azf3328_mixer_outw(chip, IDX_MIXER_RESET, 0x0000);
  }
  
  #ifdef AZF_USE_AC97_LAYER
  
  static inline void
  snd_azf3328_mixer_ac97_map_unsupported(unsigned short reg, const char *mode)
  {
  	/* need to add some more or less clever emulation? */
  	printk(KERN_WARNING
  		"azt3328: missing %s emulation for AC97 register 0x%02x!
  ",
  		mode, reg);
  }
  
  /*
   * Need to have _special_ AC97 mixer hardware register index mapper,
   * to compensate for the issue of a rather AC97-incompatible hardware layout.
   */
  #define AZF_REG_MASK 0x3f
  #define AZF_AC97_REG_UNSUPPORTED 0x8000
  #define AZF_AC97_REG_REAL_IO_READ 0x4000
  #define AZF_AC97_REG_REAL_IO_WRITE 0x2000
  #define AZF_AC97_REG_REAL_IO_RW \
  	(AZF_AC97_REG_REAL_IO_READ | AZF_AC97_REG_REAL_IO_WRITE)
  #define AZF_AC97_REG_EMU_IO_READ 0x0400
  #define AZF_AC97_REG_EMU_IO_WRITE 0x0200
  #define AZF_AC97_REG_EMU_IO_RW \
  	(AZF_AC97_REG_EMU_IO_READ | AZF_AC97_REG_EMU_IO_WRITE)
  static unsigned short
  snd_azf3328_mixer_ac97_map_reg_idx(unsigned short reg)
  {
  	static const struct {
  		unsigned short azf_reg;
  	} azf_reg_mapper[] = {
  		/* Especially when taking into consideration
  		 * mono/stereo-based sequence of azf vs. AC97 control series,
  		 * it's quite obvious that azf simply got rid
  		 * of the AC97_HEADPHONE control at its intended offset,
  		 * thus shifted _all_ controls by one,
  		 * and _then_ simply added it as an FMSYNTH control at the end,
  		 * to make up for the offset.
  		 * This means we'll have to translate indices here as
  		 * needed and then do some tiny AC97 patch action
  		 * (snd_ac97_rename_vol_ctl() etc.) - that's it.
  		 */
  		{ /* AC97_RESET */ IDX_MIXER_RESET
  			| AZF_AC97_REG_REAL_IO_WRITE
  			| AZF_AC97_REG_EMU_IO_READ },
  		{ /* AC97_MASTER */ IDX_MIXER_PLAY_MASTER },
  		 /* note large shift: AC97_HEADPHONE to IDX_MIXER_FMSYNTH! */
  		{ /* AC97_HEADPHONE */ IDX_MIXER_FMSYNTH },
  		{ /* AC97_MASTER_MONO */ IDX_MIXER_MODEMOUT },
  		{ /* AC97_MASTER_TONE */ IDX_MIXER_BASSTREBLE },
  		{ /* AC97_PC_BEEP */ IDX_MIXER_PCBEEP },
  		{ /* AC97_PHONE */ IDX_MIXER_MODEMIN },
  		{ /* AC97_MIC */ IDX_MIXER_MIC },
  		{ /* AC97_LINE */ IDX_MIXER_LINEIN },
  		{ /* AC97_CD */ IDX_MIXER_CDAUDIO },
  		{ /* AC97_VIDEO */ IDX_MIXER_VIDEO },
  		{ /* AC97_AUX */ IDX_MIXER_AUX },
  		{ /* AC97_PCM */ IDX_MIXER_WAVEOUT },
  		{ /* AC97_REC_SEL */ IDX_MIXER_REC_SELECT },
  		{ /* AC97_REC_GAIN */ IDX_MIXER_REC_VOLUME },
  		{ /* AC97_REC_GAIN_MIC */ AZF_AC97_REG_EMU_IO_RW },
  		{ /* AC97_GENERAL_PURPOSE */ IDX_MIXER_ADVCTL2 },
  		{ /* AC97_3D_CONTROL */ IDX_MIXER_ADVCTL1 },
  	};
  
  	unsigned short reg_azf = AZF_AC97_REG_UNSUPPORTED;
  
  	/* azf3328 supports the low-numbered and low-spec:ed range
  	   of AC97 regs only */
  	if (reg <= AC97_3D_CONTROL) {
  		unsigned short reg_idx = reg / 2;
  		reg_azf = azf_reg_mapper[reg_idx].azf_reg;
  		/* a translation-only entry means it's real read/write: */
  		if (!(reg_azf & ~AZF_REG_MASK))
  			reg_azf |= AZF_AC97_REG_REAL_IO_RW;
  	} else {
  		switch (reg) {
  		case AC97_POWERDOWN:
  			reg_azf = AZF_AC97_REG_EMU_IO_RW;
  			break;
  		case AC97_EXTENDED_ID:
  			reg_azf = AZF_AC97_REG_EMU_IO_READ;
  			break;
  		case AC97_EXTENDED_STATUS:
  			/* I don't know what the h*ll AC97 layer
  			 * would consult this _extended_ register for
  			 * given a base-AC97-advertised card,
  			 * but let's just emulate it anyway :-P
  			 */
  			reg_azf = AZF_AC97_REG_EMU_IO_RW;
  			break;
  		case AC97_VENDOR_ID1:
  		case AC97_VENDOR_ID2:
  			reg_azf = AZF_AC97_REG_EMU_IO_READ;
  			break;
  		}
  	}
  	return reg_azf;
  }
  
  static const unsigned short
  azf_emulated_ac97_caps =
  	AC97_BC_DEDICATED_MIC |
  	AC97_BC_BASS_TREBLE |
  	/* Headphone is an FM Synth control here */
  	AC97_BC_HEADPHONE |
  	/* no AC97_BC_LOUDNESS! */
  	/* mask 0x7c00 is
  	   vendor-specific 3D enhancement
  	   vendor indicator.
  	   Since there actually _is_ an
  	   entry for Aztech Labs
  	   (13), make damn sure
  	   to indicate it. */
  	(13 << 10);
  
  static const unsigned short
  azf_emulated_ac97_powerdown =
  	/* pretend everything to be active */
  		AC97_PD_ADC_STATUS |
  		AC97_PD_DAC_STATUS |
  		AC97_PD_MIXER_STATUS |
  		AC97_PD_VREF_STATUS;
  
  /*
   * Emulated, _inofficial_ vendor ID
   * (there might be some devices such as the MR 2800-W
   * which could reveal the real Aztech AC97 ID).
   * We choose to use "AZT" prefix, and then use 1 to indicate PCI168
   * (better don't use 0x68 since there's a PCI368 as well).
   */
  static const unsigned int
  azf_emulated_ac97_vendor_id = 0x415a5401;
  
  static unsigned short
  snd_azf3328_mixer_ac97_read(struct snd_ac97 *ac97, unsigned short reg_ac97)
  {
  	const struct snd_azf3328 *chip = ac97->private_data;
  	unsigned short reg_azf = snd_azf3328_mixer_ac97_map_reg_idx(reg_ac97);
  	unsigned short reg_val = 0;
  	bool unsupported = 0;
  
  	snd_azf3328_dbgmixer(
  		"snd_azf3328_mixer_ac97_read reg_ac97 %u
  ",
  			reg_ac97
  	);
  	if (reg_azf & AZF_AC97_REG_UNSUPPORTED)
  		unsupported = 1;
  	else {
  		if (reg_azf & AZF_AC97_REG_REAL_IO_READ)
  			reg_val = snd_azf3328_mixer_inw(chip,
  						reg_azf & AZF_REG_MASK);
  		else {
  			/*
  			 * Proceed with dummy I/O read,
  			 * to ensure compatible timing where this may matter.
  			 * (ALSA AC97 layer usually doesn't call I/O functions
  			 * due to intelligent I/O caching anyway)
  			 * Choose a mixer register that's thoroughly unrelated
  			 * to common audio (try to minimize distortion).
  			 */
  			snd_azf3328_mixer_inw(chip, IDX_MIXER_SOMETHING30H);
  		}
  
  		if (reg_azf & AZF_AC97_REG_EMU_IO_READ) {
  			switch (reg_ac97) {
  			case AC97_RESET:
  				reg_val |= azf_emulated_ac97_caps;
  				break;
  			case AC97_POWERDOWN:
  				reg_val |= azf_emulated_ac97_powerdown;
  				break;
  			case AC97_EXTENDED_ID:
  			case AC97_EXTENDED_STATUS:
  				/* AFAICS we simply can't support anything: */
  				reg_val |= 0;
  				break;
  			case AC97_VENDOR_ID1:
  				reg_val = azf_emulated_ac97_vendor_id >> 16;
  				break;
  			case AC97_VENDOR_ID2:
  				reg_val = azf_emulated_ac97_vendor_id & 0xffff;
  				break;
  			default:
  				unsupported = 1;
  				break;
  			}
  		}
  	}
  	if (unsupported)
  		snd_azf3328_mixer_ac97_map_unsupported(reg_ac97, "read");
  
  	return reg_val;
  }
  
  static void
  snd_azf3328_mixer_ac97_write(struct snd_ac97 *ac97,
  		     unsigned short reg_ac97, unsigned short val)
  {
  	const struct snd_azf3328 *chip = ac97->private_data;
  	unsigned short reg_azf = snd_azf3328_mixer_ac97_map_reg_idx(reg_ac97);
  	bool unsupported = 0;
  
  	snd_azf3328_dbgmixer(
  		"snd_azf3328_mixer_ac97_write reg_ac97 %u val %u
  ",
  			reg_ac97, val
  	);
  	if (reg_azf & AZF_AC97_REG_UNSUPPORTED)
  		unsupported = 1;
  	else {
  		if (reg_azf & AZF_AC97_REG_REAL_IO_WRITE)
  			snd_azf3328_mixer_outw(
  				chip,
  				reg_azf & AZF_REG_MASK,
  				val
  			);
  		else
  		if (reg_azf & AZF_AC97_REG_EMU_IO_WRITE) {
  			switch (reg_ac97) {
  			case AC97_REC_GAIN_MIC:
  			case AC97_POWERDOWN:
  			case AC97_EXTENDED_STATUS:
  				/*
  				 * Silently swallow these writes.
  				 * Since for most registers our card doesn't
  				 * actually support a comparable feature,
  				 * this is exactly what we should do here.
  				 * The AC97 layer's I/O caching probably
  				 * automatically takes care of all the rest...
  				 * (remembers written values etc.)
  				 */
  				break;
  			default:
  				unsupported = 1;
  				break;
  			}
  		}
  	}
  	if (unsupported)
  		snd_azf3328_mixer_ac97_map_unsupported(reg_ac97, "write");
  }
  
  static int __devinit
  snd_azf3328_mixer_new(struct snd_azf3328 *chip)
  {
  	struct snd_ac97_bus *bus;
  	struct snd_ac97_template ac97;
  	static struct snd_ac97_bus_ops ops = {
  		.write = snd_azf3328_mixer_ac97_write,
  		.read = snd_azf3328_mixer_ac97_read,
  	};
  	int rc;
  
  	memset(&ac97, 0, sizeof(ac97));
  	ac97.scaps = AC97_SCAP_SKIP_MODEM
  			| AC97_SCAP_AUDIO /* we support audio! */
  			| AC97_SCAP_NO_SPDIF;
  	ac97.private_data = chip;
  	ac97.pci = chip->pci;
  
  	/*
  	 * ALSA's AC97 layer has terrible init crackling issues,
  	 * unfortunately, and since it makes use of AC97_RESET,
  	 * there's no use trying to mute Master Playback proactively.
  	 */
  
  	rc = snd_ac97_bus(chip->card, 0, &ops, NULL, &bus);
  	if (!rc)
  		rc = snd_ac97_mixer(bus, &ac97, &chip->ac97);
  		/*
  		 * Make sure to complain loudly in case of AC97 init failure,
  		 * since failure may happen quite often,
  		 * due to this card being a very quirky AC97 "lookalike".
  		 */
  	if (rc)
  		printk(KERN_ERR "azt3328: AC97 init failed, err %d!
  ", rc);
  
  	/* If we return an error here, then snd_card_free() should
  	 * free up any ac97 codecs that got created, as well as the bus.
  	 */
  	return rc;
  }
  #else /* AZF_USE_AC97_LAYER */
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
849
  static void
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
850
851
852
853
854
855
  snd_azf3328_mixer_write_volume_gradually(const struct snd_azf3328 *chip,
  					 unsigned reg,
  					 unsigned char dst_vol_left,
  					 unsigned char dst_vol_right,
  					 int chan_sel, int delay
  )
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
856
  {
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
857
  	unsigned long portbase = chip->mixer_io + reg;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
858
  	unsigned char curr_vol_left = 0, curr_vol_right = 0;
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
859
  	int left_change = 0, right_change = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
860
  	snd_azf3328_dbgcallenter();
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
861
862
  
  	if (chan_sel & SET_CHAN_LEFT) {
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
863
  		curr_vol_left  = inb(portbase + 1);
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
864
865
866
867
868
869
870
871
872
873
874
  
  		/* take care of muting flag contained in left channel */
  		if (curr_vol_left & AZF_MUTE_BIT)
  			dst_vol_left |= AZF_MUTE_BIT;
  		else
  			dst_vol_left &= ~AZF_MUTE_BIT;
  
  		left_change = (curr_vol_left > dst_vol_left) ? -1 : 1;
  	}
  
  	if (chan_sel & SET_CHAN_RIGHT) {
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
875
  		curr_vol_right = inb(portbase + 0);
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
876
877
878
  
  		right_change = (curr_vol_right > dst_vol_right) ? -1 : 1;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
879

e2f872608   Andreas Mohr   [ALSA] azt3328.c:...
880
  	do {
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
881
882
883
884
885
886
  		if (left_change) {
  			if (curr_vol_left != dst_vol_left) {
  				curr_vol_left += left_change;
  				outb(curr_vol_left, portbase + 1);
  			} else
  			    left_change = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
887
  		}
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
888
889
890
  		if (right_change) {
  			if (curr_vol_right != dst_vol_right) {
  				curr_vol_right += right_change;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
891
892
893
  			/* during volume change, the right channel is crackling
  			 * somewhat more than the left channel, unfortunately.
  			 * This seems to be a hardware issue. */
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
894
895
896
  				outb(curr_vol_right, portbase + 0);
  			} else
  			    right_change = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
897
898
899
  		}
  		if (delay)
  			mdelay(delay);
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
900
  	} while ((left_change) || (right_change));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
901
902
903
904
905
906
  	snd_azf3328_dbgcallleave();
  }
  
  /*
   * general mixer element
   */
95de77660   Takashi Iwai   [ALSA] Remove xxx...
907
  struct azf3328_mixer_reg {
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
908
  	unsigned reg;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
909
910
911
912
913
  	unsigned int lchan_shift, rchan_shift;
  	unsigned int mask;
  	unsigned int invert: 1;
  	unsigned int stereo: 1;
  	unsigned int enum_c: 4;
95de77660   Takashi Iwai   [ALSA] Remove xxx...
914
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
915
916
  
  #define COMPOSE_MIXER_REG(reg,lchan_shift,rchan_shift,mask,invert,stereo,enum_c) \
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
917
918
919
920
921
   ((reg) | (lchan_shift << 8) | (rchan_shift << 12) | \
    (mask << 16) | \
    (invert << 24) | \
    (stereo << 25) | \
    (enum_c << 26))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
922

95de77660   Takashi Iwai   [ALSA] Remove xxx...
923
  static void snd_azf3328_mixer_reg_decode(struct azf3328_mixer_reg *r, unsigned long val)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
  {
  	r->reg = val & 0xff;
  	r->lchan_shift = (val >> 8) & 0x0f;
  	r->rchan_shift = (val >> 12) & 0x0f;
  	r->mask = (val >> 16) & 0xff;
  	r->invert = (val >> 24) & 1;
  	r->stereo = (val >> 25) & 1;
  	r->enum_c = (val >> 26) & 0x0f;
  }
  
  /*
   * mixer switches/volumes
   */
  
  #define AZF3328_MIXER_SWITCH(xname, reg, shift, invert) \
  { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
    .info = snd_azf3328_info_mixer, \
    .get = snd_azf3328_get_mixer, .put = snd_azf3328_put_mixer, \
    .private_value = COMPOSE_MIXER_REG(reg, shift, 0, 0x1, invert, 0, 0), \
  }
  
  #define AZF3328_MIXER_VOL_STEREO(xname, reg, mask, invert) \
  { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
    .info = snd_azf3328_info_mixer, \
    .get = snd_azf3328_get_mixer, .put = snd_azf3328_put_mixer, \
    .private_value = COMPOSE_MIXER_REG(reg, 8, 0, mask, invert, 1, 0), \
  }
  
  #define AZF3328_MIXER_VOL_MONO(xname, reg, mask, is_right_chan) \
  { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
    .info = snd_azf3328_info_mixer, \
    .get = snd_azf3328_get_mixer, .put = snd_azf3328_put_mixer, \
    .private_value = COMPOSE_MIXER_REG(reg, is_right_chan ? 0 : 8, 0, mask, 1, 0, 0), \
  }
  
  #define AZF3328_MIXER_VOL_SPECIAL(xname, reg, mask, shift, invert) \
  { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
    .info = snd_azf3328_info_mixer, \
    .get = snd_azf3328_get_mixer, .put = snd_azf3328_put_mixer, \
    .private_value = COMPOSE_MIXER_REG(reg, shift, 0, mask, invert, 0, 0), \
  }
  
  #define AZF3328_MIXER_ENUM(xname, reg, enum_c, shift) \
  { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
    .info = snd_azf3328_info_mixer_enum, \
    .get = snd_azf3328_get_mixer_enum, .put = snd_azf3328_put_mixer_enum, \
    .private_value = COMPOSE_MIXER_REG(reg, shift, 0, 0, 0, 0, enum_c), \
  }
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
972
  static int
95de77660   Takashi Iwai   [ALSA] Remove xxx...
973
974
  snd_azf3328_info_mixer(struct snd_kcontrol *kcontrol,
  		       struct snd_ctl_elem_info *uinfo)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
975
  {
95de77660   Takashi Iwai   [ALSA] Remove xxx...
976
  	struct azf3328_mixer_reg reg;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
977
978
979
  
  	snd_azf3328_dbgcallenter();
  	snd_azf3328_mixer_reg_decode(&reg, kcontrol->private_value);
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
980
981
  	uinfo->type = reg.mask == 1 ?
  		SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
982
983
984
985
986
987
  	uinfo->count = reg.stereo + 1;
  	uinfo->value.integer.min = 0;
  	uinfo->value.integer.max = reg.mask;
  	snd_azf3328_dbgcallleave();
  	return 0;
  }
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
988
  static int
95de77660   Takashi Iwai   [ALSA] Remove xxx...
989
990
  snd_azf3328_get_mixer(struct snd_kcontrol *kcontrol,
  		      struct snd_ctl_elem_value *ucontrol)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
991
  {
95de77660   Takashi Iwai   [ALSA] Remove xxx...
992
993
  	struct snd_azf3328 *chip = snd_kcontrol_chip(kcontrol);
  	struct azf3328_mixer_reg reg;
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
994
  	u16 oreg, val;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
995
996
997
  
  	snd_azf3328_dbgcallenter();
  	snd_azf3328_mixer_reg_decode(&reg, kcontrol->private_value);
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
998
  	oreg = snd_azf3328_mixer_inw(chip, reg.reg);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
  	val = (oreg >> reg.lchan_shift) & reg.mask;
  	if (reg.invert)
  		val = reg.mask - val;
  	ucontrol->value.integer.value[0] = val;
  	if (reg.stereo) {
  		val = (oreg >> reg.rchan_shift) & reg.mask;
  		if (reg.invert)
  			val = reg.mask - val;
  		ucontrol->value.integer.value[1] = val;
  	}
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
1009
1010
1011
1012
1013
1014
  	snd_azf3328_dbgmixer("get: %02x is %04x -> vol %02lx|%02lx "
  			     "(shift %02d|%02d, mask %02x, inv. %d, stereo %d)
  ",
  		reg.reg, oreg,
  		ucontrol->value.integer.value[0], ucontrol->value.integer.value[1],
  		reg.lchan_shift, reg.rchan_shift, reg.mask, reg.invert, reg.stereo);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1015
1016
1017
  	snd_azf3328_dbgcallleave();
  	return 0;
  }
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
1018
  static int
95de77660   Takashi Iwai   [ALSA] Remove xxx...
1019
1020
  snd_azf3328_put_mixer(struct snd_kcontrol *kcontrol,
  		      struct snd_ctl_elem_value *ucontrol)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1021
  {
95de77660   Takashi Iwai   [ALSA] Remove xxx...
1022
1023
  	struct snd_azf3328 *chip = snd_kcontrol_chip(kcontrol);
  	struct azf3328_mixer_reg reg;
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
1024
  	u16 oreg, nreg, val;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1025
1026
1027
  
  	snd_azf3328_dbgcallenter();
  	snd_azf3328_mixer_reg_decode(&reg, kcontrol->private_value);
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
1028
  	oreg = snd_azf3328_mixer_inw(chip, reg.reg);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
  	val = ucontrol->value.integer.value[0] & reg.mask;
  	if (reg.invert)
  		val = reg.mask - val;
  	nreg = oreg & ~(reg.mask << reg.lchan_shift);
  	nreg |= (val << reg.lchan_shift);
  	if (reg.stereo) {
  		val = ucontrol->value.integer.value[1] & reg.mask;
  		if (reg.invert)
  			val = reg.mask - val;
  		nreg &= ~(reg.mask << reg.rchan_shift);
  		nreg |= (val << reg.rchan_shift);
  	}
  	if (reg.mask >= 0x07) /* it's a volume control, so better take care */
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
1042
1043
1044
1045
1046
  		snd_azf3328_mixer_write_volume_gradually(
  			chip, reg.reg, nreg >> 8, nreg & 0xff,
  			/* just set both channels, doesn't matter */
  			SET_CHAN_LEFT|SET_CHAN_RIGHT,
  			0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1047
  	else
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
1048
          	snd_azf3328_mixer_outw(chip, reg.reg, nreg);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1049

d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
1050
1051
1052
1053
1054
1055
  	snd_azf3328_dbgmixer("put: %02x to %02lx|%02lx, "
  			     "oreg %04x; shift %02d|%02d -> nreg %04x; after: %04x
  ",
  		reg.reg, ucontrol->value.integer.value[0], ucontrol->value.integer.value[1],
  		oreg, reg.lchan_shift, reg.rchan_shift,
  		nreg, snd_azf3328_mixer_inw(chip, reg.reg));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1056
1057
1058
  	snd_azf3328_dbgcallleave();
  	return (nreg != oreg);
  }
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
1059
  static int
95de77660   Takashi Iwai   [ALSA] Remove xxx...
1060
1061
  snd_azf3328_info_mixer_enum(struct snd_kcontrol *kcontrol,
  			    struct snd_ctl_elem_info *uinfo)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1062
  {
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
1063
  	static const char * const texts1[] = {
13769e3f2   Andreas Mohr   [ALSA] azt3328.c:...
1064
  		"Mic1", "Mic2"
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
1065
1066
  	};
  	static const char * const texts2[] = {
13769e3f2   Andreas Mohr   [ALSA] azt3328.c:...
1067
  		"Mix", "Mic"
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
1068
1069
  	};
  	static const char * const texts3[] = {
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
1070
  		"Mic", "CD", "Video", "Aux",
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
1071
  		"Line", "Mix", "Mix Mono", "Phone"
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1072
          };
13769e3f2   Andreas Mohr   [ALSA] azt3328.c:...
1073
1074
1075
  	static const char * const texts4[] = {
  		"pre 3D", "post 3D"
          };
95de77660   Takashi Iwai   [ALSA] Remove xxx...
1076
  	struct azf3328_mixer_reg reg;
627d3e7ab   Andreas Mohr   ALSA: PCI168 snd-...
1077
  	const char * const *p = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1078
1079
1080
1081
1082
1083
1084
  
  	snd_azf3328_mixer_reg_decode(&reg, kcontrol->private_value);
          uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
          uinfo->count = (reg.reg == IDX_MIXER_REC_SELECT) ? 2 : 1;
          uinfo->value.enumerated.items = reg.enum_c;
          if (uinfo->value.enumerated.item > reg.enum_c - 1U)
                  uinfo->value.enumerated.item = reg.enum_c - 1U;
e2f872608   Andreas Mohr   [ALSA] azt3328.c:...
1085
  	if (reg.reg == IDX_MIXER_ADVCTL2) {
13769e3f2   Andreas Mohr   [ALSA] azt3328.c:...
1086
1087
  		switch(reg.lchan_shift) {
  		case 8: /* modem out sel */
627d3e7ab   Andreas Mohr   ALSA: PCI168 snd-...
1088
  			p = texts1;
13769e3f2   Andreas Mohr   [ALSA] azt3328.c:...
1089
1090
  			break;
  		case 9: /* mono sel source */
627d3e7ab   Andreas Mohr   ALSA: PCI168 snd-...
1091
  			p = texts2;
13769e3f2   Andreas Mohr   [ALSA] azt3328.c:...
1092
1093
  			break;
  		case 15: /* PCM Out Path */
627d3e7ab   Andreas Mohr   ALSA: PCI168 snd-...
1094
  			p = texts4;
13769e3f2   Andreas Mohr   [ALSA] azt3328.c:...
1095
1096
  			break;
  		}
e2f872608   Andreas Mohr   [ALSA] azt3328.c:...
1097
  	} else
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
1098
  	if (reg.reg == IDX_MIXER_REC_SELECT)
627d3e7ab   Andreas Mohr   ALSA: PCI168 snd-...
1099
  		p = texts3;
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
1100

627d3e7ab   Andreas Mohr   ALSA: PCI168 snd-...
1101
  	strcpy(uinfo->value.enumerated.name, p[uinfo->value.enumerated.item]);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1102
1103
          return 0;
  }
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
1104
  static int
95de77660   Takashi Iwai   [ALSA] Remove xxx...
1105
1106
  snd_azf3328_get_mixer_enum(struct snd_kcontrol *kcontrol,
  			   struct snd_ctl_elem_value *ucontrol)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1107
  {
95de77660   Takashi Iwai   [ALSA] Remove xxx...
1108
1109
          struct snd_azf3328 *chip = snd_kcontrol_chip(kcontrol);
  	struct azf3328_mixer_reg reg;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1110
          unsigned short val;
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
1111

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1112
  	snd_azf3328_mixer_reg_decode(&reg, kcontrol->private_value);
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
1113
  	val = snd_azf3328_mixer_inw(chip, reg.reg);
e2f872608   Andreas Mohr   [ALSA] azt3328.c:...
1114
  	if (reg.reg == IDX_MIXER_REC_SELECT) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1115
1116
          	ucontrol->value.enumerated.item[0] = (val >> 8) & (reg.enum_c - 1);
          	ucontrol->value.enumerated.item[1] = (val >> 0) & (reg.enum_c - 1);
e2f872608   Andreas Mohr   [ALSA] azt3328.c:...
1117
  	} else
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1118
          	ucontrol->value.enumerated.item[0] = (val >> reg.lchan_shift) & (reg.enum_c - 1);
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
1119
1120
1121
1122
1123
  
  	snd_azf3328_dbgmixer("get_enum: %02x is %04x -> %d|%d (shift %02d, enum_c %d)
  ",
  		reg.reg, val, ucontrol->value.enumerated.item[0], ucontrol->value.enumerated.item[1],
  		reg.lchan_shift, reg.enum_c);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1124
1125
          return 0;
  }
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
1126
  static int
95de77660   Takashi Iwai   [ALSA] Remove xxx...
1127
1128
  snd_azf3328_put_mixer_enum(struct snd_kcontrol *kcontrol,
  			   struct snd_ctl_elem_value *ucontrol)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1129
  {
95de77660   Takashi Iwai   [ALSA] Remove xxx...
1130
1131
          struct snd_azf3328 *chip = snd_kcontrol_chip(kcontrol);
  	struct azf3328_mixer_reg reg;
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
1132
  	u16 oreg, nreg, val;
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
1133

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1134
  	snd_azf3328_mixer_reg_decode(&reg, kcontrol->private_value);
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
1135
  	oreg = snd_azf3328_mixer_inw(chip, reg.reg);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1136
  	val = oreg;
e2f872608   Andreas Mohr   [ALSA] azt3328.c:...
1137
  	if (reg.reg == IDX_MIXER_REC_SELECT) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1138
1139
1140
1141
1142
          	if (ucontrol->value.enumerated.item[0] > reg.enum_c - 1U ||
              	ucontrol->value.enumerated.item[1] > reg.enum_c - 1U)
                  	return -EINVAL;
          	val = (ucontrol->value.enumerated.item[0] << 8) |
          	      (ucontrol->value.enumerated.item[1] << 0);
e2f872608   Andreas Mohr   [ALSA] azt3328.c:...
1143
  	} else {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1144
1145
1146
1147
1148
          	if (ucontrol->value.enumerated.item[0] > reg.enum_c - 1U)
                  	return -EINVAL;
  		val &= ~((reg.enum_c - 1) << reg.lchan_shift);
          	val |= (ucontrol->value.enumerated.item[0] << reg.lchan_shift);
  	}
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
1149
  	snd_azf3328_mixer_outw(chip, reg.reg, val);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1150
1151
1152
1153
1154
1155
  	nreg = val;
  
  	snd_azf3328_dbgmixer("put_enum: %02x to %04x, oreg %04x
  ", reg.reg, val, oreg);
  	return (nreg != oreg);
  }
1b60f6b09   Takashi Iwai   [ALSA] Fix confli...
1156
  static struct snd_kcontrol_new snd_azf3328_mixer_controls[] __devinitdata = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1157
1158
  	AZF3328_MIXER_SWITCH("Master Playback Switch", IDX_MIXER_PLAY_MASTER, 15, 1),
  	AZF3328_MIXER_VOL_STEREO("Master Playback Volume", IDX_MIXER_PLAY_MASTER, 0x1f, 1),
627d3e7ab   Andreas Mohr   ALSA: PCI168 snd-...
1159
1160
1161
1162
1163
  	AZF3328_MIXER_SWITCH("PCM Playback Switch", IDX_MIXER_WAVEOUT, 15, 1),
  	AZF3328_MIXER_VOL_STEREO("PCM Playback Volume",
  					IDX_MIXER_WAVEOUT, 0x1f, 1),
  	AZF3328_MIXER_SWITCH("PCM 3D Bypass Playback Switch",
  					IDX_MIXER_ADVCTL2, 7, 1),
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
  	AZF3328_MIXER_SWITCH("FM Playback Switch", IDX_MIXER_FMSYNTH, 15, 1),
  	AZF3328_MIXER_VOL_STEREO("FM Playback Volume", IDX_MIXER_FMSYNTH, 0x1f, 1),
  	AZF3328_MIXER_SWITCH("CD Playback Switch", IDX_MIXER_CDAUDIO, 15, 1),
  	AZF3328_MIXER_VOL_STEREO("CD Playback Volume", IDX_MIXER_CDAUDIO, 0x1f, 1),
  	AZF3328_MIXER_SWITCH("Capture Switch", IDX_MIXER_REC_VOLUME, 15, 1),
  	AZF3328_MIXER_VOL_STEREO("Capture Volume", IDX_MIXER_REC_VOLUME, 0x0f, 0),
  	AZF3328_MIXER_ENUM("Capture Source", IDX_MIXER_REC_SELECT, 8, 0),
  	AZF3328_MIXER_SWITCH("Mic Playback Switch", IDX_MIXER_MIC, 15, 1),
  	AZF3328_MIXER_VOL_MONO("Mic Playback Volume", IDX_MIXER_MIC, 0x1f, 1),
  	AZF3328_MIXER_SWITCH("Mic Boost (+20dB)", IDX_MIXER_MIC, 6, 0),
  	AZF3328_MIXER_SWITCH("Line Playback Switch", IDX_MIXER_LINEIN, 15, 1),
  	AZF3328_MIXER_VOL_STEREO("Line Playback Volume", IDX_MIXER_LINEIN, 0x1f, 1),
d355c82a0   Jaroslav Kysela   ALSA: rename "PC ...
1176
1177
  	AZF3328_MIXER_SWITCH("Beep Playback Switch", IDX_MIXER_PCBEEP, 15, 1),
  	AZF3328_MIXER_VOL_SPECIAL("Beep Playback Volume", IDX_MIXER_PCBEEP, 0x0f, 1, 1),
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1178
1179
1180
1181
1182
1183
1184
1185
  	AZF3328_MIXER_SWITCH("Video Playback Switch", IDX_MIXER_VIDEO, 15, 1),
  	AZF3328_MIXER_VOL_STEREO("Video Playback Volume", IDX_MIXER_VIDEO, 0x1f, 1),
  	AZF3328_MIXER_SWITCH("Aux Playback Switch", IDX_MIXER_AUX, 15, 1),
  	AZF3328_MIXER_VOL_STEREO("Aux Playback Volume", IDX_MIXER_AUX, 0x1f, 1),
  	AZF3328_MIXER_SWITCH("Modem Playback Switch", IDX_MIXER_MODEMOUT, 15, 1),
  	AZF3328_MIXER_VOL_MONO("Modem Playback Volume", IDX_MIXER_MODEMOUT, 0x1f, 1),
  	AZF3328_MIXER_SWITCH("Modem Capture Switch", IDX_MIXER_MODEMIN, 15, 1),
  	AZF3328_MIXER_VOL_MONO("Modem Capture Volume", IDX_MIXER_MODEMIN, 0x1f, 1),
13769e3f2   Andreas Mohr   [ALSA] azt3328.c:...
1186
1187
  	AZF3328_MIXER_ENUM("Mic Select", IDX_MIXER_ADVCTL2, 2, 8),
  	AZF3328_MIXER_ENUM("Mono Output Select", IDX_MIXER_ADVCTL2, 2, 9),
e24a121aa   Andreas Mohr   [ALSA] azt3328.c:...
1188
  	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! */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1189
1190
  	AZF3328_MIXER_VOL_SPECIAL("Tone Control - Treble", IDX_MIXER_BASSTREBLE, 0x07, 1, 0),
  	AZF3328_MIXER_VOL_SPECIAL("Tone Control - Bass", IDX_MIXER_BASSTREBLE, 0x07, 9, 0),
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
1191
  	AZF3328_MIXER_SWITCH("3D Control - Switch", IDX_MIXER_ADVCTL2, 13, 0),
13769e3f2   Andreas Mohr   [ALSA] azt3328.c:...
1192
1193
  	AZF3328_MIXER_VOL_SPECIAL("3D Control - Width", IDX_MIXER_ADVCTL1, 0x07, 1, 0), /* "3D Width" */
  	AZF3328_MIXER_VOL_SPECIAL("3D Control - Depth", IDX_MIXER_ADVCTL1, 0x03, 8, 0), /* "Hifi 3D" */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
  #if MIXER_TESTING
  	AZF3328_MIXER_SWITCH("0", IDX_MIXER_ADVCTL2, 0, 0),
  	AZF3328_MIXER_SWITCH("1", IDX_MIXER_ADVCTL2, 1, 0),
  	AZF3328_MIXER_SWITCH("2", IDX_MIXER_ADVCTL2, 2, 0),
  	AZF3328_MIXER_SWITCH("3", IDX_MIXER_ADVCTL2, 3, 0),
  	AZF3328_MIXER_SWITCH("4", IDX_MIXER_ADVCTL2, 4, 0),
  	AZF3328_MIXER_SWITCH("5", IDX_MIXER_ADVCTL2, 5, 0),
  	AZF3328_MIXER_SWITCH("6", IDX_MIXER_ADVCTL2, 6, 0),
  	AZF3328_MIXER_SWITCH("7", IDX_MIXER_ADVCTL2, 7, 0),
  	AZF3328_MIXER_SWITCH("8", IDX_MIXER_ADVCTL2, 8, 0),
  	AZF3328_MIXER_SWITCH("9", IDX_MIXER_ADVCTL2, 9, 0),
  	AZF3328_MIXER_SWITCH("10", IDX_MIXER_ADVCTL2, 10, 0),
  	AZF3328_MIXER_SWITCH("11", IDX_MIXER_ADVCTL2, 11, 0),
  	AZF3328_MIXER_SWITCH("12", IDX_MIXER_ADVCTL2, 12, 0),
  	AZF3328_MIXER_SWITCH("13", IDX_MIXER_ADVCTL2, 13, 0),
  	AZF3328_MIXER_SWITCH("14", IDX_MIXER_ADVCTL2, 14, 0),
  	AZF3328_MIXER_SWITCH("15", IDX_MIXER_ADVCTL2, 15, 0),
  #endif
  };
1b60f6b09   Takashi Iwai   [ALSA] Fix confli...
1213
  static u16 __devinitdata snd_azf3328_init_values[][2] = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
          { IDX_MIXER_PLAY_MASTER,	MIXER_MUTE_MASK|0x1f1f },
          { IDX_MIXER_MODEMOUT,		MIXER_MUTE_MASK|0x1f1f },
  	{ IDX_MIXER_BASSTREBLE,		0x0000 },
  	{ IDX_MIXER_PCBEEP,		MIXER_MUTE_MASK|0x1f1f },
  	{ IDX_MIXER_MODEMIN,		MIXER_MUTE_MASK|0x1f1f },
  	{ IDX_MIXER_MIC,		MIXER_MUTE_MASK|0x001f },
  	{ IDX_MIXER_LINEIN,		MIXER_MUTE_MASK|0x1f1f },
  	{ IDX_MIXER_CDAUDIO,		MIXER_MUTE_MASK|0x1f1f },
  	{ IDX_MIXER_VIDEO,		MIXER_MUTE_MASK|0x1f1f },
  	{ IDX_MIXER_AUX,		MIXER_MUTE_MASK|0x1f1f },
          { IDX_MIXER_WAVEOUT,		MIXER_MUTE_MASK|0x1f1f },
          { IDX_MIXER_FMSYNTH,		MIXER_MUTE_MASK|0x1f1f },
          { IDX_MIXER_REC_VOLUME,		MIXER_MUTE_MASK|0x0707 },
  };
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
1228
  static int __devinit
95de77660   Takashi Iwai   [ALSA] Remove xxx...
1229
  snd_azf3328_mixer_new(struct snd_azf3328 *chip)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1230
  {
95de77660   Takashi Iwai   [ALSA] Remove xxx...
1231
1232
  	struct snd_card *card;
  	const struct snd_kcontrol_new *sw;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1233
1234
1235
1236
  	unsigned int idx;
  	int err;
  
  	snd_azf3328_dbgcallenter();
da3cec35d   Takashi Iwai   ALSA: Kill snd_as...
1237
1238
  	if (snd_BUG_ON(!chip || !chip->card))
  		return -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1239
1240
1241
1242
  
  	card = chip->card;
  
  	/* mixer reset */
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
1243
  	snd_azf3328_mixer_outw(chip, IDX_MIXER_RESET, 0x0000);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1244
1245
  
  	/* mute and zero volume channels */
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
1246
  	for (idx = 0; idx < ARRAY_SIZE(snd_azf3328_init_values); ++idx) {
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
1247
1248
1249
  		snd_azf3328_mixer_outw(chip,
  			snd_azf3328_init_values[idx][0],
  			snd_azf3328_init_values[idx][1]);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1250
  	}
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
1251

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1252
1253
  	/* add mixer controls */
  	sw = snd_azf3328_mixer_controls;
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
1254
1255
  	for (idx = 0; idx < ARRAY_SIZE(snd_azf3328_mixer_controls);
  			++idx, ++sw) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1256
1257
1258
1259
1260
1261
1262
1263
1264
  		if ((err = snd_ctl_add(chip->card, snd_ctl_new1(sw, chip))) < 0)
  			return err;
  	}
  	snd_component_add(card, "AZF3328 mixer");
  	strcpy(card->mixername, "AZF3328 mixer");
  
  	snd_azf3328_dbgcallleave();
  	return 0;
  }
b5dc20cd2   Andreas Mohr   ALSA: azt3328: ad...
1265
  #endif /* AZF_USE_AC97_LAYER */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1266

d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
1267
  static int
95de77660   Takashi Iwai   [ALSA] Remove xxx...
1268
1269
  snd_azf3328_hw_params(struct snd_pcm_substream *substream,
  				 struct snd_pcm_hw_params *hw_params)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1270
1271
1272
1273
1274
1275
1276
  {
  	int res;
  	snd_azf3328_dbgcallenter();
  	res = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
  	snd_azf3328_dbgcallleave();
  	return res;
  }
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
1277
  static int
95de77660   Takashi Iwai   [ALSA] Remove xxx...
1278
  snd_azf3328_hw_free(struct snd_pcm_substream *substream)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1279
1280
1281
1282
1283
1284
  {
  	snd_azf3328_dbgcallenter();
  	snd_pcm_lib_free_pages(substream);
  	snd_azf3328_dbgcallleave();
  	return 0;
  }
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
1285
  static void
da237f35a   Andreas Mohr   ALSA: azt3328: us...
1286
  snd_azf3328_codec_setfmt(struct snd_azf3328_codec_data *codec,
627d3e7ab   Andreas Mohr   ALSA: PCI168 snd-...
1287
  			       enum azf_freq_t bitrate,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1288
1289
1290
1291
  			       unsigned int format_width,
  			       unsigned int channels
  )
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1292
  	unsigned long flags;
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
1293
  	u16 val = 0xff00;
8d9a114e6   Andreas Mohr   ALSA: azt3328: _s...
1294
  	u8 freq = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1295
1296
1297
  
  	snd_azf3328_dbgcallenter();
  	switch (bitrate) {
c9ba374d2   Andreas Mohr   ALSA: azt3328 - ...
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
  	case AZF_FREQ_4000:  freq = SOUNDFORMAT_FREQ_SUSPECTED_4000; break;
  	case AZF_FREQ_4800:  freq = SOUNDFORMAT_FREQ_SUSPECTED_4800; break;
  	case AZF_FREQ_5512:
  		/* the AZF3328 names it "5510" for some strange reason */
  			     freq = SOUNDFORMAT_FREQ_5510; break;
  	case AZF_FREQ_6620:  freq = SOUNDFORMAT_FREQ_6620; break;
  	case AZF_FREQ_8000:  freq = SOUNDFORMAT_FREQ_8000; break;
  	case AZF_FREQ_9600:  freq = SOUNDFORMAT_FREQ_9600; break;
  	case AZF_FREQ_11025: freq = SOUNDFORMAT_FREQ_11025; break;
  	case AZF_FREQ_13240: freq = SOUNDFORMAT_FREQ_SUSPECTED_13240; break;
  	case AZF_FREQ_16000: freq = SOUNDFORMAT_FREQ_16000; break;
  	case AZF_FREQ_22050: freq = SOUNDFORMAT_FREQ_22050; break;
  	case AZF_FREQ_32000: freq = SOUNDFORMAT_FREQ_32000; break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1311
  	default:
99b359ba1   Takashi Iwai   [ALSA] Add missin...
1312
1313
  		snd_printk(KERN_WARNING "unknown bitrate %d, assuming 44.1kHz!
  ", bitrate);
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
1314
  		/* fall-through */
c9ba374d2   Andreas Mohr   ALSA: azt3328 - ...
1315
1316
1317
  	case AZF_FREQ_44100: freq = SOUNDFORMAT_FREQ_44100; break;
  	case AZF_FREQ_48000: freq = SOUNDFORMAT_FREQ_48000; break;
  	case AZF_FREQ_66200: freq = SOUNDFORMAT_FREQ_SUSPECTED_66200; break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1318
  	}
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
1319
1320
1321
1322
  	/* val = 0xff07; 3m27.993s (65301Hz; -> 64000Hz???) hmm, 66120, 65967, 66123 */
  	/* val = 0xff09; 17m15.098s (13123,478Hz; -> 12000Hz???) hmm, 13237.2Hz? */
  	/* val = 0xff0a; 47m30.599s (4764,891Hz; -> 4800Hz???) yup, 4803Hz */
  	/* val = 0xff0c; 57m0.510s (4010,263Hz; -> 4000Hz???) yup, 4003Hz */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1323
1324
1325
1326
1327
  	/* val = 0xff05; 5m11.556s (... -> 44100Hz) */
  	/* val = 0xff03; 10m21.529s (21872,463Hz; -> 22050Hz???) */
  	/* val = 0xff0f; 20m41.883s (10937,993Hz; -> 11025Hz???) */
  	/* val = 0xff0d; 41m23.135s (5523,600Hz; -> 5512Hz???) */
  	/* val = 0xff0e; 28m30.777s (8017Hz; -> 8000Hz???) */
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
1328

8d9a114e6   Andreas Mohr   ALSA: azt3328: _s...
1329
  	val |= freq;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1330
1331
1332
1333
1334
  	if (channels == 2)
  		val |= SOUNDFORMAT_FLAG_2CHANNELS;
  
  	if (format_width == 16)
  		val |= SOUNDFORMAT_FLAG_16BIT;
da237f35a   Andreas Mohr   ALSA: azt3328: us...
1335
  	spin_lock_irqsave(codec->lock, flags);
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
1336

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1337
  	/* set bitrate/format */
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
1338
  	snd_azf3328_codec_outw(codec, IDX_IO_CODEC_SOUNDFORMAT, val);
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
1339

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1340
1341
1342
1343
1344
1345
1346
  	/* changing the bitrate/format settings switches off the
  	 * audio output with an annoying click in case of 8/16bit format change
  	 * (maybe shutting down DAC/ADC?), thus immediately
  	 * do some tweaking to reenable it and get rid of the clicking
  	 * (FIXME: yes, it works, but what exactly am I doing here?? :)
  	 * FIXME: does this have some side effects for full-duplex
  	 * or other dramatic side effects? */
adf5931f8   Andreas Mohr   ALSA: azt3328: co...
1347
  	/* do it for non-capture codecs only */
da237f35a   Andreas Mohr   ALSA: azt3328: us...
1348
  	if (codec->type != AZF_CODEC_CAPTURE)
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
1349
1350
1351
1352
  		snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS,
  			snd_azf3328_codec_inw(codec, IDX_IO_CODEC_DMA_FLAGS) |
  			DMA_RUN_SOMETHING1 |
  			DMA_RUN_SOMETHING2 |
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
1353
1354
1355
1356
  			SOMETHING_ALMOST_ALWAYS_SET |
  			DMA_EPILOGUE_SOMETHING |
  			DMA_SOMETHING_ELSE
  		);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1357

da237f35a   Andreas Mohr   ALSA: azt3328: us...
1358
  	spin_unlock_irqrestore(codec->lock, flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1359
1360
  	snd_azf3328_dbgcallleave();
  }
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
1361
  static inline void
da237f35a   Andreas Mohr   ALSA: azt3328: us...
1362
  snd_azf3328_codec_setfmt_lowpower(struct snd_azf3328_codec_data *codec
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
1363
1364
1365
1366
1367
1368
  )
  {
  	/* choose lowest frequency for low power consumption.
  	 * While this will cause louder noise due to rather coarse frequency,
  	 * it should never matter since output should always
  	 * get disabled properly when idle anyway. */
da237f35a   Andreas Mohr   ALSA: azt3328: us...
1369
  	snd_azf3328_codec_setfmt(codec, AZF_FREQ_4000, 8, 1);
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
1370
  }
627d3e7ab   Andreas Mohr   ALSA: PCI168 snd-...
1371
  static void
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
1372
  snd_azf3328_ctrl_reg_6AH_update(struct snd_azf3328 *chip,
627d3e7ab   Andreas Mohr   ALSA: PCI168 snd-...
1373
  					unsigned bitmask,
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
1374
  					bool enable
627d3e7ab   Andreas Mohr   ALSA: PCI168 snd-...
1375
1376
  )
  {
78df617ac   Andreas Mohr   ALSA: azt3328: fi...
1377
1378
  	bool do_mask = !enable;
  	if (do_mask)
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
1379
  		chip->shadow_reg_ctrl_6AH |= bitmask;
78df617ac   Andreas Mohr   ALSA: azt3328: fi...
1380
1381
1382
1383
1384
  	else
  		chip->shadow_reg_ctrl_6AH &= ~bitmask;
  	snd_azf3328_dbgcodec("6AH_update mask 0x%04x do_mask %d: val 0x%04x
  ",
  			bitmask, do_mask, chip->shadow_reg_ctrl_6AH);
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
1385
  	snd_azf3328_ctrl_outw(chip, IDX_IO_6AH, chip->shadow_reg_ctrl_6AH);
627d3e7ab   Andreas Mohr   ALSA: PCI168 snd-...
1386
  }
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
1387
  static inline void
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
1388
  snd_azf3328_ctrl_enable_codecs(struct snd_azf3328 *chip, bool enable)
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
1389
  {
78df617ac   Andreas Mohr   ALSA: azt3328: fi...
1390
1391
  	snd_azf3328_dbgcodec("codec_enable %d
  ", enable);
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
1392
1393
  	/* no idea what exactly is being done here, but I strongly assume it's
  	 * PM related */
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
1394
  	snd_azf3328_ctrl_reg_6AH_update(
627d3e7ab   Andreas Mohr   ALSA: PCI168 snd-...
1395
  		chip, IO_6A_PAUSE_PLAYBACK_BIT8, enable
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
1396
1397
1398
1399
  	);
  }
  
  static void
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
1400
1401
1402
  snd_azf3328_ctrl_codec_activity(struct snd_azf3328 *chip,
  				enum snd_azf3328_codec_type codec_type,
  				bool enable
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
1403
1404
  )
  {
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
1405
1406
  	struct snd_azf3328_codec_data *codec = &chip->codecs[codec_type];
  	bool need_change = (codec->running != enable);
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
1407

78df617ac   Andreas Mohr   ALSA: azt3328: fi...
1408
  	snd_azf3328_dbgcodec(
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
1409
1410
1411
  		"codec_activity: %s codec, enable %d, need_change %d
  ",
  				codec->name, enable, need_change
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
1412
1413
  	);
  	if (need_change) {
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
  		static const struct {
  			enum snd_azf3328_codec_type other1;
  			enum snd_azf3328_codec_type other2;
  		} peer_codecs[3] =
  			{ { AZF_CODEC_CAPTURE, AZF_CODEC_I2S_OUT },
  			  { AZF_CODEC_PLAYBACK, AZF_CODEC_I2S_OUT },
  			  { AZF_CODEC_PLAYBACK, AZF_CODEC_CAPTURE } };
  		bool call_function;
  
  		if (enable)
  			/* if enable codec, call enable_codecs func
  			   to enable codec supply... */
  			call_function = 1;
  		else {
  			/* ...otherwise call enable_codecs func
  			   (which globally shuts down operation of codecs)
  			   only in case the other codecs are currently
  			   not active either! */
78df617ac   Andreas Mohr   ALSA: azt3328: fi...
1432
1433
1434
1435
1436
  			call_function =
  				((!chip->codecs[peer_codecs[codec_type].other1]
  					.running)
  			     &&  (!chip->codecs[peer_codecs[codec_type].other2]
  					.running));
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
1437
1438
1439
  		 }
  		 if (call_function)
  			snd_azf3328_ctrl_enable_codecs(chip, enable);
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
1440
1441
1442
1443
  
  		/* ...and adjust clock, too
  		 * (reduce noise and power consumption) */
  		if (!enable)
da237f35a   Andreas Mohr   ALSA: azt3328: us...
1444
  			snd_azf3328_codec_setfmt_lowpower(codec);
78df617ac   Andreas Mohr   ALSA: azt3328: fi...
1445
  		codec->running = enable;
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
1446
  	}
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
1447
  }
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
1448
  static void
da237f35a   Andreas Mohr   ALSA: azt3328: us...
1449
  snd_azf3328_codec_setdmaa(struct snd_azf3328_codec_data *codec,
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
1450
  				unsigned long addr,
689c69120   Andreas Mohr   ALSA: azt3328: im...
1451
1452
  				unsigned int period_bytes,
  				unsigned int buffer_bytes
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
1453
  )
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1454
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1455
  	snd_azf3328_dbgcallenter();
689c69120   Andreas Mohr   ALSA: azt3328: im...
1456
1457
1458
1459
1460
1461
  	WARN_ONCE(period_bytes & 1, "odd period length!?
  ");
  	WARN_ONCE(buffer_bytes != 2 * period_bytes,
  		 "missed our input expectations! %u vs. %u
  ",
  		 buffer_bytes, period_bytes);
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
1462
1463
  	if (!codec->running) {
  		/* AZF3328 uses a two buffer pointer DMA transfer approach */
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
1464

689c69120   Andreas Mohr   ALSA: azt3328: im...
1465
  		unsigned long flags;
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
1466
1467
  
  		/* width 32bit (prevent overflow): */
689c69120   Andreas Mohr   ALSA: azt3328: im...
1468
1469
1470
1471
1472
1473
  		u32 area_length;
  		struct codec_setup_io {
  			u32 dma_start_1;
  			u32 dma_start_2;
  			u32 dma_lengths;
  		} __attribute__((packed)) setup_io;
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
1474

689c69120   Andreas Mohr   ALSA: azt3328: im...
1475
  		area_length = buffer_bytes/2;
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
1476

689c69120   Andreas Mohr   ALSA: azt3328: im...
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
  		setup_io.dma_start_1 = addr;
  		setup_io.dma_start_2 = addr+area_length;
  
  		snd_azf3328_dbgcodec(
  			"setdma: buffers %08x[%u] / %08x[%u], %u, %u
  ",
  				setup_io.dma_start_1, area_length,
  				setup_io.dma_start_2, area_length,
  				period_bytes, buffer_bytes);
  
  		/* Hmm, are we really supposed to decrement this by 1??
  		   Most definitely certainly not: configuring full length does
  		   work properly (i.e. likely better), and BTW we
  		   violated possibly differing frame sizes with this...
  
  		area_length--; |* max. index *|
  		*/
7974150c8   Andreas Mohr   ALSA: azt3328: pe...
1494

d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
1495
  		/* build combined I/O buffer length word */
689c69120   Andreas Mohr   ALSA: azt3328: im...
1496
  		setup_io.dma_lengths = (area_length << 16) | (area_length);
da237f35a   Andreas Mohr   ALSA: azt3328: us...
1497
  		spin_lock_irqsave(codec->lock, flags);
689c69120   Andreas Mohr   ALSA: azt3328: im...
1498
1499
1500
  		snd_azf3328_codec_outl_multi(
  			codec, IDX_IO_CODEC_DMA_START_1, &setup_io, 3
  		);
da237f35a   Andreas Mohr   ALSA: azt3328: us...
1501
  		spin_unlock_irqrestore(codec->lock, flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1502
1503
1504
  	}
  	snd_azf3328_dbgcallleave();
  }
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
1505
  static int
da237f35a   Andreas Mohr   ALSA: azt3328: us...
1506
  snd_azf3328_pcm_prepare(struct snd_pcm_substream *substream)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1507
  {
95de77660   Takashi Iwai   [ALSA] Remove xxx...
1508
  	struct snd_pcm_runtime *runtime = substream->runtime;
da237f35a   Andreas Mohr   ALSA: azt3328: us...
1509
  	struct snd_azf3328_codec_data *codec = runtime->private_data;
345855951   Andreas Mohr   ALSA: azt3328: us...
1510
  #if 0
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1511
1512
1513
1514
1515
          unsigned int size = snd_pcm_lib_buffer_bytes(substream);
  	unsigned int count = snd_pcm_lib_period_bytes(substream);
  #endif
  
  	snd_azf3328_dbgcallenter();
345855951   Andreas Mohr   ALSA: azt3328: us...
1516
1517
  
  	codec->dma_base = runtime->dma_addr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1518
  #if 0
da237f35a   Andreas Mohr   ALSA: azt3328: us...
1519
  	snd_azf3328_codec_setfmt(codec,
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
1520
1521
1522
  		runtime->rate,
  		snd_pcm_format_width(runtime->format),
  		runtime->channels);
da237f35a   Andreas Mohr   ALSA: azt3328: us...
1523
  	snd_azf3328_codec_setdmaa(codec,
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
1524
  					runtime->dma_addr, count, size);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1525
1526
1527
1528
  #endif
  	snd_azf3328_dbgcallleave();
  	return 0;
  }
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
1529
  static int
da237f35a   Andreas Mohr   ALSA: azt3328: us...
1530
  snd_azf3328_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1531
  {
95de77660   Takashi Iwai   [ALSA] Remove xxx...
1532
1533
  	struct snd_azf3328 *chip = snd_pcm_substream_chip(substream);
  	struct snd_pcm_runtime *runtime = substream->runtime;
da237f35a   Andreas Mohr   ALSA: azt3328: us...
1534
  	struct snd_azf3328_codec_data *codec = runtime->private_data;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1535
  	int result = 0;
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
1536
1537
  	u16 flags1;
  	bool previously_muted = 0;
da237f35a   Andreas Mohr   ALSA: azt3328: us...
1538
  	bool is_main_mixer_playback_codec = (AZF_CODEC_PLAYBACK == codec->type);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1539

da237f35a   Andreas Mohr   ALSA: azt3328: us...
1540
1541
  	snd_azf3328_dbgcalls("snd_azf3328_pcm_trigger cmd %d
  ", cmd);
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
1542

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1543
1544
  	switch (cmd) {
  	case SNDRV_PCM_TRIGGER_START:
78df617ac   Andreas Mohr   ALSA: azt3328: fi...
1545
1546
  		snd_azf3328_dbgcodec("START %s
  ", codec->name);
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
1547

da237f35a   Andreas Mohr   ALSA: azt3328: us...
1548
  		if (is_main_mixer_playback_codec) {
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
1549
1550
  			/* mute WaveOut (avoid clicking during setup) */
  			previously_muted =
b5dc20cd2   Andreas Mohr   ALSA: azt3328: ad...
1551
1552
  				snd_azf3328_mixer_mute_control_pcm(
  						chip, 1
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
1553
1554
  				);
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1555

da237f35a   Andreas Mohr   ALSA: azt3328: us...
1556
  		snd_azf3328_codec_setfmt(codec,
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
1557
1558
1559
  			runtime->rate,
  			snd_pcm_format_width(runtime->format),
  			runtime->channels);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1560

da237f35a   Andreas Mohr   ALSA: azt3328: us...
1561
  		spin_lock(codec->lock);
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
1562
  		/* first, remember current value: */
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
1563
  		flags1 = snd_azf3328_codec_inw(codec, IDX_IO_CODEC_DMA_FLAGS);
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
1564

dfbf95111   Andreas Mohr   ALSA: azt3328: la...
1565
1566
1567
  		/* stop transfer */
  		flags1 &= ~DMA_RESUME;
  		snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, flags1);
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
1568

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1569
  		/* FIXME: clear interrupts or what??? */
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
1570
  		snd_azf3328_codec_outw(codec, IDX_IO_CODEC_IRQTYPE, 0xffff);
da237f35a   Andreas Mohr   ALSA: azt3328: us...
1571
  		spin_unlock(codec->lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1572

da237f35a   Andreas Mohr   ALSA: azt3328: us...
1573
  		snd_azf3328_codec_setdmaa(codec, runtime->dma_addr,
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
1574
  			snd_pcm_lib_period_bytes(substream),
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
1575
1576
  			snd_pcm_lib_buffer_bytes(substream)
  		);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1577

da237f35a   Andreas Mohr   ALSA: azt3328: us...
1578
  		spin_lock(codec->lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1579
1580
  #ifdef WIN9X
  		/* FIXME: enable playback/recording??? */
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
1581
1582
  		flags1 |= DMA_RUN_SOMETHING1 | DMA_RUN_SOMETHING2;
  		snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, flags1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1583

dfbf95111   Andreas Mohr   ALSA: azt3328: la...
1584
  		/* start transfer again */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1585
  		/* FIXME: what is this value (0x0010)??? */
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
1586
1587
  		flags1 |= DMA_RESUME | DMA_EPILOGUE_SOMETHING;
  		snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, flags1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1588
  #else /* NT4 */
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
1589
  		snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS,
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
1590
  			0x0000);
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
1591
1592
1593
1594
1595
1596
  		snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS,
  			DMA_RUN_SOMETHING1);
  		snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS,
  			DMA_RUN_SOMETHING1 |
  			DMA_RUN_SOMETHING2);
  		snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS,
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
1597
1598
1599
1600
  			DMA_RESUME |
  			SOMETHING_ALMOST_ALWAYS_SET |
  			DMA_EPILOGUE_SOMETHING |
  			DMA_SOMETHING_ELSE);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1601
  #endif
da237f35a   Andreas Mohr   ALSA: azt3328: us...
1602
1603
  		spin_unlock(codec->lock);
  		snd_azf3328_ctrl_codec_activity(chip, codec->type, 1);
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
1604

da237f35a   Andreas Mohr   ALSA: azt3328: us...
1605
  		if (is_main_mixer_playback_codec) {
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
1606
1607
  			/* now unmute WaveOut */
  			if (!previously_muted)
b5dc20cd2   Andreas Mohr   ALSA: azt3328: ad...
1608
1609
  				snd_azf3328_mixer_mute_control_pcm(
  						chip, 0
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
1610
1611
  				);
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1612

78df617ac   Andreas Mohr   ALSA: azt3328: fi...
1613
1614
  		snd_azf3328_dbgcodec("STARTED %s
  ", codec->name);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1615
  		break;
ca54bde36   Andreas Mohr   [ALSA] azt3328.c:...
1616
  	case SNDRV_PCM_TRIGGER_RESUME:
78df617ac   Andreas Mohr   ALSA: azt3328: fi...
1617
1618
  		snd_azf3328_dbgcodec("RESUME %s
  ", codec->name);
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
1619
  		/* resume codec if we were active */
da237f35a   Andreas Mohr   ALSA: azt3328: us...
1620
  		spin_lock(codec->lock);
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
1621
1622
1623
1624
1625
1626
  		if (codec->running)
  			snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS,
  				snd_azf3328_codec_inw(
  					codec, IDX_IO_CODEC_DMA_FLAGS
  				) | DMA_RESUME
  			);
da237f35a   Andreas Mohr   ALSA: azt3328: us...
1627
  		spin_unlock(codec->lock);
ca54bde36   Andreas Mohr   [ALSA] azt3328.c:...
1628
  		break;
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
1629
  	case SNDRV_PCM_TRIGGER_STOP:
78df617ac   Andreas Mohr   ALSA: azt3328: fi...
1630
1631
  		snd_azf3328_dbgcodec("STOP %s
  ", codec->name);
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
1632

da237f35a   Andreas Mohr   ALSA: azt3328: us...
1633
  		if (is_main_mixer_playback_codec) {
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
1634
1635
  			/* mute WaveOut (avoid clicking during setup) */
  			previously_muted =
b5dc20cd2   Andreas Mohr   ALSA: azt3328: ad...
1636
1637
  				snd_azf3328_mixer_mute_control_pcm(
  						chip, 1
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
1638
1639
  				);
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1640

da237f35a   Andreas Mohr   ALSA: azt3328: us...
1641
  		spin_lock(codec->lock);
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
1642
  		/* first, remember current value: */
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
1643
  		flags1 = snd_azf3328_codec_inw(codec, IDX_IO_CODEC_DMA_FLAGS);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1644

dfbf95111   Andreas Mohr   ALSA: azt3328: la...
1645
1646
1647
  		/* stop transfer */
  		flags1 &= ~DMA_RESUME;
  		snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, flags1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1648

d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
1649
1650
  		/* hmm, is this really required? we're resetting the same bit
  		 * immediately thereafter... */
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
1651
1652
  		flags1 |= DMA_RUN_SOMETHING1;
  		snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, flags1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1653

dfbf95111   Andreas Mohr   ALSA: azt3328: la...
1654
1655
  		flags1 &= ~DMA_RUN_SOMETHING1;
  		snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, flags1);
da237f35a   Andreas Mohr   ALSA: azt3328: us...
1656
1657
  		spin_unlock(codec->lock);
  		snd_azf3328_ctrl_codec_activity(chip, codec->type, 0);
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
1658

da237f35a   Andreas Mohr   ALSA: azt3328: us...
1659
  		if (is_main_mixer_playback_codec) {
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
1660
1661
  			/* now unmute WaveOut */
  			if (!previously_muted)
b5dc20cd2   Andreas Mohr   ALSA: azt3328: ad...
1662
1663
  				snd_azf3328_mixer_mute_control_pcm(
  						chip, 0
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
1664
1665
  				);
  		}
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
1666

78df617ac   Andreas Mohr   ALSA: azt3328: fi...
1667
1668
  		snd_azf3328_dbgcodec("STOPPED %s
  ", codec->name);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1669
  		break;
ca54bde36   Andreas Mohr   [ALSA] azt3328.c:...
1670
  	case SNDRV_PCM_TRIGGER_SUSPEND:
78df617ac   Andreas Mohr   ALSA: azt3328: fi...
1671
1672
  		snd_azf3328_dbgcodec("SUSPEND %s
  ", codec->name);
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
1673
1674
1675
1676
1677
1678
  		/* make sure codec is stopped */
  		snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS,
  			snd_azf3328_codec_inw(
  				codec, IDX_IO_CODEC_DMA_FLAGS
  			) & ~DMA_RESUME
  		);
ca54bde36   Andreas Mohr   [ALSA] azt3328.c:...
1679
  		break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1680
          case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
99b359ba1   Takashi Iwai   [ALSA] Add missin...
1681
1682
  		snd_printk(KERN_ERR "FIXME: SNDRV_PCM_TRIGGER_PAUSE_PUSH NIY!
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1683
1684
                  break;
          case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
99b359ba1   Takashi Iwai   [ALSA] Add missin...
1685
1686
  		snd_printk(KERN_ERR "FIXME: SNDRV_PCM_TRIGGER_PAUSE_RELEASE NIY!
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1687
1688
                  break;
          default:
78df617ac   Andreas Mohr   ALSA: azt3328: fi...
1689
1690
  		snd_printk(KERN_ERR "FIXME: unknown trigger mode!
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1691
1692
                  return -EINVAL;
  	}
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
1693

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1694
1695
1696
  	snd_azf3328_dbgcallleave();
  	return result;
  }
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
1697
  static snd_pcm_uframes_t
da237f35a   Andreas Mohr   ALSA: azt3328: us...
1698
  snd_azf3328_pcm_pointer(struct snd_pcm_substream *substream
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
1699
  )
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1700
  {
da237f35a   Andreas Mohr   ALSA: azt3328: us...
1701
1702
  	const struct snd_azf3328_codec_data *codec =
  		substream->runtime->private_data;
345855951   Andreas Mohr   ALSA: azt3328: us...
1703
  	unsigned long result;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1704
  	snd_pcm_uframes_t frmres;
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
1705
  	result = snd_azf3328_codec_inl(codec, IDX_IO_CODEC_DMA_CURRPOS);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1706

d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
1707
  	/* calculate offset */
345855951   Andreas Mohr   ALSA: azt3328: us...
1708
1709
1710
1711
1712
  #ifdef QUERY_HARDWARE
  	result -= snd_azf3328_codec_inl(codec, IDX_IO_CODEC_DMA_START_1);
  #else
  	result -= codec->dma_base;
  #endif
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
1713
  	frmres = bytes_to_frames( substream->runtime, result);
adf5931f8   Andreas Mohr   ALSA: azt3328: co...
1714
1715
1716
  	snd_azf3328_dbgcodec("%08li %s @ 0x%8lx, frames %8ld
  ",
  				jiffies, codec->name, result, frmres);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1717
1718
  	return frmres;
  }
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
1719
1720
1721
1722
  /******************************************************************/
  
  #ifdef SUPPORT_GAMEPORT
  static inline void
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
1723
1724
1725
  snd_azf3328_gameport_irq_enable(struct snd_azf3328 *chip,
  				bool enable
  )
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
1726
1727
1728
1729
1730
1731
1732
1733
1734
  {
  	snd_azf3328_io_reg_setb(
  		chip->game_io+IDX_GAME_HWCONFIG,
  		GAME_HWCFG_IRQ_ENABLE,
  		enable
  	);
  }
  
  static inline void
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
1735
1736
1737
  snd_azf3328_gameport_legacy_address_enable(struct snd_azf3328 *chip,
  					   bool enable
  )
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
1738
1739
1740
1741
1742
1743
1744
  {
  	snd_azf3328_io_reg_setb(
  		chip->game_io+IDX_GAME_HWCONFIG,
  		GAME_HWCFG_LEGACY_ADDRESS_ENABLE,
  		enable
  	);
  }
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
  static void
  snd_azf3328_gameport_set_counter_frequency(struct snd_azf3328 *chip,
  					   unsigned int freq_cfg
  )
  {
  	snd_azf3328_io_reg_setb(
  		chip->game_io+IDX_GAME_HWCONFIG,
  		0x02,
  		(freq_cfg & 1) != 0
  	);
  	snd_azf3328_io_reg_setb(
  		chip->game_io+IDX_GAME_HWCONFIG,
  		0x04,
  		(freq_cfg & 2) != 0
  	);
  }
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
1761
  static inline void
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
1762
  snd_azf3328_gameport_axis_circuit_enable(struct snd_azf3328 *chip, bool enable)
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
1763
  {
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
1764
  	snd_azf3328_ctrl_reg_6AH_update(
627d3e7ab   Andreas Mohr   ALSA: PCI168 snd-...
1765
  		chip, IO_6A_SOMETHING2_GAMEPORT, enable
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
  	);
  }
  
  static inline void
  snd_azf3328_gameport_interrupt(struct snd_azf3328 *chip)
  {
  	/*
  	 * skeleton handler only
  	 * (we do not want axis reading in interrupt handler - too much load!)
  	 */
  	snd_azf3328_dbggame("gameport irq
  ");
  
  	 /* this should ACK the gameport IRQ properly, hopefully. */
  	snd_azf3328_game_inw(chip, IDX_GAME_AXIS_VALUE);
  }
  
  static int
  snd_azf3328_gameport_open(struct gameport *gameport, int mode)
  {
  	struct snd_azf3328 *chip = gameport_get_port_data(gameport);
  	int res;
  
  	snd_azf3328_dbggame("gameport_open, mode %d
  ", mode);
  	switch (mode) {
  	case GAMEPORT_MODE_COOKED:
  	case GAMEPORT_MODE_RAW:
  		res = 0;
  		break;
  	default:
  		res = -1;
  		break;
  	}
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
1800
1801
  	snd_azf3328_gameport_set_counter_frequency(chip,
  				GAME_HWCFG_ADC_COUNTER_FREQ_STD);
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
  	snd_azf3328_gameport_axis_circuit_enable(chip, (res == 0));
  
  	return res;
  }
  
  static void
  snd_azf3328_gameport_close(struct gameport *gameport)
  {
  	struct snd_azf3328 *chip = gameport_get_port_data(gameport);
  
  	snd_azf3328_dbggame("gameport_close
  ");
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
1814
1815
  	snd_azf3328_gameport_set_counter_frequency(chip,
  				GAME_HWCFG_ADC_COUNTER_FREQ_1_200);
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
  	snd_azf3328_gameport_axis_circuit_enable(chip, 0);
  }
  
  static int
  snd_azf3328_gameport_cooked_read(struct gameport *gameport,
  				 int *axes,
  				 int *buttons
  )
  {
  	struct snd_azf3328 *chip = gameport_get_port_data(gameport);
  	int i;
  	u8 val;
  	unsigned long flags;
da3cec35d   Takashi Iwai   ALSA: Kill snd_as...
1829
1830
  	if (snd_BUG_ON(!chip))
  		return 0;
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
  
  	spin_lock_irqsave(&chip->reg_lock, flags);
  	val = snd_azf3328_game_inb(chip, IDX_GAME_LEGACY_COMPATIBLE);
  	*buttons = (~(val) >> 4) & 0xf;
  
  	/* ok, this one is a bit dirty: cooked_read is being polled by a timer,
  	 * thus we're atomic and cannot actively wait in here
  	 * (which would be useful for us since it probably would be better
  	 * to trigger a measurement in here, then wait a short amount of
  	 * time until it's finished, then read values of _this_ measurement).
  	 *
  	 * Thus we simply resort to reading values if they're available already
  	 * and trigger the next measurement.
  	 */
  
  	val = snd_azf3328_game_inb(chip, IDX_GAME_AXES_CONFIG);
  	if (val & GAME_AXES_SAMPLING_READY) {
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
1848
  		for (i = 0; i < ARRAY_SIZE(chip->axes); ++i) {
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
1849
1850
1851
1852
1853
1854
1855
1856
1857
  			/* configure the axis to read */
  			val = (i << 4) | 0x0f;
  			snd_azf3328_game_outb(chip, IDX_GAME_AXES_CONFIG, val);
  
  			chip->axes[i] = snd_azf3328_game_inw(
  						chip, IDX_GAME_AXIS_VALUE
  					);
  		}
  	}
adf5931f8   Andreas Mohr   ALSA: azt3328: co...
1858
  	/* trigger next sampling of axes, to be evaluated the next time we
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
  	 * enter this function */
  
  	/* for some very, very strange reason we cannot enable
  	 * Measurement Ready monitoring for all axes here,
  	 * at least not when only one joystick connected */
  	val = 0x03; /* we're able to monitor axes 1 and 2 only */
  	snd_azf3328_game_outb(chip, IDX_GAME_AXES_CONFIG, val);
  
  	snd_azf3328_game_outw(chip, IDX_GAME_AXIS_VALUE, 0xffff);
  	spin_unlock_irqrestore(&chip->reg_lock, flags);
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
1869
  	for (i = 0; i < ARRAY_SIZE(chip->axes); i++) {
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
  		axes[i] = chip->axes[i];
  		if (axes[i] == 0xffff)
  			axes[i] = -1;
  	}
  
  	snd_azf3328_dbggame("cooked_read: axes %d %d %d %d buttons %d
  ",
  		axes[0], axes[1], axes[2], axes[3], *buttons
  	);
  
  	return 0;
  }
  
  static int __devinit
  snd_azf3328_gameport(struct snd_azf3328 *chip, int dev)
  {
  	struct gameport *gp;
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
  	chip->gameport = gp = gameport_allocate_port();
  	if (!gp) {
  		printk(KERN_ERR "azt3328: cannot alloc memory for gameport
  ");
  		return -ENOMEM;
  	}
  
  	gameport_set_name(gp, "AZF3328 Gameport");
  	gameport_set_phys(gp, "pci%s/gameport0", pci_name(chip->pci));
  	gameport_set_dev_parent(gp, &chip->pci->dev);
627d3e7ab   Andreas Mohr   ALSA: PCI168 snd-...
1897
  	gp->io = chip->game_io;
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
1898
1899
1900
1901
1902
1903
1904
1905
1906
  	gameport_set_port_data(gp, chip);
  
  	gp->open = snd_azf3328_gameport_open;
  	gp->close = snd_azf3328_gameport_close;
  	gp->fuzz = 16; /* seems ok */
  	gp->cooked_read = snd_azf3328_gameport_cooked_read;
  
  	/* DISABLE legacy address: we don't need it! */
  	snd_azf3328_gameport_legacy_address_enable(chip, 0);
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
1907
1908
  	snd_azf3328_gameport_set_counter_frequency(chip,
  				GAME_HWCFG_ADC_COUNTER_FREQ_1_200);
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
  	snd_azf3328_gameport_axis_circuit_enable(chip, 0);
  
  	gameport_register_port(chip->gameport);
  
  	return 0;
  }
  
  static void
  snd_azf3328_gameport_free(struct snd_azf3328 *chip)
  {
  	if (chip->gameport) {
  		gameport_unregister_port(chip->gameport);
  		chip->gameport = NULL;
  	}
  	snd_azf3328_gameport_irq_enable(chip, 0);
  }
  #else
  static inline int
  snd_azf3328_gameport(struct snd_azf3328 *chip, int dev) { return -ENOSYS; }
  static inline void
  snd_azf3328_gameport_free(struct snd_azf3328 *chip) { }
  static inline void
  snd_azf3328_gameport_interrupt(struct snd_azf3328 *chip)
  {
  	printk(KERN_WARNING "huh, game port IRQ occurred!?
  ");
  }
  #endif /* SUPPORT_GAMEPORT */
  
  /******************************************************************/
627d3e7ab   Andreas Mohr   ALSA: PCI168 snd-...
1939
1940
1941
  static inline void
  snd_azf3328_irq_log_unknown_type(u8 which)
  {
78df617ac   Andreas Mohr   ALSA: azt3328: fi...
1942
  	snd_azf3328_dbgcodec(
627d3e7ab   Andreas Mohr   ALSA: PCI168 snd-...
1943
1944
1945
1946
1947
  	"azt3328: unknown IRQ type (%x) occurred, please report!
  ",
  		which
  	);
  }
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
1948
  static inline void
da237f35a   Andreas Mohr   ALSA: azt3328: us...
1949
1950
1951
  snd_azf3328_pcm_interrupt(const struct snd_azf3328_codec_data *first_codec,
  			  u8 status
  )
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
1952
1953
1954
  {
  	u8 which;
  	enum snd_azf3328_codec_type codec_type;
da237f35a   Andreas Mohr   ALSA: azt3328: us...
1955
  	const struct snd_azf3328_codec_data *codec = first_codec;
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
1956
1957
1958
  
  	for (codec_type = AZF_CODEC_PLAYBACK;
  		 codec_type <= AZF_CODEC_I2S_OUT;
da237f35a   Andreas Mohr   ALSA: azt3328: us...
1959
  			 ++codec_type, ++codec) {
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
1960
1961
1962
1963
  
  		/* skip codec if there's no interrupt for it */
  		if (!(status & (1 << codec_type)))
  			continue;
da237f35a   Andreas Mohr   ALSA: azt3328: us...
1964
  		spin_lock(codec->lock);
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
1965
1966
1967
  		which = snd_azf3328_codec_inb(codec, IDX_IO_CODEC_IRQTYPE);
  		/* ack all IRQ types immediately */
  		snd_azf3328_codec_outb(codec, IDX_IO_CODEC_IRQTYPE, which);
da237f35a   Andreas Mohr   ALSA: azt3328: us...
1968
  		spin_unlock(codec->lock);
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
1969

da237f35a   Andreas Mohr   ALSA: azt3328: us...
1970
  		if (codec->substream) {
78df617ac   Andreas Mohr   ALSA: azt3328: fi...
1971
1972
1973
  			snd_pcm_period_elapsed(codec->substream);
  			snd_azf3328_dbgcodec("%s period done (#%x), @ %x
  ",
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
  				codec->name,
  				which,
  				snd_azf3328_codec_inl(
  					codec, IDX_IO_CODEC_DMA_CURRPOS
  				)
  			);
  		} else
  			printk(KERN_WARNING "azt3328: irq handler problem!
  ");
  		if (which & IRQ_SOMETHING)
  			snd_azf3328_irq_log_unknown_type(which);
  	}
  }
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
1987
  static irqreturn_t
7d12e780e   David Howells   IRQ: Maintain reg...
1988
  snd_azf3328_interrupt(int irq, void *dev_id)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1989
  {
95de77660   Takashi Iwai   [ALSA] Remove xxx...
1990
  	struct snd_azf3328 *chip = dev_id;
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
1991
  	u8 status;
78df617ac   Andreas Mohr   ALSA: azt3328: fi...
1992
  #if DEBUG_CODEC
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
1993
  	static unsigned long irq_count;
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
1994
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1995

dfbf95111   Andreas Mohr   ALSA: azt3328: la...
1996
  	status = snd_azf3328_ctrl_inb(chip, IDX_IO_IRQSTATUS);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1997
1998
  
          /* fast path out, to ease interrupt sharing */
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
1999
  	if (!(status &
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
2000
2001
  		(IRQ_PLAYBACK|IRQ_RECORDING|IRQ_I2S_OUT
  		|IRQ_GAMEPORT|IRQ_MPU401|IRQ_TIMER)
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
2002
  	))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2003
  		return IRQ_NONE; /* must be interrupt for another device */
78df617ac   Andreas Mohr   ALSA: azt3328: fi...
2004
  	snd_azf3328_dbgcodec(
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
2005
2006
  		"irq_count %ld! IDX_IO_IRQSTATUS %04x
  ",
627d3e7ab   Andreas Mohr   ALSA: PCI168 snd-...
2007
  			irq_count++ /* debug-only */,
627d3e7ab   Andreas Mohr   ALSA: PCI168 snd-...
2008
2009
  			status
  	);
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
2010

e2f872608   Andreas Mohr   [ALSA] azt3328.c:...
2011
  	if (status & IRQ_TIMER) {
78df617ac   Andreas Mohr   ALSA: azt3328: fi...
2012
2013
  		/* snd_azf3328_dbgcodec("timer %ld
  ",
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
2014
2015
2016
  			snd_azf3328_codec_inl(chip, IDX_IO_TIMER_VALUE)
  				& TIMER_VALUE_MASK
  		); */
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
2017
2018
2019
2020
  		if (chip->timer)
  			snd_timer_interrupt(chip->timer, chip->timer->sticks);
  		/* ACK timer */
                  spin_lock(&chip->reg_lock);
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
2021
  		snd_azf3328_ctrl_outb(chip, IDX_IO_TIMER_VALUE + 3, 0x07);
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
2022
  		spin_unlock(&chip->reg_lock);
78df617ac   Andreas Mohr   ALSA: azt3328: fi...
2023
2024
  		snd_azf3328_dbgcodec("azt3328: timer IRQ
  ");
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
2025
  	}
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
2026

dfbf95111   Andreas Mohr   ALSA: azt3328: la...
2027
  	if (status & (IRQ_PLAYBACK|IRQ_RECORDING|IRQ_I2S_OUT))
da237f35a   Andreas Mohr   ALSA: azt3328: us...
2028
  		snd_azf3328_pcm_interrupt(chip->codecs, status);
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
2029

02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
2030
2031
  	if (status & IRQ_GAMEPORT)
  		snd_azf3328_gameport_interrupt(chip);
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
2032

d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
2033
2034
  	/* MPU401 has less critical IRQ requirements
  	 * than timer and playback/recording, right? */
e2f872608   Andreas Mohr   [ALSA] azt3328.c:...
2035
  	if (status & IRQ_MPU401) {
7d12e780e   David Howells   IRQ: Maintain reg...
2036
  		snd_mpu401_uart_interrupt(irq, chip->rmidi->private_data);
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
2037
2038
  
  		/* hmm, do we have to ack the IRQ here somehow?
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
2039
  		 * If so, then I don't know how yet... */
78df617ac   Andreas Mohr   ALSA: azt3328: fi...
2040
2041
  		snd_azf3328_dbgcodec("azt3328: MPU401 IRQ
  ");
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
2042
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2043
2044
2045
2046
  	return IRQ_HANDLED;
  }
  
  /*****************************************************************/
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
2047
2048
2049
2050
2051
  /* as long as we think we have identical snd_pcm_hardware parameters
     for playback, capture and i2s out, we can use the same physical struct
     since the struct is simply being copied into a member.
  */
  static const struct snd_pcm_hardware snd_azf3328_hardware =
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2052
2053
  {
  	/* FIXME!! Correct? */
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
  	.info =			SNDRV_PCM_INFO_MMAP |
  				SNDRV_PCM_INFO_INTERLEAVED |
  				SNDRV_PCM_INFO_MMAP_VALID,
  	.formats =		SNDRV_PCM_FMTBIT_S8 |
  				SNDRV_PCM_FMTBIT_U8 |
  				SNDRV_PCM_FMTBIT_S16_LE |
  				SNDRV_PCM_FMTBIT_U16_LE,
  	.rates =		SNDRV_PCM_RATE_5512 |
  				SNDRV_PCM_RATE_8000_48000 |
  				SNDRV_PCM_RATE_KNOT,
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
2064
2065
  	.rate_min =		AZF_FREQ_4000,
  	.rate_max =		AZF_FREQ_66200,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2066
2067
  	.channels_min =		1,
  	.channels_max =		2,
7974150c8   Andreas Mohr   ALSA: azt3328: pe...
2068
2069
2070
2071
2072
2073
2074
2075
2076
  	.buffer_bytes_max =	(64*1024),
  	.period_bytes_min =	1024,
  	.period_bytes_max =	(32*1024),
  	/* We simply have two DMA areas (instead of a list of descriptors
  	   such as other cards); I believe that this is a fixed hardware
  	   attribute and there isn't much driver magic to be done to expand it.
  	   Thus indicate that we have at least and at most 2 periods. */
  	.periods_min =		2,
  	.periods_max =		2,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2077
2078
2079
2080
2081
  	/* FIXME: maybe that card actually has a FIFO?
  	 * Hmm, it seems newer revisions do have one, but we still don't know
  	 * its size... */
  	.fifo_size =		0,
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2082
2083
  
  static unsigned int snd_azf3328_fixed_rates[] = {
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
  	AZF_FREQ_4000,
  	AZF_FREQ_4800,
  	AZF_FREQ_5512,
  	AZF_FREQ_6620,
  	AZF_FREQ_8000,
  	AZF_FREQ_9600,
  	AZF_FREQ_11025,
  	AZF_FREQ_13240,
  	AZF_FREQ_16000,
  	AZF_FREQ_22050,
  	AZF_FREQ_32000,
  	AZF_FREQ_44100,
  	AZF_FREQ_48000,
  	AZF_FREQ_66200
  };
95de77660   Takashi Iwai   [ALSA] Remove xxx...
2099
  static struct snd_pcm_hw_constraint_list snd_azf3328_hw_constraints_rates = {
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
2100
  	.count = ARRAY_SIZE(snd_azf3328_fixed_rates),
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2101
2102
2103
2104
2105
  	.list = snd_azf3328_fixed_rates,
  	.mask = 0,
  };
  
  /*****************************************************************/
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
2106
  static int
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
2107
2108
2109
  snd_azf3328_pcm_open(struct snd_pcm_substream *substream,
  		     enum snd_azf3328_codec_type codec_type
  )
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2110
  {
95de77660   Takashi Iwai   [ALSA] Remove xxx...
2111
2112
  	struct snd_azf3328 *chip = snd_pcm_substream_chip(substream);
  	struct snd_pcm_runtime *runtime = substream->runtime;
da237f35a   Andreas Mohr   ALSA: azt3328: us...
2113
  	struct snd_azf3328_codec_data *codec = &chip->codecs[codec_type];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2114
2115
  
  	snd_azf3328_dbgcallenter();
da237f35a   Andreas Mohr   ALSA: azt3328: us...
2116
  	codec->substream = substream;
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
2117
2118
2119
  
  	/* same parameters for all our codecs - at least we think so... */
  	runtime->hw = snd_azf3328_hardware;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2120
2121
  	snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
  				   &snd_azf3328_hw_constraints_rates);
da237f35a   Andreas Mohr   ALSA: azt3328: us...
2122
  	runtime->private_data = codec;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2123
2124
2125
  	snd_azf3328_dbgcallleave();
  	return 0;
  }
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
2126
  static int
da237f35a   Andreas Mohr   ALSA: azt3328: us...
2127
  snd_azf3328_pcm_playback_open(struct snd_pcm_substream *substream)
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
2128
2129
2130
2131
2132
  {
  	return snd_azf3328_pcm_open(substream, AZF_CODEC_PLAYBACK);
  }
  
  static int
da237f35a   Andreas Mohr   ALSA: azt3328: us...
2133
  snd_azf3328_pcm_capture_open(struct snd_pcm_substream *substream)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2134
  {
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
2135
2136
  	return snd_azf3328_pcm_open(substream, AZF_CODEC_CAPTURE);
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2137

dfbf95111   Andreas Mohr   ALSA: azt3328: la...
2138
  static int
da237f35a   Andreas Mohr   ALSA: azt3328: us...
2139
  snd_azf3328_pcm_i2s_out_open(struct snd_pcm_substream *substream)
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
2140
2141
  {
  	return snd_azf3328_pcm_open(substream, AZF_CODEC_I2S_OUT);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2142
  }
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
2143
  static int
da237f35a   Andreas Mohr   ALSA: azt3328: us...
2144
  snd_azf3328_pcm_close(struct snd_pcm_substream *substream
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
2145
  )
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2146
  {
da237f35a   Andreas Mohr   ALSA: azt3328: us...
2147
2148
  	struct snd_azf3328_codec_data *codec =
  		substream->runtime->private_data;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2149
2150
  
  	snd_azf3328_dbgcallenter();
da237f35a   Andreas Mohr   ALSA: azt3328: us...
2151
  	codec->substream = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2152
2153
2154
  	snd_azf3328_dbgcallleave();
  	return 0;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2155
  /******************************************************************/
95de77660   Takashi Iwai   [ALSA] Remove xxx...
2156
  static struct snd_pcm_ops snd_azf3328_playback_ops = {
da237f35a   Andreas Mohr   ALSA: azt3328: us...
2157
2158
  	.open =		snd_azf3328_pcm_playback_open,
  	.close =	snd_azf3328_pcm_close,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2159
2160
2161
  	.ioctl =	snd_pcm_lib_ioctl,
  	.hw_params =	snd_azf3328_hw_params,
  	.hw_free =	snd_azf3328_hw_free,
da237f35a   Andreas Mohr   ALSA: azt3328: us...
2162
2163
2164
  	.prepare =	snd_azf3328_pcm_prepare,
  	.trigger =	snd_azf3328_pcm_trigger,
  	.pointer =	snd_azf3328_pcm_pointer
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2165
  };
95de77660   Takashi Iwai   [ALSA] Remove xxx...
2166
  static struct snd_pcm_ops snd_azf3328_capture_ops = {
da237f35a   Andreas Mohr   ALSA: azt3328: us...
2167
2168
  	.open =		snd_azf3328_pcm_capture_open,
  	.close =	snd_azf3328_pcm_close,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2169
2170
2171
  	.ioctl =	snd_pcm_lib_ioctl,
  	.hw_params =	snd_azf3328_hw_params,
  	.hw_free =	snd_azf3328_hw_free,
da237f35a   Andreas Mohr   ALSA: azt3328: us...
2172
2173
2174
  	.prepare =	snd_azf3328_pcm_prepare,
  	.trigger =	snd_azf3328_pcm_trigger,
  	.pointer =	snd_azf3328_pcm_pointer
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
2175
2176
2177
  };
  
  static struct snd_pcm_ops snd_azf3328_i2s_out_ops = {
da237f35a   Andreas Mohr   ALSA: azt3328: us...
2178
2179
  	.open =		snd_azf3328_pcm_i2s_out_open,
  	.close =	snd_azf3328_pcm_close,
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
2180
2181
2182
  	.ioctl =	snd_pcm_lib_ioctl,
  	.hw_params =	snd_azf3328_hw_params,
  	.hw_free =	snd_azf3328_hw_free,
da237f35a   Andreas Mohr   ALSA: azt3328: us...
2183
2184
2185
  	.prepare =	snd_azf3328_pcm_prepare,
  	.trigger =	snd_azf3328_pcm_trigger,
  	.pointer =	snd_azf3328_pcm_pointer
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2186
  };
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
2187
  static int __devinit
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
2188
  snd_azf3328_pcm(struct snd_azf3328 *chip)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2189
  {
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
2190
  enum { AZF_PCMDEV_STD, AZF_PCMDEV_I2S_OUT, NUM_AZF_PCMDEVS }; /* pcm devices */
95de77660   Takashi Iwai   [ALSA] Remove xxx...
2191
  	struct snd_pcm *pcm;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2192
2193
2194
  	int err;
  
  	snd_azf3328_dbgcallenter();
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
2195
2196
2197
2198
  
  	err = snd_pcm_new(chip->card, "AZF3328 DSP", AZF_PCMDEV_STD,
  								1, 1, &pcm);
  	if (err < 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2199
  		return err;
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
2200
2201
2202
2203
  	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
  						&snd_azf3328_playback_ops);
  	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
  						&snd_azf3328_capture_ops);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2204
2205
  
  	pcm->private_data = chip;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2206
2207
  	pcm->info_flags = 0;
  	strcpy(pcm->name, chip->card->shortname);
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
2208
2209
2210
  	/* same pcm object for playback/capture (see snd_pcm_new() above) */
  	chip->pcm[AZF_CODEC_PLAYBACK] = pcm;
  	chip->pcm[AZF_CODEC_CAPTURE] = pcm;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2211
2212
  
  	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
  						snd_dma_pci_data(chip->pci),
  							64*1024, 64*1024);
  
  	err = snd_pcm_new(chip->card, "AZF3328 I2S OUT", AZF_PCMDEV_I2S_OUT,
  								1, 0, &pcm);
  	if (err < 0)
  		return err;
  	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
  						&snd_azf3328_i2s_out_ops);
  
  	pcm->private_data = chip;
  	pcm->info_flags = 0;
  	strcpy(pcm->name, chip->card->shortname);
  	chip->pcm[AZF_CODEC_I2S_OUT] = pcm;
  
  	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
  						snd_dma_pci_data(chip->pci),
  							64*1024, 64*1024);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2231
2232
2233
2234
2235
2236
  
  	snd_azf3328_dbgcallleave();
  	return 0;
  }
  
  /******************************************************************/
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
2237
2238
  /*** NOTE: the physical timer resolution actually is 1024000 ticks per second
   *** (probably derived from main crystal via a divider of 24),
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
2239
2240
2241
2242
2243
2244
2245
2246
2247
   *** but announcing those attributes to user-space would make programs
   *** configure the timer to a 1 tick value, resulting in an absolutely fatal
   *** timer IRQ storm.
   *** Thus I chose to announce a down-scaled virtual timer to the outside and
   *** calculate real timer countdown values internally.
   *** (the scale factor can be set via module parameter "seqtimer_scaling").
   ***/
  
  static int
95de77660   Takashi Iwai   [ALSA] Remove xxx...
2248
  snd_azf3328_timer_start(struct snd_timer *timer)
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
2249
  {
95de77660   Takashi Iwai   [ALSA] Remove xxx...
2250
  	struct snd_azf3328 *chip;
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
2251
2252
2253
2254
2255
2256
  	unsigned long flags;
  	unsigned int delay;
  
  	snd_azf3328_dbgcallenter();
  	chip = snd_timer_chip(timer);
  	delay = ((timer->sticks * seqtimer_scaling) - 1) & TIMER_VALUE_MASK;
e2f872608   Andreas Mohr   [ALSA] azt3328.c:...
2257
  	if (delay < 49) {
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
2258
2259
2260
2261
2262
2263
2264
2265
  		/* uhoh, that's not good, since user-space won't know about
  		 * this timing tweak
  		 * (we need to do it to avoid a lockup, though) */
  
  		snd_azf3328_dbgtimer("delay was too low (%d)!
  ", delay);
  		delay = 49; /* minimum time is 49 ticks */
  	}
adf5931f8   Andreas Mohr   ALSA: azt3328: co...
2266
2267
  	snd_azf3328_dbgtimer("setting timer countdown value %d
  ", delay);
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
2268
  	delay |= TIMER_COUNTDOWN_ENABLE | TIMER_IRQ_ENABLE;
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
2269
  	spin_lock_irqsave(&chip->reg_lock, flags);
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
2270
  	snd_azf3328_ctrl_outl(chip, IDX_IO_TIMER_VALUE, delay);
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
2271
2272
2273
2274
2275
2276
  	spin_unlock_irqrestore(&chip->reg_lock, flags);
  	snd_azf3328_dbgcallleave();
  	return 0;
  }
  
  static int
95de77660   Takashi Iwai   [ALSA] Remove xxx...
2277
  snd_azf3328_timer_stop(struct snd_timer *timer)
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
2278
  {
95de77660   Takashi Iwai   [ALSA] Remove xxx...
2279
  	struct snd_azf3328 *chip;
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
2280
2281
2282
2283
2284
2285
  	unsigned long flags;
  
  	snd_azf3328_dbgcallenter();
  	chip = snd_timer_chip(timer);
  	spin_lock_irqsave(&chip->reg_lock, flags);
  	/* disable timer countdown and interrupt */
7974150c8   Andreas Mohr   ALSA: azt3328: pe...
2286
2287
2288
2289
2290
2291
2292
  	/* Hmm, should we write TIMER_IRQ_ACK here?
  	   YES indeed, otherwise a rogue timer operation - which prompts
  	   ALSA(?) to call repeated stop() in vain, but NOT start() -
  	   will never end (value 0x03 is kept shown in control byte).
  	   Simply manually poking 0x04 _once_ immediately successfully stops
  	   the hardware/ALSA interrupt activity. */
  	snd_azf3328_ctrl_outb(chip, IDX_IO_TIMER_VALUE + 3, 0x04);
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
2293
2294
2295
2296
2297
2298
2299
  	spin_unlock_irqrestore(&chip->reg_lock, flags);
  	snd_azf3328_dbgcallleave();
  	return 0;
  }
  
  
  static int
95de77660   Takashi Iwai   [ALSA] Remove xxx...
2300
  snd_azf3328_timer_precise_resolution(struct snd_timer *timer,
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
2301
2302
2303
2304
2305
2306
2307
2308
  					       unsigned long *num, unsigned long *den)
  {
  	snd_azf3328_dbgcallenter();
  	*num = 1;
  	*den = 1024000 / seqtimer_scaling;
  	snd_azf3328_dbgcallleave();
  	return 0;
  }
95de77660   Takashi Iwai   [ALSA] Remove xxx...
2309
  static struct snd_timer_hardware snd_azf3328_timer_hw = {
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
2310
2311
2312
2313
2314
2315
2316
2317
2318
  	.flags = SNDRV_TIMER_HW_AUTO,
  	.resolution = 977, /* 1000000/1024000 = 0.9765625us */
  	.ticks = 1024000, /* max tick count, defined by the value register; actually it's not 1024000, but 1048576, but we don't care */
  	.start = snd_azf3328_timer_start,
  	.stop = snd_azf3328_timer_stop,
  	.precise_resolution = snd_azf3328_timer_precise_resolution,
  };
  
  static int __devinit
95de77660   Takashi Iwai   [ALSA] Remove xxx...
2319
  snd_azf3328_timer(struct snd_azf3328 *chip, int device)
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
2320
  {
95de77660   Takashi Iwai   [ALSA] Remove xxx...
2321
2322
  	struct snd_timer *timer = NULL;
  	struct snd_timer_id tid;
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
  	int err;
  
  	snd_azf3328_dbgcallenter();
  	tid.dev_class = SNDRV_TIMER_CLASS_CARD;
  	tid.dev_sclass = SNDRV_TIMER_SCLASS_NONE;
  	tid.card = chip->card->number;
  	tid.device = device;
  	tid.subdevice = 0;
  
  	snd_azf3328_timer_hw.resolution *= seqtimer_scaling;
  	snd_azf3328_timer_hw.ticks /= seqtimer_scaling;
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
2334
2335
2336
  
  	err = snd_timer_new(chip->card, "AZF3328", &tid, &timer);
  	if (err < 0)
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
2337
  		goto out;
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
2338
2339
2340
2341
2342
2343
  
  	strcpy(timer->name, "AZF3328 timer");
  	timer->private_data = chip;
  	timer->hw = snd_azf3328_timer_hw;
  
  	chip->timer = timer;
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
2344
  	snd_azf3328_timer_stop(timer);
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
2345
2346
2347
2348
2349
2350
2351
2352
  	err = 0;
  
  out:
  	snd_azf3328_dbgcallleave();
  	return err;
  }
  
  /******************************************************************/
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
2353
2354
2355
2356
2357
  static int
  snd_azf3328_free(struct snd_azf3328 *chip)
  {
  	if (chip->irq < 0)
  		goto __end_hw;
b5dc20cd2   Andreas Mohr   ALSA: azt3328: ad...
2358
  	snd_azf3328_mixer_reset(chip);
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
  
  	snd_azf3328_timer_stop(chip->timer);
  	snd_azf3328_gameport_free(chip);
  
  	if (chip->irq >= 0)
  		synchronize_irq(chip->irq);
  __end_hw:
  	if (chip->irq >= 0)
  		free_irq(chip->irq, chip);
  	pci_release_regions(chip->pci);
  	pci_disable_device(chip->pci);
  
  	kfree(chip);
  	return 0;
  }
  
  static int
  snd_azf3328_dev_free(struct snd_device *device)
  {
  	struct snd_azf3328 *chip = device->device_data;
  	return snd_azf3328_free(chip);
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2381
2382
  #if 0
  /* check whether a bit can be modified */
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
2383
  static void
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
2384
  snd_azf3328_test_bit(unsigned unsigned reg, int bit)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
  {
  	unsigned char val, valoff, valon;
  
  	val = inb(reg);
  
  	outb(val & ~(1 << bit), reg);
  	valoff = inb(reg);
  
  	outb(val|(1 << bit), reg);
  	valon = inb(reg);
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
2395

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2396
  	outb(val, reg);
78df617ac   Andreas Mohr   ALSA: azt3328: fi...
2397
2398
  	printk(KERN_DEBUG "reg %04x bit %d: %02x %02x %02x
  ",
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
2399
2400
  				reg, bit, val, valoff, valon
  	);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2401
2402
  }
  #endif
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
2403
  static inline void
95de77660   Takashi Iwai   [ALSA] Remove xxx...
2404
  snd_azf3328_debug_show_ports(const struct snd_azf3328 *chip)
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
2405
  {
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
2406
  #if DEBUG_MISC
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
2407
  	u16 tmp;
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
2408
  	snd_azf3328_dbgmisc(
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
2409
  		"ctrl_io 0x%lx, game_io 0x%lx, mpu_io 0x%lx, "
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
2410
2411
  		"opl3_io 0x%lx, mixer_io 0x%lx, irq %d
  ",
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
2412
  		chip->ctrl_io, chip->game_io, chip->mpu_io,
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
2447
  		chip->opl3_io, chip->mixer_io, chip->irq
  	);
  
  	snd_azf3328_dbgmisc("game %02x %02x %02x %02x %02x %02x
  ",
  		snd_azf3328_game_inb(chip, 0),
  		snd_azf3328_game_inb(chip, 1),
  		snd_azf3328_game_inb(chip, 2),
  		snd_azf3328_game_inb(chip, 3),
  		snd_azf3328_game_inb(chip, 4),
  		snd_azf3328_game_inb(chip, 5)
  	);
  
  	for (tmp = 0; tmp < 0x07; tmp += 1)
  		snd_azf3328_dbgmisc("mpu_io 0x%04x
  ", inb(chip->mpu_io + tmp));
  
  	for (tmp = 0; tmp <= 0x07; tmp += 1)
  		snd_azf3328_dbgmisc("0x%02x: game200 0x%04x, game208 0x%04x
  ",
  			tmp, inb(0x200 + tmp), inb(0x208 + tmp));
  
  	for (tmp = 0; tmp <= 0x01; tmp += 1)
  		snd_azf3328_dbgmisc(
  			"0x%02x: mpu300 0x%04x, mpu310 0x%04x, mpu320 0x%04x, "
  			"mpu330 0x%04x opl388 0x%04x opl38c 0x%04x
  ",
  				tmp,
  				inb(0x300 + tmp),
  				inb(0x310 + tmp),
  				inb(0x320 + tmp),
  				inb(0x330 + tmp),
  				inb(0x388 + tmp),
  				inb(0x38c + tmp)
  		);
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
2448

dfbf95111   Andreas Mohr   ALSA: azt3328: la...
2449
2450
2451
2452
  	for (tmp = 0; tmp < AZF_IO_SIZE_CTRL; tmp += 2)
  		snd_azf3328_dbgmisc("ctrl 0x%02x: 0x%04x
  ",
  			tmp, snd_azf3328_ctrl_inw(chip, tmp)
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
2453
  		);
e24a121aa   Andreas Mohr   [ALSA] azt3328.c:...
2454
2455
  
  	for (tmp = 0; tmp < AZF_IO_SIZE_MIXER; tmp += 2)
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
2456
2457
2458
2459
2460
  		snd_azf3328_dbgmisc("mixer 0x%02x: 0x%04x
  ",
  			tmp, snd_azf3328_mixer_inw(chip, tmp)
  		);
  #endif /* DEBUG_MISC */
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
2461
2462
2463
  }
  
  static int __devinit
95de77660   Takashi Iwai   [ALSA] Remove xxx...
2464
  snd_azf3328_create(struct snd_card *card,
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
2465
2466
2467
  		   struct pci_dev *pci,
  		   unsigned long device_type,
  		   struct snd_azf3328 **rchip)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2468
  {
95de77660   Takashi Iwai   [ALSA] Remove xxx...
2469
  	struct snd_azf3328 *chip;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2470
  	int err;
95de77660   Takashi Iwai   [ALSA] Remove xxx...
2471
  	static struct snd_device_ops ops = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2472
2473
  		.dev_free =     snd_azf3328_dev_free,
  	};
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
2474
2475
  	u8 dma_init;
  	enum snd_azf3328_codec_type codec_type;
da237f35a   Andreas Mohr   ALSA: azt3328: us...
2476
  	struct snd_azf3328_codec_data *codec_setup;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2477
2478
  
  	*rchip = NULL;
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
2479
2480
  	err = pci_enable_device(pci);
  	if (err < 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2481
  		return err;
e560d8d83   Takashi Iwai   [ALSA] Replace wi...
2482
  	chip = kzalloc(sizeof(*chip), GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2483
  	if (chip == NULL) {
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
2484
2485
  		err = -ENOMEM;
  		goto out_err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2486
2487
2488
2489
2490
2491
2492
  	}
  	spin_lock_init(&chip->reg_lock);
  	chip->card = card;
  	chip->pci = pci;
  	chip->irq = -1;
  
  	/* check if we can restrict PCI DMA transfers to 24 bits */
2f4f27d42   Yang Hongyang   dma-mapping: repl...
2493
2494
  	if (pci_set_dma_mask(pci, DMA_BIT_MASK(24)) < 0 ||
  	    pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(24)) < 0) {
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
2495
2496
2497
2498
  		snd_printk(KERN_ERR "architecture does not support "
  					"24bit PCI busmaster DMA
  "
  		);
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
2499
2500
  		err = -ENXIO;
  		goto out_err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2501
  	}
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
2502
2503
  	err = pci_request_regions(pci, "Aztech AZF3328");
  	if (err < 0)
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
2504
  		goto out_err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2505

dfbf95111   Andreas Mohr   ALSA: azt3328: la...
2506
  	chip->ctrl_io  = pci_resource_start(pci, 0);
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
2507
2508
  	chip->game_io  = pci_resource_start(pci, 1);
  	chip->mpu_io   = pci_resource_start(pci, 2);
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
2509
  	chip->opl3_io  = pci_resource_start(pci, 3);
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
2510
  	chip->mixer_io = pci_resource_start(pci, 4);
9fd8d36ca   Andreas Mohr   ALSA: azt3328: co...
2511
2512
  	codec_setup = &chip->codecs[AZF_CODEC_PLAYBACK];
  	codec_setup->io_base = chip->ctrl_io + AZF_IO_OFFS_CODEC_PLAYBACK;
da237f35a   Andreas Mohr   ALSA: azt3328: us...
2513
2514
  	codec_setup->lock = &chip->reg_lock;
  	codec_setup->type = AZF_CODEC_PLAYBACK;
9fd8d36ca   Andreas Mohr   ALSA: azt3328: co...
2515
2516
2517
2518
  	codec_setup->name = "PLAYBACK";
  
  	codec_setup = &chip->codecs[AZF_CODEC_CAPTURE];
  	codec_setup->io_base = chip->ctrl_io + AZF_IO_OFFS_CODEC_CAPTURE;
da237f35a   Andreas Mohr   ALSA: azt3328: us...
2519
2520
  	codec_setup->lock = &chip->reg_lock;
  	codec_setup->type = AZF_CODEC_CAPTURE;
9fd8d36ca   Andreas Mohr   ALSA: azt3328: co...
2521
2522
2523
2524
  	codec_setup->name = "CAPTURE";
  
  	codec_setup = &chip->codecs[AZF_CODEC_I2S_OUT];
  	codec_setup->io_base = chip->ctrl_io + AZF_IO_OFFS_CODEC_I2S_OUT;
da237f35a   Andreas Mohr   ALSA: azt3328: us...
2525
2526
  	codec_setup->lock = &chip->reg_lock;
  	codec_setup->type = AZF_CODEC_I2S_OUT;
9fd8d36ca   Andreas Mohr   ALSA: azt3328: co...
2527
  	codec_setup->name = "I2S_OUT";
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2528

437a5a460   Takashi Iwai   [ALSA] Remove IRQ...
2529
  	if (request_irq(pci->irq, snd_azf3328_interrupt,
934c2b6d0   Takashi Iwai   ALSA: use KBUILD_...
2530
  			IRQF_SHARED, KBUILD_MODNAME, chip)) {
99b359ba1   Takashi Iwai   [ALSA] Add missin...
2531
2532
  		snd_printk(KERN_ERR "unable to grab IRQ %d
  ", pci->irq);
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
2533
2534
  		err = -EBUSY;
  		goto out_err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2535
2536
2537
2538
  	}
  	chip->irq = pci->irq;
  	pci_set_master(pci);
  	synchronize_irq(chip->irq);
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
2539
  	snd_azf3328_debug_show_ports(chip);
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
2540
2541
2542
  
  	err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
  	if (err < 0)
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
2543
  		goto out_err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2544
2545
  
  	/* create mixer interface & switches */
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
2546
2547
  	err = snd_azf3328_mixer_new(chip);
  	if (err < 0)
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
2548
  		goto out_err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2549

dfbf95111   Andreas Mohr   ALSA: azt3328: la...
2550
2551
2552
2553
2554
2555
2556
2557
  	/* standard codec init stuff */
  		/* default DMA init value */
  	dma_init = DMA_RUN_SOMETHING2|DMA_EPILOGUE_SOMETHING|DMA_SOMETHING_ELSE;
  
  	for (codec_type = AZF_CODEC_PLAYBACK;
  		codec_type <= AZF_CODEC_I2S_OUT; ++codec_type) {
  		struct snd_azf3328_codec_data *codec =
  			 &chip->codecs[codec_type];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2558

adf5931f8   Andreas Mohr   ALSA: azt3328: co...
2559
  		/* shutdown codecs to reduce power / noise */
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
2560
2561
2562
  			/* have ...ctrl_codec_activity() act properly */
  		codec->running = 1;
  		snd_azf3328_ctrl_codec_activity(chip, codec_type, 0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2563

da237f35a   Andreas Mohr   ALSA: azt3328: us...
2564
  		spin_lock_irq(codec->lock);
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
2565
2566
  		snd_azf3328_codec_outb(codec, IDX_IO_CODEC_DMA_FLAGS,
  						 dma_init);
da237f35a   Andreas Mohr   ALSA: azt3328: us...
2567
  		spin_unlock_irq(codec->lock);
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
2568
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2569
2570
2571
2572
  
  	snd_card_set_dev(card, &pci->dev);
  
  	*rchip = chip;
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
2573
2574
2575
2576
2577
2578
2579
2580
2581
2582
2583
  
  	err = 0;
  	goto out;
  
  out_err:
  	if (chip)
  		snd_azf3328_free(chip);
  	pci_disable_device(pci);
  
  out:
  	return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2584
  }
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
2585
2586
  static int __devinit
  snd_azf3328_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2587
2588
  {
  	static int dev;
95de77660   Takashi Iwai   [ALSA] Remove xxx...
2589
2590
2591
  	struct snd_card *card;
  	struct snd_azf3328 *chip;
  	struct snd_opl3 *opl3;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2592
2593
2594
  	int err;
  
  	snd_azf3328_dbgcallenter();
a5a3973da   Julia Lawall   ALSA: azt3328 - a...
2595
2596
2597
2598
  	if (dev >= SNDRV_CARDS) {
  		err = -ENODEV;
  		goto out;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2599
2600
  	if (!enable[dev]) {
  		dev++;
a5a3973da   Julia Lawall   ALSA: azt3328 - a...
2601
2602
  		err = -ENOENT;
  		goto out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2603
  	}
e58de7baf   Takashi Iwai   ALSA: Convert to ...
2604
2605
  	err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
  	if (err < 0)
a5a3973da   Julia Lawall   ALSA: azt3328 - a...
2606
  		goto out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2607
2608
2609
  
  	strcpy(card->driver, "AZF3328");
  	strcpy(card->shortname, "Aztech AZF3328 (PCI168)");
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
2610
2611
  	err = snd_azf3328_create(card, pci, pci_id->driver_data, &chip);
  	if (err < 0)
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
2612
  		goto out_err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2613

ca54bde36   Andreas Mohr   [ALSA] azt3328.c:...
2614
  	card->private_data = chip;
78df617ac   Andreas Mohr   ALSA: azt3328: fi...
2615
2616
  	/* chose to use MPU401_HW_AZT2320 ID instead of MPU401_HW_MPU401,
  	   since our hardware ought to be similar, thus use same ID. */
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
2617
  	err = snd_mpu401_uart_new(
78df617ac   Andreas Mohr   ALSA: azt3328: fi...
2618
2619
  		card, 0,
  		MPU401_HW_AZT2320, chip->mpu_io, MPU401_INFO_INTEGRATED,
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
2620
2621
2622
2623
2624
2625
2626
  		pci->irq, 0, &chip->rmidi
  	);
  	if (err < 0) {
  		snd_printk(KERN_ERR "azf3328: no MPU-401 device at 0x%lx?
  ",
  				chip->mpu_io
  		);
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
2627
2628
  		goto out_err;
  	}
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
2629
2630
  	err = snd_azf3328_timer(chip, 0);
  	if (err < 0)
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
2631
  		goto out_err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2632

dfbf95111   Andreas Mohr   ALSA: azt3328: la...
2633
  	err = snd_azf3328_pcm(chip);
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
2634
  	if (err < 0)
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
2635
  		goto out_err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2636

02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
2637
  	if (snd_opl3_create(card, chip->opl3_io, chip->opl3_io+2,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2638
  			    OPL3_HW_AUTO, 1, &opl3) < 0) {
99b359ba1   Takashi Iwai   [ALSA] Add missin...
2639
2640
  		snd_printk(KERN_ERR "azf3328: no OPL3 device at 0x%lx-0x%lx?
  ",
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
2641
2642
  			   chip->opl3_io, chip->opl3_io+2
  		);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2643
  	} else {
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
2644
2645
2646
2647
2648
2649
  		/* need to use IDs 1, 2 since ID 0 is snd_azf3328_timer above */
  		err = snd_opl3_timer_new(opl3, 1, 2);
  		if (err < 0)
  			goto out_err;
  		err = snd_opl3_hwdep_new(opl3, 0, 1, NULL);
  		if (err < 0)
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
2650
  			goto out_err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2651
  	}
ca54bde36   Andreas Mohr   [ALSA] azt3328.c:...
2652
  	opl3->private_data = chip;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2653
  	sprintf(card->longname, "%s at 0x%lx, irq %i",
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
2654
  		card->shortname, chip->ctrl_io, chip->irq);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2655

02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
2656
2657
  	err = snd_card_register(card);
  	if (err < 0)
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
2658
  		goto out_err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2659
2660
  
  #ifdef MODULE
78df617ac   Andreas Mohr   ALSA: azt3328: fi...
2661
  	printk(KERN_INFO
e24a121aa   Andreas Mohr   [ALSA] azt3328.c:...
2662
2663
2664
2665
  "azt3328: Sound driver for Aztech AZF3328-based soundcards such as PCI168.
  "
  "azt3328: Hardware was completely undocumented, unfortunately.
  "
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
2666
2667
2668
2669
2670
  "azt3328: Feel free to contact andi AT lisas.de for bug reports etc.!
  "
  "azt3328: User-scalable sequencer timer set to %dHz (1024000Hz / %d).
  ",
  	1024000 / seqtimer_scaling, seqtimer_scaling);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2671
  #endif
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
2672
  	snd_azf3328_gameport(chip, dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2673
2674
2675
  
  	pci_set_drvdata(pci, card);
  	dev++;
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
2676
2677
  	err = 0;
  	goto out;
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
2678

d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
2679
  out_err:
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
2680
2681
  	snd_printk(KERN_ERR "azf3328: something failed, exiting
  ");
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
2682
  	snd_card_free(card);
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
2683

d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
2684
  out:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2685
  	snd_azf3328_dbgcallleave();
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
2686
  	return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2687
  }
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
2688
2689
  static void __devexit
  snd_azf3328_remove(struct pci_dev *pci)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2690
2691
2692
2693
2694
2695
  {
  	snd_azf3328_dbgcallenter();
  	snd_card_free(pci_get_drvdata(pci));
  	pci_set_drvdata(pci, NULL);
  	snd_azf3328_dbgcallleave();
  }
ca54bde36   Andreas Mohr   [ALSA] azt3328.c:...
2696
  #ifdef CONFIG_PM
78df617ac   Andreas Mohr   ALSA: azt3328: fi...
2697
2698
2699
2700
2701
2702
2703
2704
2705
2706
2707
2708
2709
2710
  static inline void
  snd_azf3328_suspend_regs(unsigned long io_addr, unsigned count, u32 *saved_regs)
  {
  	unsigned reg;
  
  	for (reg = 0; reg < count; ++reg) {
  		*saved_regs = inl(io_addr);
  		snd_azf3328_dbgpm("suspend: io 0x%04lx: 0x%08x
  ",
  			io_addr, *saved_regs);
  		++saved_regs;
  		io_addr += sizeof(*saved_regs);
  	}
  }
b5dc20cd2   Andreas Mohr   ALSA: azt3328: ad...
2711
2712
2713
2714
2715
2716
2717
2718
2719
2720
2721
2722
2723
2724
2725
2726
2727
2728
2729
2730
2731
2732
2733
2734
2735
2736
2737
2738
2739
2740
2741
2742
2743
2744
2745
2746
2747
2748
2749
2750
2751
2752
2753
2754
2755
2756
2757
2758
2759
  static inline void
  snd_azf3328_resume_regs(const u32 *saved_regs,
  			unsigned long io_addr,
  			unsigned count
  )
  {
  	unsigned reg;
  
  	for (reg = 0; reg < count; ++reg) {
  		outl(*saved_regs, io_addr);
  		snd_azf3328_dbgpm("resume: io 0x%04lx: 0x%08x --> 0x%08x
  ",
  			io_addr, *saved_regs, inl(io_addr));
  		++saved_regs;
  		io_addr += sizeof(*saved_regs);
  	}
  }
  
  static inline void
  snd_azf3328_suspend_ac97(struct snd_azf3328 *chip)
  {
  #ifdef AZF_USE_AC97_LAYER
  	snd_ac97_suspend(chip->ac97);
  #else
  	snd_azf3328_suspend_regs(chip->mixer_io,
  		ARRAY_SIZE(chip->saved_regs_mixer), chip->saved_regs_mixer);
  
  	/* make sure to disable master volume etc. to prevent looping sound */
  	snd_azf3328_mixer_mute_control_master(chip, 1);
  	snd_azf3328_mixer_mute_control_pcm(chip, 1);
  #endif /* AZF_USE_AC97_LAYER */
  }
  
  static inline void
  snd_azf3328_resume_ac97(const struct snd_azf3328 *chip)
  {
  #ifdef AZF_USE_AC97_LAYER
  	snd_ac97_resume(chip->ac97);
  #else
  	snd_azf3328_resume_regs(chip->saved_regs_mixer, chip->mixer_io,
  					ARRAY_SIZE(chip->saved_regs_mixer));
  
  	/* unfortunately with 32bit transfers, IDX_MIXER_PLAY_MASTER (0x02)
  	   and IDX_MIXER_RESET (offset 0x00) get touched at the same time,
  	   resulting in a mixer reset condition persisting until _after_
  	   master vol was restored. Thus master vol needs an extra restore. */
  	outw(((u16 *)chip->saved_regs_mixer)[1], chip->mixer_io + 2);
  #endif /* AZF_USE_AC97_LAYER */
  }
ca54bde36   Andreas Mohr   [ALSA] azt3328.c:...
2760
2761
2762
2763
2764
  static int
  snd_azf3328_suspend(struct pci_dev *pci, pm_message_t state)
  {
  	struct snd_card *card = pci_get_drvdata(pci);
  	struct snd_azf3328 *chip = card->private_data;
78df617ac   Andreas Mohr   ALSA: azt3328: fi...
2765
  	u16 *saved_regs_ctrl_u16;
ca54bde36   Andreas Mohr   [ALSA] azt3328.c:...
2766
2767
  
  	snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
2768

adf5931f8   Andreas Mohr   ALSA: azt3328: co...
2769
  	/* same pcm object for playback/capture */
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
2770
2771
  	snd_pcm_suspend_all(chip->pcm[AZF_CODEC_PLAYBACK]);
  	snd_pcm_suspend_all(chip->pcm[AZF_CODEC_I2S_OUT]);
ca54bde36   Andreas Mohr   [ALSA] azt3328.c:...
2772

b5dc20cd2   Andreas Mohr   ALSA: azt3328: ad...
2773
  	snd_azf3328_suspend_ac97(chip);
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
2774

78df617ac   Andreas Mohr   ALSA: azt3328: fi...
2775
2776
  	snd_azf3328_suspend_regs(chip->ctrl_io,
  		ARRAY_SIZE(chip->saved_regs_ctrl), chip->saved_regs_ctrl);
627d3e7ab   Andreas Mohr   ALSA: PCI168 snd-...
2777
2778
  
  	/* manually store the one currently relevant write-only reg, too */
78df617ac   Andreas Mohr   ALSA: azt3328: fi...
2779
2780
  	saved_regs_ctrl_u16 = (u16 *)chip->saved_regs_ctrl;
  	saved_regs_ctrl_u16[IDX_IO_6AH / 2] = chip->shadow_reg_ctrl_6AH;
627d3e7ab   Andreas Mohr   ALSA: PCI168 snd-...
2781

78df617ac   Andreas Mohr   ALSA: azt3328: fi...
2782
2783
2784
2785
2786
2787
  	snd_azf3328_suspend_regs(chip->game_io,
  		ARRAY_SIZE(chip->saved_regs_game), chip->saved_regs_game);
  	snd_azf3328_suspend_regs(chip->mpu_io,
  		ARRAY_SIZE(chip->saved_regs_mpu), chip->saved_regs_mpu);
  	snd_azf3328_suspend_regs(chip->opl3_io,
  		ARRAY_SIZE(chip->saved_regs_opl3), chip->saved_regs_opl3);
ca54bde36   Andreas Mohr   [ALSA] azt3328.c:...
2788

ca54bde36   Andreas Mohr   [ALSA] azt3328.c:...
2789
2790
  	pci_disable_device(pci);
  	pci_save_state(pci);
30b35399c   Takashi Iwai   [ALSA] Various fi...
2791
  	pci_set_power_state(pci, pci_choose_state(pci, state));
ca54bde36   Andreas Mohr   [ALSA] azt3328.c:...
2792
2793
2794
2795
2796
2797
2798
  	return 0;
  }
  
  static int
  snd_azf3328_resume(struct pci_dev *pci)
  {
  	struct snd_card *card = pci_get_drvdata(pci);
dfbf95111   Andreas Mohr   ALSA: azt3328: la...
2799
  	const struct snd_azf3328 *chip = card->private_data;
ca54bde36   Andreas Mohr   [ALSA] azt3328.c:...
2800

ca54bde36   Andreas Mohr   [ALSA] azt3328.c:...
2801
  	pci_set_power_state(pci, PCI_D0);
30b35399c   Takashi Iwai   [ALSA] Various fi...
2802
2803
2804
2805
2806
2807
2808
2809
  	pci_restore_state(pci);
  	if (pci_enable_device(pci) < 0) {
  		printk(KERN_ERR "azt3328: pci_enable_device failed, "
  		       "disabling device
  ");
  		snd_card_disconnect(card);
  		return -EIO;
  	}
ca54bde36   Andreas Mohr   [ALSA] azt3328.c:...
2810
  	pci_set_master(pci);
78df617ac   Andreas Mohr   ALSA: azt3328: fi...
2811
2812
2813
2814
2815
2816
  	snd_azf3328_resume_regs(chip->saved_regs_game, chip->game_io,
  					ARRAY_SIZE(chip->saved_regs_game));
  	snd_azf3328_resume_regs(chip->saved_regs_mpu, chip->mpu_io,
  					ARRAY_SIZE(chip->saved_regs_mpu));
  	snd_azf3328_resume_regs(chip->saved_regs_opl3, chip->opl3_io,
  					ARRAY_SIZE(chip->saved_regs_opl3));
b5dc20cd2   Andreas Mohr   ALSA: azt3328: ad...
2817
  	snd_azf3328_resume_ac97(chip);
78df617ac   Andreas Mohr   ALSA: azt3328: fi...
2818
2819
2820
  
  	snd_azf3328_resume_regs(chip->saved_regs_ctrl, chip->ctrl_io,
  					ARRAY_SIZE(chip->saved_regs_ctrl));
ca54bde36   Andreas Mohr   [ALSA] azt3328.c:...
2821
2822
2823
2824
  
  	snd_power_change_state(card, SNDRV_CTL_POWER_D0);
  	return 0;
  }
02330fbaa   Andreas Mohr   [ALSA] PCI168 snd...
2825
  #endif /* CONFIG_PM */
ca54bde36   Andreas Mohr   [ALSA] azt3328.c:...
2826

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2827
  static struct pci_driver driver = {
3733e424c   Takashi Iwai   ALSA: Use KBUILD_...
2828
  	.name = KBUILD_MODNAME,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2829
2830
2831
  	.id_table = snd_azf3328_ids,
  	.probe = snd_azf3328_probe,
  	.remove = __devexit_p(snd_azf3328_remove),
ca54bde36   Andreas Mohr   [ALSA] azt3328.c:...
2832
2833
2834
2835
  #ifdef CONFIG_PM
  	.suspend = snd_azf3328_suspend,
  	.resume = snd_azf3328_resume,
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2836
  };
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
2837
2838
  static int __init
  alsa_card_azf3328_init(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2839
2840
2841
  {
  	int err;
  	snd_azf3328_dbgcallenter();
01d25d460   Takashi Iwai   [ALSA] Replace pc...
2842
  	err = pci_register_driver(&driver);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2843
2844
2845
  	snd_azf3328_dbgcallleave();
  	return err;
  }
d91c64c82   Andreas Mohr   [ALSA] AZT3328 dr...
2846
2847
  static void __exit
  alsa_card_azf3328_exit(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2848
2849
2850
2851
2852
2853
2854
2855
  {
  	snd_azf3328_dbgcallenter();
  	pci_unregister_driver(&driver);
  	snd_azf3328_dbgcallleave();
  }
  
  module_init(alsa_card_azf3328_init)
  module_exit(alsa_card_azf3328_exit)