Linux kernel & device driver programming

Cross-Referenced Linux and Device Driver Code

[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ]
Version: [ 2.6.11.8 ] [ 2.6.25 ] [ 2.6.25.8 ] [ 2.6.31.13 ] Architecture: [ i386 ]
  1 /*
  2  *  Routines for Trident 4DWave NX/DX soundcards - Synthesizer
  3  *  Copyright (c) by Scott McNab <jedi@tartarus.uwa.edu.au>
  4  *
  5  *
  6  *   This program is free software; you can redistribute it and/or modify
  7  *   it under the terms of the GNU General Public License as published by
  8  *   the Free Software Foundation; either version 2 of the License, or
  9  *   (at your option) any later version.
 10  *
 11  *   This program is distributed in the hope that it will be useful,
 12  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 13  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 14  *   GNU General Public License for more details.
 15  *
 16  *   You should have received a copy of the GNU General Public License
 17  *   along with this program; if not, write to the Free Software
 18  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
 19  *
 20  */
 21 
 22 #include <sound/driver.h>
 23 #include <asm/io.h>
 24 #include <linux/init.h>
 25 #include <linux/slab.h>
 26 #include <linux/pci.h>
 27 #include <sound/core.h>
 28 #include <sound/trident.h>
 29 #include <sound/seq_device.h>
 30 
 31 MODULE_AUTHOR("Scott McNab <jedi@tartarus.uwa.edu.au>");
 32 MODULE_DESCRIPTION("Routines for Trident 4DWave NX/DX soundcards - Synthesizer");
 33 MODULE_LICENSE("GPL");
 34 
 35 /* linear to log pan conversion table (4.2 channel attenuation format) */
 36 static unsigned int pan_table[63] = {
 37         7959, 7733, 7514, 7301, 7093, 6892, 6697, 6507, 
 38         6322, 6143, 5968, 5799, 5634, 5475, 5319, 5168, 
 39         5022, 4879, 4741, 4606, 4475, 4349, 4225, 4105, 
 40         3989, 3876, 3766, 3659, 3555, 3454, 3356, 3261, 
 41         3168, 3078, 2991, 2906, 2824, 2744, 2666, 2590, 
 42         2517, 2445, 2376, 2308, 2243, 2179, 2117, 2057, 
 43         1999, 1942, 1887, 1833, 1781, 1731, 1682, 1634, 
 44         1588, 1543, 1499, 1456, 1415, 1375, 1336
 45 };
 46 
 47 #define LOG_TABLE_SIZE 386
 48 
 49 /* Linear half-attenuation to log conversion table in the format:
 50  *   {linear volume, logarithmic attenuation equivalent}, ...
 51  *
 52  * Provides conversion from a linear half-volume value in the range
 53  * [0,8192] to a logarithmic attenuation value in the range 0 to 6.02dB.
 54  * Halving the linear volume is equivalent to an additional 6dB of 
 55  * logarithmic attenuation. The algorithm used in log_from_linear()
 56  * therefore uses this table as follows:
 57  * 
 58  * - loop and for every time the volume is less than half the maximum 
 59  *   volume (16384), add another 6dB and halve the maximum value used
 60  *   for this comparison.
 61  * - when the volume is greater than half the maximum volume, take
 62  *   the difference of the volume to half volume (in the range [0,8192])
 63  *   and look up the log_table[] to find the nearest entry.
 64  * - take the logarithic component of this entry and add it to the 
 65  *   resulting attenuation.
 66  *
 67  * Thus this routine provides a linear->log conversion for a range of
 68  * [0,16384] using only 386 table entries
 69  *
 70  * Note: although this table stores log attenuation in 8.8 format, values
 71  * were only calculated for 6 bits fractional precision, since that is
 72  * the most precision offered by the trident hardware.
 73  */
 74 
 75 static unsigned short log_table[LOG_TABLE_SIZE*2] =
 76 {
 77         4, 0x0604, 19, 0x0600, 34, 0x05fc, 
 78         49, 0x05f8, 63, 0x05f4, 78, 0x05f0, 93, 0x05ec, 108, 0x05e8, 
 79         123, 0x05e4, 138, 0x05e0, 153, 0x05dc, 168, 0x05d8, 183, 0x05d4, 
 80         198, 0x05d0, 213, 0x05cc, 228, 0x05c8, 244, 0x05c4, 259, 0x05c0, 
 81         274, 0x05bc, 289, 0x05b8, 304, 0x05b4, 320, 0x05b0, 335, 0x05ac, 
 82         350, 0x05a8, 366, 0x05a4, 381, 0x05a0, 397, 0x059c, 412, 0x0598, 
 83         428, 0x0594, 443, 0x0590, 459, 0x058c, 474, 0x0588, 490, 0x0584, 
 84         506, 0x0580, 521, 0x057c, 537, 0x0578, 553, 0x0574, 568, 0x0570, 
 85         584, 0x056c, 600, 0x0568, 616, 0x0564, 632, 0x0560, 647, 0x055c, 
 86         663, 0x0558, 679, 0x0554, 695, 0x0550, 711, 0x054c, 727, 0x0548, 
 87         743, 0x0544, 759, 0x0540, 776, 0x053c, 792, 0x0538, 808, 0x0534, 
 88         824, 0x0530, 840, 0x052c, 857, 0x0528, 873, 0x0524, 889, 0x0520, 
 89         906, 0x051c, 922, 0x0518, 938, 0x0514, 955, 0x0510, 971, 0x050c, 
 90         988, 0x0508, 1004, 0x0504, 1021, 0x0500, 1037, 0x04fc, 1054, 0x04f8, 
 91         1071, 0x04f4, 1087, 0x04f0, 1104, 0x04ec, 1121, 0x04e8, 1138, 0x04e4, 
 92         1154, 0x04e0, 1171, 0x04dc, 1188, 0x04d8, 1205, 0x04d4, 1222, 0x04d0, 
 93         1239, 0x04cc, 1256, 0x04c8, 1273, 0x04c4, 1290, 0x04c0, 1307, 0x04bc, 
 94         1324, 0x04b8, 1341, 0x04b4, 1358, 0x04b0, 1376, 0x04ac, 1393, 0x04a8, 
 95         1410, 0x04a4, 1427, 0x04a0, 1445, 0x049c, 1462, 0x0498, 1479, 0x0494, 
 96         1497, 0x0490, 1514, 0x048c, 1532, 0x0488, 1549, 0x0484, 1567, 0x0480, 
 97         1584, 0x047c, 1602, 0x0478, 1620, 0x0474, 1637, 0x0470, 1655, 0x046c, 
 98         1673, 0x0468, 1690, 0x0464, 1708, 0x0460, 1726, 0x045c, 1744, 0x0458, 
 99         1762, 0x0454, 1780, 0x0450, 1798, 0x044c, 1816, 0x0448, 1834, 0x0444, 
100         1852, 0x0440, 1870, 0x043c, 1888, 0x0438, 1906, 0x0434, 1924, 0x0430, 
101         1943, 0x042c, 1961, 0x0428, 1979, 0x0424, 1997, 0x0420, 2016, 0x041c, 
102         2034, 0x0418, 2053, 0x0414, 2071, 0x0410, 2089, 0x040c, 2108, 0x0408, 
103         2127, 0x0404, 2145, 0x0400, 2164, 0x03fc, 2182, 0x03f8, 2201, 0x03f4, 
104         2220, 0x03f0, 2239, 0x03ec, 2257, 0x03e8, 2276, 0x03e4, 2295, 0x03e0, 
105         2314, 0x03dc, 2333, 0x03d8, 2352, 0x03d4, 2371, 0x03d0, 2390, 0x03cc, 
106         2409, 0x03c8, 2428, 0x03c4, 2447, 0x03c0, 2466, 0x03bc, 2485, 0x03b8, 
107         2505, 0x03b4, 2524, 0x03b0, 2543, 0x03ac, 2562, 0x03a8, 2582, 0x03a4, 
108         2601, 0x03a0, 2621, 0x039c, 2640, 0x0398, 2660, 0x0394, 2679, 0x0390, 
109         2699, 0x038c, 2718, 0x0388, 2738, 0x0384, 2758, 0x0380, 2777, 0x037c, 
110         2797, 0x0378, 2817, 0x0374, 2837, 0x0370, 2857, 0x036c, 2876, 0x0368, 
111         2896, 0x0364, 2916, 0x0360, 2936, 0x035c, 2956, 0x0358, 2976, 0x0354, 
112         2997, 0x0350, 3017, 0x034c, 3037, 0x0348, 3057, 0x0344, 3077, 0x0340, 
113         3098, 0x033c, 3118, 0x0338, 3138, 0x0334, 3159, 0x0330, 3179, 0x032c, 
114         3200, 0x0328, 3220, 0x0324, 3241, 0x0320, 3261, 0x031c, 3282, 0x0318, 
115         3303, 0x0314, 3323, 0x0310, 3344, 0x030c, 3365, 0x0308, 3386, 0x0304, 
116         3406, 0x0300, 3427, 0x02fc, 3448, 0x02f8, 3469, 0x02f4, 3490, 0x02f0, 
117         3511, 0x02ec, 3532, 0x02e8, 3553, 0x02e4, 3575, 0x02e0, 3596, 0x02dc, 
118         3617, 0x02d8, 3638, 0x02d4, 3660, 0x02d0, 3681, 0x02cc, 3702, 0x02c8, 
119         3724, 0x02c4, 3745, 0x02c0, 3767, 0x02bc, 3788, 0x02b8, 3810, 0x02b4, 
120         3831, 0x02b0, 3853, 0x02ac, 3875, 0x02a8, 3896, 0x02a4, 3918, 0x02a0, 
121         3940, 0x029c, 3962, 0x0298, 3984, 0x0294, 4006, 0x0290, 4028, 0x028c, 
122         4050, 0x0288, 4072, 0x0284, 4094, 0x0280, 4116, 0x027c, 4138, 0x0278, 
123         4160, 0x0274, 4182, 0x0270, 4205, 0x026c, 4227, 0x0268, 4249, 0x0264, 
124         4272, 0x0260, 4294, 0x025c, 4317, 0x0258, 4339, 0x0254, 4362, 0x0250, 
125         4384, 0x024c, 4407, 0x0248, 4430, 0x0244, 4453, 0x0240, 4475, 0x023c, 
126         4498, 0x0238, 4521, 0x0234, 4544, 0x0230, 4567, 0x022c, 4590, 0x0228, 
127         4613, 0x0224, 4636, 0x0220, 4659, 0x021c, 4682, 0x0218, 4705, 0x0214, 
128         4728, 0x0210, 4752, 0x020c, 4775, 0x0208, 4798, 0x0204, 4822, 0x0200, 
129         4845, 0x01fc, 4869, 0x01f8, 4892, 0x01f4, 4916, 0x01f0, 4939, 0x01ec, 
130         4963, 0x01e8, 4987, 0x01e4, 5010, 0x01e0, 5034, 0x01dc, 5058, 0x01d8, 
131         5082, 0x01d4, 5106, 0x01d0, 5130, 0x01cc, 5154, 0x01c8, 5178, 0x01c4, 
132         5202, 0x01c0, 5226, 0x01bc, 5250, 0x01b8, 5274, 0x01b4, 5299, 0x01b0, 
133         5323, 0x01ac, 5347, 0x01a8, 5372, 0x01a4, 5396, 0x01a0, 5420, 0x019c, 
134         5445, 0x0198, 5469, 0x0194, 5494, 0x0190, 5519, 0x018c, 5543, 0x0188, 
135         5568, 0x0184, 5593, 0x0180, 5618, 0x017c, 5643, 0x0178, 5668, 0x0174, 
136         5692, 0x0170, 5717, 0x016c, 5743, 0x0168, 5768, 0x0164, 5793, 0x0160, 
137         5818, 0x015c, 5843, 0x0158, 5868, 0x0154, 5894, 0x0150, 5919, 0x014c, 
138         5945, 0x0148, 5970, 0x0144, 5995, 0x0140, 6021, 0x013c, 6047, 0x0138, 
139         6072, 0x0134, 6098, 0x0130, 6124, 0x012c, 6149, 0x0128, 6175, 0x0124, 
140         6201, 0x0120, 6227, 0x011c, 6253, 0x0118, 6279, 0x0114, 6305, 0x0110, 
141         6331, 0x010c, 6357, 0x0108, 6384, 0x0104, 6410, 0x0100, 6436, 0x00fc, 
142         6462, 0x00f8, 6489, 0x00f4, 6515, 0x00f0, 6542, 0x00ec, 6568, 0x00e8, 
143         6595, 0x00e4, 6621, 0x00e0, 6648, 0x00dc, 6675, 0x00d8, 6702, 0x00d4, 
144         6728, 0x00d0, 6755, 0x00cc, 6782, 0x00c8, 6809, 0x00c4, 6836, 0x00c0, 
145         6863, 0x00bc, 6890, 0x00b8, 6917, 0x00b4, 6945, 0x00b0, 6972, 0x00ac, 
146         6999, 0x00a8, 7027, 0x00a4, 7054, 0x00a0, 7081, 0x009c, 7109, 0x0098, 
147         7136, 0x0094, 7164, 0x0090, 7192, 0x008c, 7219, 0x0088, 7247, 0x0084, 
148         7275, 0x0080, 7303, 0x007c, 7331, 0x0078, 7359, 0x0074, 7387, 0x0070, 
149         7415, 0x006c, 7443, 0x0068, 7471, 0x0064, 7499, 0x0060, 7527, 0x005c, 
150         7556, 0x0058, 7584, 0x0054, 7613, 0x0050, 7641, 0x004c, 7669, 0x0048, 
151         7698, 0x0044, 7727, 0x0040, 7755, 0x003c, 7784, 0x0038, 7813, 0x0034, 
152         7842, 0x0030, 7870, 0x002c, 7899, 0x0028, 7928, 0x0024, 7957, 0x0020, 
153         7986, 0x001c, 8016, 0x0018, 8045, 0x0014, 8074, 0x0010, 8103, 0x000c, 
154         8133, 0x0008, 8162, 0x0004, 8192, 0x0000
155 };
156 
157 static unsigned short lookup_volume_table( unsigned short value )
158 {
159         /* This code is an optimised version of:
160          *   int i = 0;
161          *   while( volume_table[i*2] < value )
162          *       i++;
163          *   return volume_table[i*2+1];
164          */
165         unsigned short *ptr = log_table;
166         while( *ptr < value )
167                 ptr += 2;
168         return *(ptr+1);
169 }
170 
171 /* this function calculates a 8.8 fixed point logarithmic attenuation
172  * value from a linear volume value in the range 0 to 16384 */
173 static unsigned short log_from_linear( unsigned short value )
174 {
175         if (value >= 16384)
176                 return 0x0000;
177         if (value) {
178                 unsigned short result = 0;
179                 int v, c;
180                 for( c = 0, v = 8192; c < 14; c++, v >>= 1 ) {
181                         if( value >= v ) {
182                                 result += lookup_volume_table( (value - v) << c );
183                                 return result;
184                         }
185                         result += 0x0605;       /* 6.0205 (result of -20*log10(0.5)) */
186                 }
187         }
188         return 0xffff;
189 }
190 
191 /*
192  * Sample handling operations
193  */
194 
195 static void sample_start(trident_t * trident, snd_trident_voice_t * voice, snd_seq_position_t position);
196 static void sample_stop(trident_t * trident, snd_trident_voice_t * voice, snd_seq_stop_mode_t mode);
197 static void sample_freq(trident_t * trident, snd_trident_voice_t * voice, snd_seq_frequency_t freq);
198 static void sample_volume(trident_t * trident, snd_trident_voice_t * voice, snd_seq_ev_volume_t * volume);
199 static void sample_loop(trident_t * trident, snd_trident_voice_t * voice, snd_seq_ev_loop_t * loop);
200 static void sample_pos(trident_t * trident, snd_trident_voice_t * voice, snd_seq_position_t position);
201 static void sample_private1(trident_t * trident, snd_trident_voice_t * voice, unsigned char *data);
202 
203 static snd_trident_sample_ops_t sample_ops =
204 {
205         sample_start,
206         sample_stop,
207         sample_freq,
208         sample_volume,
209         sample_loop,
210         sample_pos,
211         sample_private1
212 };
213 
214 static void snd_trident_simple_init(snd_trident_voice_t * voice)
215 {
216         //voice->handler_wave = interrupt_wave;
217         //voice->handler_volume = interrupt_volume;
218         //voice->handler_effect = interrupt_effect;
219         //voice->volume_change = NULL;
220         voice->sample_ops = &sample_ops;
221 }
222 
223 static void sample_start(trident_t * trident, snd_trident_voice_t * voice, snd_seq_position_t position)
224 {
225         simple_instrument_t *simple;
226         snd_seq_kinstr_t *instr;
227         unsigned long flags;
228         unsigned int loop_start, loop_end, sample_start, sample_end, start_offset;
229         unsigned int value;
230         unsigned int shift = 0;
231 
232         instr = snd_seq_instr_find(trident->synth.ilist, &voice->instr, 0, 1);
233         if (instr == NULL)
234                 return;
235         voice->instr = instr->instr;    /* copy ID to speedup aliases */
236         simple = KINSTR_DATA(instr);
237 
238         spin_lock_irqsave(&trident->reg_lock, flags);
239 
240         if (trident->device == TRIDENT_DEVICE_ID_SI7018)
241                 voice->GVSel = 1;       /* route to Wave volume */
242 
243         voice->CTRL = 0;
244         voice->Alpha = 0;
245         voice->FMS = 0;
246 
247         loop_start = simple->loop_start >> 4;
248         loop_end = simple->loop_end >> 4;
249         sample_start = (simple->start + position) >> 4;
250         if( sample_start >= simple->size )
251                 sample_start = simple->start >> 4;
252         sample_end = simple->size;
253         start_offset = position >> 4;
254 
255         if (simple->format & SIMPLE_WAVE_16BIT) {
256                 voice->CTRL |= 8;
257                 shift++;
258         }
259         if (simple->format & SIMPLE_WAVE_STEREO) {
260                 voice->CTRL |= 4;
261                 shift++;
262         }
263         if (!(simple->format & SIMPLE_WAVE_UNSIGNED))
264                 voice->CTRL |= 2;
265 
266         voice->LBA = simple->address.memory;
267 
268         if (simple->format & SIMPLE_WAVE_LOOP) {
269                 voice->CTRL |= 1;
270                 voice->LBA += loop_start << shift;
271                 if( start_offset >= loop_start ) {
272                         voice->CSO = start_offset - loop_start;
273                         voice->negCSO = 0;
274                 } else {
275                         voice->CSO = loop_start - start_offset;
276                         voice->negCSO = 1;
277                 }
278                 voice->ESO = loop_end - loop_start - 1;
279         } else {
280                 voice->LBA += start_offset << shift;
281                 voice->CSO = sample_start;
282                 voice->ESO = sample_end - 1;
283                 voice->negCSO = 0;
284         }
285 
286         if (voice->flags & SNDRV_TRIDENT_VFLG_RUNNING) {
287                 snd_trident_stop_voice(trident, voice->number);
288                 voice->flags &= ~SNDRV_TRIDENT_VFLG_RUNNING;
289         }
290 
291         /* set CSO sign */
292         value = inl(TRID_REG(trident, T4D_SIGN_CSO_A));
293         if( voice->negCSO ) {
294                 value |= 1 << (voice->number&31);
295         } else {
296                 value &= ~(1 << (voice->number&31));
297         }
298         outl(value,TRID_REG(trident, T4D_SIGN_CSO_A));
299 
300         voice->Attribute = 0;   
301         snd_trident_write_voice_regs(trident, voice);
302         snd_trident_start_voice(trident, voice->number);
303         voice->flags |= SNDRV_TRIDENT_VFLG_RUNNING;
304         spin_unlock_irqrestore(&trident->reg_lock, flags);
305         snd_seq_instr_free_use(trident->synth.ilist, instr);
306 }
307 
308 static void sample_stop(trident_t * trident, snd_trident_voice_t * voice, snd_seq_stop_mode_t mode)
309 {
310         unsigned long flags;
311 
312         if (!(voice->flags & SNDRV_TRIDENT_VFLG_RUNNING))
313                 return;
314 
315         switch (mode) {
316         default:
317                 spin_lock_irqsave(&trident->reg_lock, flags);
318                 snd_trident_stop_voice(trident, voice->number);
319                 voice->flags &= ~SNDRV_TRIDENT_VFLG_RUNNING;
320                 spin_unlock_irqrestore(&trident->reg_lock, flags);
321                 break;
322         case SAMPLE_STOP_LOOP:  /* disable loop only */
323                 voice->CTRL &= ~1;
324                 spin_lock_irqsave(&trident->reg_lock, flags);
325                 outb((unsigned char) voice->number, TRID_REG(trident, T4D_LFO_GC_CIR));
326                 outw((((voice->CTRL << 12) | (voice->EC & 0x0fff)) & 0xffff), CH_GVSEL_PAN_VOL_CTRL_EC);
327                 spin_unlock_irqrestore(&trident->reg_lock, flags);
328                 break;
329         }
330 }
331 
332 static void sample_freq(trident_t * trident, snd_trident_voice_t * voice, snd_seq_frequency_t freq)
333 {
334         unsigned long flags;
335         freq >>= 4;
336 
337         spin_lock_irqsave(&trident->reg_lock, flags);
338         if (freq == 44100)
339                 voice->Delta = 0xeb3;
340         else if (freq == 8000)
341                 voice->Delta = 0x2ab;
342         else if (freq == 48000)
343                 voice->Delta = 0x1000;
344         else
345                 voice->Delta = (((freq << 12) + freq) / 48000) & 0x0000ffff;
346 
347         outb((unsigned char) voice->number, TRID_REG(trident, T4D_LFO_GC_CIR));
348         if (trident->device == TRIDENT_DEVICE_ID_NX) {
349                 outb((unsigned char) voice->Delta, TRID_REG(trident, CH_NX_DELTA_CSO + 3));
350                 outb((unsigned char) (voice->Delta >> 8), TRID_REG(trident, CH_NX_DELTA_ESO + 3));
351         } else {
352                 outw((unsigned short) voice->Delta, TRID_REG(trident, CH_DX_ESO_DELTA));
353         }
354 
355         spin_unlock_irqrestore(&trident->reg_lock, flags);
356 }
357 
358 static void sample_volume(trident_t * trident, snd_trident_voice_t * voice, snd_seq_ev_volume_t * volume)
359 {
360         unsigned long flags;
361         unsigned short value;
362 
363         spin_lock_irqsave(&trident->reg_lock, flags);
364         voice->GVSel = 0;       /* use global music volume */
365         voice->FMC = 0x03;      /* fixme: can we do something useful with FMC? */
366         if (volume->volume >= 0) {
367                 volume->volume &= 0x3fff;
368                 /* linear volume -> logarithmic attenuation conversion
369                  * uses EC register for greater resolution (6.6 bits) than Vol register (5.3 bits)
370                  * Vol register used when additional attenuation is required */
371                 voice->RVol = 0;
372                 voice->CVol = 0;
373                 value = log_from_linear( volume->volume );
374                 voice->Vol = 0;
375                 voice->EC = (value & 0x3fff) >> 2;
376                 if (value > 0x3fff) {
377                         voice->EC |= 0xfc0;
378                         if (value < 0x5f00 )
379                                 voice->Vol = ((value >> 8) - 0x3f) << 5;
380                         else {
381                                 voice->Vol = 0x3ff;
382                                 voice->EC = 0xfff;
383                         }
384                 }
385         }
386         if (volume->lr >= 0) {
387                 volume->lr &= 0x3fff;
388                 /* approximate linear pan by attenuating channels */
389                 if (volume->lr >= 0x2000) {     /* attenuate left (pan right) */
390                         value = 0x3fff - volume->lr;
391                         for (voice->Pan = 0; voice->Pan < 63; voice->Pan++ ) 
392                                 if (value >= pan_table[voice->Pan] )
393                                         break;
394                 } else {                        /* attenuate right (pan left) */
395                         for (voice->Pan = 0; voice->Pan < 63; voice->Pan++ ) 
396                                 if ((unsigned int)volume->lr >= pan_table[voice->Pan] )
397                                         break;
398                         voice->Pan |= 0x40;
399                 }
400         }
401         outb((unsigned char) voice->number, TRID_REG(trident, T4D_LFO_GC_CIR));
402         outl((voice->GVSel << 31) | ((voice->Pan & 0x0000007f) << 24) |
403                  ((voice->Vol & 0x000000ff) << 16) | ((voice->CTRL & 0x0000000f) << 12) |
404                  (voice->EC & 0x00000fff), TRID_REG(trident, CH_GVSEL_PAN_VOL_CTRL_EC));
405         value = ((voice->FMC & 0x03) << 14) | ((voice->RVol & 0x7f) << 7) | (voice->CVol & 0x7f);
406         outw(value, TRID_REG(trident, CH_DX_FMC_RVOL_CVOL));
407         spin_unlock_irqrestore(&trident->reg_lock, flags);
408 }
409 
410 static void sample_loop(trident_t * trident, snd_trident_voice_t * voice, snd_seq_ev_loop_t * loop)
411 {
412         unsigned long flags;
413         simple_instrument_t *simple;
414         snd_seq_kinstr_t *instr;
415         unsigned int loop_start, loop_end;
416 
417         instr = snd_seq_instr_find(trident->synth.ilist, &voice->instr, 0, 1);
418         if (instr == NULL)
419                 return;
420         voice->instr = instr->instr;    /* copy ID to speedup aliases */
421         simple = KINSTR_DATA(instr);
422 
423         loop_start = loop->start >> 4;
424         loop_end = loop->end >> 4;
425 
426         spin_lock_irqsave(&trident->reg_lock, flags);
427 
428         voice->LBA = simple->address.memory + loop_start;
429         voice->CSO = 0;
430         voice->ESO = loop_end - loop_start - 1;
431 
432         outb((unsigned char) voice->number, TRID_REG(trident, T4D_LFO_GC_CIR));
433         outb((voice->LBA >> 16), TRID_REG(trident, CH_LBA + 2));
434         outw((voice->LBA & 0xffff), TRID_REG(trident, CH_LBA));
435         if (trident->device == TRIDENT_DEVICE_ID_NX) {
436                 outb((voice->ESO >> 16), TRID_REG(trident, CH_NX_DELTA_ESO + 2));
437                 outw((voice->ESO & 0xffff), TRID_REG(trident, CH_NX_DELTA_ESO));
438                 outb((voice->CSO >> 16), TRID_REG(trident, CH_NX_DELTA_CSO + 2));
439                 outw((voice->CSO & 0xffff), TRID_REG(trident, CH_NX_DELTA_CSO));
440         } else {
441                 outw((voice->ESO & 0xffff), TRID_REG(trident, CH_DX_ESO_DELTA + 2));
442                 outw((voice->CSO & 0xffff), TRID_REG(trident, CH_DX_CSO_ALPHA_FMS + 2));
443         }
444 
445         spin_unlock_irqrestore(&trident->reg_lock, flags);
446         snd_seq_instr_free_use(trident->synth.ilist, instr);
447 }
448 
449 static void sample_pos(trident_t * trident, snd_trident_voice_t * voice, snd_seq_position_t position)
450 {
451         unsigned long flags;
452         simple_instrument_t *simple;
453         snd_seq_kinstr_t *instr;
454         unsigned int value;
455 
456         instr = snd_seq_instr_find(trident->synth.ilist, &voice->instr, 0, 1);
457         if (instr == NULL)
458                 return;
459         voice->instr = instr->instr;    /* copy ID to speedup aliases */
460         simple = KINSTR_DATA(instr);
461 
462         spin_lock_irqsave(&trident->reg_lock, flags);
463 
464         if (simple->format & SIMPLE_WAVE_LOOP) {
465                 if( position >= simple->loop_start ) {
466                         voice->CSO = (position - simple->loop_start) >> 4;
467                         voice->negCSO = 0;
468                 } else {
469                         voice->CSO = (simple->loop_start - position) >> 4;
470                         voice->negCSO = 1;
471                 }
472         } else {
473                 voice->CSO = position >> 4;
474                 voice->negCSO = 0;
475         }
476 
477         /* set CSO sign */
478         value = inl(TRID_REG(trident, T4D_SIGN_CSO_A));
479         if( voice->negCSO ) {
480                 value |= 1 << (voice->number&31);
481         } else {
482                 value &= ~(1 << (voice->number&31));
483         }
484         outl(value,TRID_REG(trident, T4D_SIGN_CSO_A));
485         
486 
487         outb((unsigned char) voice->number, TRID_REG(trident, T4D_LFO_GC_CIR));
488         if (trident->device == TRIDENT_DEVICE_ID_NX) {
489                 outw((voice->CSO & 0xffff), TRID_REG(trident, CH_NX_DELTA_CSO));
490                 outb((voice->CSO >> 16), TRID_REG(trident, CH_NX_DELTA_CSO + 2));
491         } else {
492                 outw((voice->CSO & 0xffff), TRID_REG(trident, CH_DX_CSO_ALPHA_FMS) + 2);
493         }
494 
495         spin_unlock_irqrestore(&trident->reg_lock, flags);
496         snd_seq_instr_free_use(trident->synth.ilist, instr);
497 }
498 
499 static void sample_private1(trident_t * trident, snd_trident_voice_t * voice, unsigned char *data)
500 {
501 }
502 
503 /*
504  * Memory management / sample loading
505  */
506 
507 static int snd_trident_simple_put_sample(void *private_data, simple_instrument_t * instr,
508                                          char __user *data, long len, int atomic)
509 {
510         trident_t *trident = private_data;
511         int size = instr->size;
512         int shift = 0;
513 
514         if (instr->format & SIMPLE_WAVE_BACKWARD ||
515             instr->format & SIMPLE_WAVE_BIDIR ||
516             instr->format & SIMPLE_WAVE_ULAW) 
517                 return -EINVAL; /* not supported */
518 
519         if (instr->format & SIMPLE_WAVE_16BIT)
520                 shift++;
521         if (instr->format & SIMPLE_WAVE_STEREO)
522                 shift++;
523         size <<= shift;
524 
525         if (trident->synth.current_size + size > trident->synth.max_size)
526                 return -ENOMEM;
527 
528         if (verify_area(VERIFY_READ, data, size))
529                 return -EFAULT;
530 
531         if (trident->tlb.entries) {
532                 snd_util_memblk_t *memblk;
533                 memblk = snd_trident_synth_alloc(trident, size); 
534                 if (memblk == NULL)
535                         return -ENOMEM;
536                 if (snd_trident_synth_copy_from_user(trident, memblk, 0, data, size) ) {
537                         snd_trident_synth_free(trident, memblk);
538                         return -EFAULT;
539                 }
540                 instr->address.ptr = (unsigned char*)memblk;
541                 instr->address.memory = memblk->offset;
542         } else {
543                 struct snd_dma_buffer dmab;
544                 if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(trident->pci),
545                                         size, &dmab) < 0)
546                         return -ENOMEM;
547 
548                 if (copy_from_user(dmab.area, data, size)) {
549                         snd_dma_free_pages(&dmab);
550                         return -EFAULT;
551                 }
552                 instr->address.ptr = dmab.area;
553                 instr->address.memory = dmab.addr;
554         }
555 
556         trident->synth.current_size += size;
557         return 0;
558 }
559 
560 static int snd_trident_simple_get_sample(void *private_data, simple_instrument_t * instr,
561                                          char __user *data, long len, int atomic)
562 {
563         //trident_t *trident = private_data;
564         int size = instr->size;
565         int shift = 0;
566 
567         if (instr->format & SIMPLE_WAVE_16BIT)
568                 shift++;
569         if (instr->format & SIMPLE_WAVE_STEREO)
570                 shift++;
571         size <<= shift;
572 
573         if (verify_area(VERIFY_WRITE, data, size))
574                 return -EFAULT;
575 
576         /* FIXME: not implemented yet */
577 
578         return -EBUSY;
579 }
580 
581 static int snd_trident_simple_remove_sample(void *private_data, simple_instrument_t * instr,
582                                             int atomic)
583 {
584         trident_t *trident = private_data;
585         int size = instr->size;
586 
587         if (instr->format & SIMPLE_WAVE_16BIT)
588                 size <<= 1;
589         if (instr->format & SIMPLE_WAVE_STEREO)
590                 size <<= 1;
591 
592         if (trident->tlb.entries) {
593                 snd_util_memblk_t *memblk = (snd_util_memblk_t*)instr->address.ptr;
594                 if (memblk)
595                         snd_trident_synth_free(trident, memblk);
596                 else
597                         return -EFAULT;
598         } else {
599                 struct snd_dma_buffer dmab;
600                 dmab.dev.type = SNDRV_DMA_TYPE_DEV;
601                 dmab.dev.dev = snd_dma_pci_data(trident->pci);
602                 dmab.area = instr->address.ptr;
603                 dmab.addr = instr->address.memory;
604                 dmab.bytes = size;
605                 snd_dma_free_pages(&dmab);
606         }
607 
608         trident->synth.current_size -= size;
609         if (trident->synth.current_size < 0)    /* shouldn't need this check... */
610                 trident->synth.current_size = 0;
611 
612         return 0;
613 }
614 
615 static void select_instrument(trident_t * trident, snd_trident_voice_t * v)
616 {
617         snd_seq_kinstr_t *instr;
618         instr = snd_seq_instr_find(trident->synth.ilist, &v->instr, 0, 1);
619         if (instr != NULL) {
620                 if (instr->ops) {
621                         if (!strcmp(instr->ops->instr_type, SNDRV_SEQ_INSTR_ID_SIMPLE))
622                                 snd_trident_simple_init(v);
623                 }
624                 snd_seq_instr_free_use(trident->synth.ilist, instr);
625         }
626 }
627 
628 /*
629 
630  */
631 
632 static void event_sample(snd_seq_event_t * ev, snd_trident_port_t * p, snd_trident_voice_t * v)
633 {
634         if (v->sample_ops && v->sample_ops->sample_stop)
635                 v->sample_ops->sample_stop(p->trident, v, SAMPLE_STOP_IMMEDIATELY);
636         v->instr.std = ev->data.sample.param.sample.std;
637         if (v->instr.std & 0xff000000) {        /* private instrument */
638                 v->instr.std &= 0x00ffffff;
639                 v->instr.std |= (unsigned int)ev->source.client << 24;
640         }
641         v->instr.bank = ev->data.sample.param.sample.bank;
642         v->instr.prg = ev->data.sample.param.sample.prg;
643         select_instrument(p->trident, v);
644 }
645 
646 static void event_cluster(snd_seq_event_t * ev, snd_trident_port_t * p, snd_trident_voice_t * v)
647 {
648         if (v->sample_ops && v->sample_ops->sample_stop)
649                 v->sample_ops->sample_stop(p->trident, v, SAMPLE_STOP_IMMEDIATELY);
650         v->instr.cluster = ev->data.sample.param.cluster.cluster;
651         select_instrument(p->trident, v);
652 }
653 
654 static void event_start(snd_seq_event_t * ev, snd_trident_port_t * p, snd_trident_voice_t * v)
655 {
656         if (v->sample_ops && v->sample_ops->sample_start)
657                 v->sample_ops->sample_start(p->trident, v, ev->data.sample.param.position);
658 }
659 
660 static void event_stop(snd_seq_event_t * ev, snd_trident_port_t * p, snd_trident_voice_t * v)
661 {
662         if (v->sample_ops && v->sample_ops->sample_stop)
663                 v->sample_ops->sample_stop(p->trident, v, ev->data.sample.param.stop_mode);
664 }
665 
666 static void event_freq(snd_seq_event_t * ev, snd_trident_port_t * p, snd_trident_voice_t * v)
667 {
668         if (v->sample_ops && v->sample_ops->sample_freq)
669                 v->sample_ops->sample_freq(p->trident, v, ev->data.sample.param.frequency);
670 }
671 
672 static void event_volume(snd_seq_event_t * ev, snd_trident_port_t * p, snd_trident_voice_t * v)
673 {
674         if (v->sample_ops && v->sample_ops->sample_volume)
675                 v->sample_ops->sample_volume(p->trident, v, &ev->data.sample.param.volume);
676 }
677 
678 static void event_loop(snd_seq_event_t * ev, snd_trident_port_t * p, snd_trident_voice_t * v)
679 {
680         if (v->sample_ops && v->sample_ops->sample_loop)
681                 v->sample_ops->sample_loop(p->trident, v, &ev->data.sample.param.loop);
682 }
683 
684 static void event_position(snd_seq_event_t * ev, snd_trident_port_t * p, snd_trident_voice_t * v)
685 {
686         if (v->sample_ops && v->sample_ops->sample_pos)
687                 v->sample_ops->sample_pos(p->trident, v, ev->data.sample.param.position);
688 }
689 
690 static void event_private1(snd_seq_event_t * ev, snd_trident_port_t * p, snd_trident_voice_t * v)
691 {
692         if (v->sample_ops && v->sample_ops->sample_private1)
693                 v->sample_ops->sample_private1(p->trident, v, (unsigned char *) &ev->data.sample.param.raw8);
694 }
695 
696 typedef void (trident_sample_event_handler_t) (snd_seq_event_t * ev, snd_trident_port_t * p, snd_trident_voice_t * v);
697 
698 static trident_sample_event_handler_t *trident_sample_event_handlers[9] =
699 {
700         event_sample,
701         event_cluster,
702         event_start,
703         event_stop,
704         event_freq,
705         event_volume,
706         event_loop,
707         event_position,
708         event_private1
709 };
710 
711 static void snd_trident_sample_event(snd_seq_event_t * ev, snd_trident_port_t * p)
712 {
713         int idx, voice;
714         trident_t *trident = p->trident;
715         snd_trident_voice_t *v;
716         unsigned long flags;
717 
718         idx = ev->type - SNDRV_SEQ_EVENT_SAMPLE;
719         if (idx < 0 || idx > 8)
720                 return;
721         for (voice = 0; voice < 64; voice++) {
722                 v = &trident->synth.voices[voice];
723                 if (v->use && v->client == ev->source.client &&
724                     v->port == ev->source.port &&
725                     v->index == ev->data.sample.channel) {
726                         spin_lock_irqsave(&trident->event_lock, flags);
727                         trident_sample_event_handlers[idx] (ev, p, v);
728                         spin_unlock_irqrestore(&trident->event_lock, flags);
729                         return;
730                 }
731         }
732 }
733 
734 /*
735 
736  */
737 
738 static void snd_trident_synth_free_voices(trident_t * trident, int client, int port)
739 {
740         int idx;
741         snd_trident_voice_t *voice;
742 
743         for (idx = 0; idx < 32; idx++) {
744                 voice = &trident->synth.voices[idx];
745                 if (voice->use && voice->client == client && voice->port == port)
746                         snd_trident_free_voice(trident, voice);
747         }
748 }
749 
750 static int snd_trident_synth_use(void *private_data, snd_seq_port_subscribe_t * info)
751 {
752         snd_trident_port_t *port = (snd_trident_port_t *) private_data;
753         trident_t *trident = port->trident;
754         snd_trident_voice_t *voice;
755         unsigned int idx;
756         unsigned long flags;
757 
758         if (info->voices > 32)
759                 return -EINVAL;
760         spin_lock_irqsave(&trident->reg_lock, flags);
761         for (idx = 0; idx < info->voices; idx++) {
762                 voice = snd_trident_alloc_voice(trident, SNDRV_TRIDENT_VOICE_TYPE_SYNTH, info->sender.client, info->sender.port);
763                 if (voice == NULL) {
764                         snd_trident_synth_free_voices(trident, info->sender.client, info->sender.port);
765                         spin_unlock_irqrestore(&trident->reg_lock, flags);
766                         return -EBUSY;
767                 }
768                 voice->index = idx;
769                 voice->Vol = 0x3ff;
770                 voice->EC = 0x0fff;
771         }
772 #if 0
773         for (idx = 0; idx < info->midi_voices; idx++) {
774                 port->midi_has_voices = 1;
775                 voice = snd_trident_alloc_voice(trident, SNDRV_TRIDENT_VOICE_TYPE_MIDI, info->sender.client, info->sender.port);
776                 if (voice == NULL) {
777                         snd_trident_synth_free_voices(trident, info->sender.client, info->sender.port);
778                         spin_unlock_irqrestore(&trident->reg_lock, flags);
779                         return -EBUSY;
780                 }
781                 voice->Vol = 0x3ff;
782                 voice->EC = 0x0fff;
783         }
784 #endif
785         spin_unlock_irqrestore(&trident->reg_lock, flags);
786         return 0;
787 }
788 
789 static int snd_trident_synth_unuse(void *private_data, snd_seq_port_subscribe_t * info)
790 {
791         snd_trident_port_t *port = (snd_trident_port_t *) private_data;
792         trident_t *trident = port->trident;
793         unsigned long flags;
794 
795         spin_lock_irqsave(&trident->reg_lock, flags);
796         snd_trident_synth_free_voices(trident, info->sender.client, info->sender.port);
797         spin_unlock_irqrestore(&trident->reg_lock, flags);
798         return 0;
799 }
800 
801 /*
802 
803  */
804 
805 static void snd_trident_synth_free_private_instruments(snd_trident_port_t * p, int client)
806 {
807         snd_seq_instr_header_t ifree;
808 
809         memset(&ifree, 0, sizeof(ifree));
810         ifree.cmd = SNDRV_SEQ_INSTR_FREE_CMD_PRIVATE;
811         snd_seq_instr_list_free_cond(p->trident->synth.ilist, &ifree, client, 0);
812 }
813 
814 static int snd_trident_synth_event_input(snd_seq_event_t * ev, int direct, void *private_data, int atomic, int hop)
815 {
816         snd_trident_port_t *p = (snd_trident_port_t *) private_data;
817 
818         if (p == NULL)
819                 return -EINVAL;
820         if (ev->type >= SNDRV_SEQ_EVENT_SAMPLE &&
821             ev->type <= SNDRV_SEQ_EVENT_SAMPLE_PRIVATE1) {
822                 snd_trident_sample_event(ev, p);
823                 return 0;
824         }
825         if (ev->source.client == SNDRV_SEQ_CLIENT_SYSTEM &&
826             ev->source.port == SNDRV_SEQ_PORT_SYSTEM_ANNOUNCE) {
827                 if (ev->type == SNDRV_SEQ_EVENT_CLIENT_EXIT) {
828                         snd_trident_synth_free_private_instruments(p, ev->data.addr.client);
829                         return 0;
830                 }
831         }
832         if (direct) {
833                 if (ev->type >= SNDRV_SEQ_EVENT_INSTR_BEGIN) {
834                         snd_seq_instr_event(&p->trident->synth.simple_ops.kops,
835                                             p->trident->synth.ilist, ev,
836                                             p->trident->synth.seq_client, atomic, hop);
837                         return 0;
838                 }
839         }
840         return 0;
841 }
842 
843 static void snd_trident_synth_instr_notify(void *private_data,
844                                            snd_seq_kinstr_t * instr,
845                                            int what)
846 {
847         int idx;
848         trident_t *trident = private_data;
849         snd_trident_voice_t *pvoice;
850         unsigned long flags;
851 
852         spin_lock_irqsave(&trident->event_lock, flags);
853         for (idx = 0; idx < 64; idx++) {
854                 pvoice = &trident->synth.voices[idx];
855                 if (pvoice->use && !memcmp(&pvoice->instr, &instr->instr, sizeof(pvoice->instr))) {
856                         if (pvoice->sample_ops && pvoice->sample_ops->sample_stop) {
857                                 pvoice->sample_ops->sample_stop(trident, pvoice, SAMPLE_STOP_IMMEDIATELY);
858                         } else {
859                                 snd_trident_stop_voice(trident, pvoice->number);
860                                 pvoice->flags &= ~SNDRV_TRIDENT_VFLG_RUNNING;
861                         }
862                 }
863         }
864         spin_unlock_irqrestore(&trident->event_lock, flags);
865 }
866 
867 /*
868 
869  */
870 
871 static void snd_trident_synth_free_port(void *private_data)
872 {
873         snd_trident_port_t *p = (snd_trident_port_t *) private_data;
874 
875         if (p)
876                 snd_midi_channel_free_set(p->chset);
877 }
878 
879 static int snd_trident_synth_create_port(trident_t * trident, int idx)
880 {
881         snd_trident_port_t *p;
882         snd_seq_port_callback_t callbacks;
883         char name[32];
884         char *str;
885         int result;
886 
887         p = &trident->synth.seq_ports[idx];
888         p->chset = snd_midi_channel_alloc_set(16);
889         if (p->chset == NULL)
890                 return -ENOMEM;
891         p->chset->private_data = p;
892         p->trident = trident;
893         p->client = trident->synth.seq_client;
894 
895         memset(&callbacks, 0, sizeof(callbacks));
896         callbacks.owner = THIS_MODULE;
897         callbacks.use = snd_trident_synth_use;
898         callbacks.unuse = snd_trident_synth_unuse;
899         callbacks.event_input = snd_trident_synth_event_input;
900         callbacks.private_free = snd_trident_synth_free_port;
901         callbacks.private_data = p;
902 
903         str = "???";
904         switch (trident->device) {
905         case TRIDENT_DEVICE_ID_DX:      str = "Trident 4DWave-DX"; break;
906         case TRIDENT_DEVICE_ID_NX:      str = "Trident 4DWave-NX"; break;
907         case TRIDENT_DEVICE_ID_SI7018:  str = "SiS 7018"; break;
908         }
909         sprintf(name, "%s port %i", str, idx);
910         p->chset->port = snd_seq_event_port_attach(trident->synth.seq_client,
911                                                    &callbacks,
912                                                    SNDRV_SEQ_PORT_CAP_WRITE | SNDRV_SEQ_PORT_CAP_SUBS_WRITE,
913                                                    SNDRV_SEQ_PORT_TYPE_DIRECT_SAMPLE |
914                                                    SNDRV_SEQ_PORT_TYPE_SYNTH,
915                                                    16, 0,
916                                                    name);
917         if (p->chset->port < 0) {
918                 result = p->chset->port;
919                 snd_trident_synth_free_port(p);
920                 return result;
921         }
922         p->port = p->chset->port;
923         return 0;
924 }
925 
926 /*
927 
928  */
929 
930 static int snd_trident_synth_new_device(snd_seq_device_t *dev)
931 {
932         trident_t *trident;
933         int client, i;
934         snd_seq_client_callback_t callbacks;
935         snd_seq_client_info_t cinfo;
936         snd_seq_port_subscribe_t sub;
937         snd_simple_ops_t *simpleops;
938         char *str;
939 
940         trident = *(trident_t **)SNDRV_SEQ_DEVICE_ARGPTR(dev);
941         if (trident == NULL)
942                 return -EINVAL;
943 
944         trident->synth.seq_client = -1;
945 
946         /* allocate new client */
947         memset(&callbacks, 0, sizeof(callbacks));
948         callbacks.private_data = trident;
949         callbacks.allow_output = callbacks.allow_input = 1;
950         client = trident->synth.seq_client =
951             snd_seq_create_kernel_client(trident->card, 1, &callbacks);
952         if (client < 0)
953                 return client;
954 
955         /* change name of client */
956         memset(&cinfo, 0, sizeof(cinfo));
957         cinfo.client = client;
958         cinfo.type = KERNEL_CLIENT;
959         str = "???";
960         switch (trident->device) {
961         case TRIDENT_DEVICE_ID_DX:      str = "Trident 4DWave-DX"; break;
962         case TRIDENT_DEVICE_ID_NX:      str = "Trident 4DWave-NX"; break;
963         case TRIDENT_DEVICE_ID_SI7018:  str = "SiS 7018"; break;
964         }
965         sprintf(cinfo.name, str);
966         snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_SET_CLIENT_INFO, &cinfo);
967 
968         for (i = 0; i < 4; i++)
969                 snd_trident_synth_create_port(trident, i);
970 
971         trident->synth.ilist = snd_seq_instr_list_new();
972         if (trident->synth.ilist == NULL) {
973                 snd_seq_delete_kernel_client(client);
974                 trident->synth.seq_client = -1;
975                 return -ENOMEM;
976         }
977         trident->synth.ilist->flags = SNDRV_SEQ_INSTR_FLG_DIRECT;
978 
979         simpleops = &trident->synth.simple_ops;
980         snd_seq_simple_init(simpleops, trident, NULL);
981         simpleops->put_sample = snd_trident_simple_put_sample;
982         simpleops->get_sample = snd_trident_simple_get_sample;
983         simpleops->remove_sample = snd_trident_simple_remove_sample;
984         simpleops->notify = snd_trident_synth_instr_notify;
985 
986         memset(&sub, 0, sizeof(sub));
987         sub.sender.client = SNDRV_SEQ_CLIENT_SYSTEM;
988         sub.sender.port = SNDRV_SEQ_PORT_SYSTEM_ANNOUNCE;
989         sub.dest.client = client;
990         sub.dest.port = 0;
991         snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_SUBSCRIBE_PORT, &sub);
992 
993         return 0;
994 }
995 
996 static int snd_trident_synth_delete_device(snd_seq_device_t *dev)
997 {
998         trident_t *trident;
999 
1000         trident = *(trident_t **)SNDRV_SEQ_DEVICE_ARGPTR(dev);
1001         if (trident == NULL)
1002                 return -EINVAL;
1003 
1004         if (trident->synth.seq_client >= 0) {
1005                 snd_seq_delete_kernel_client(trident->synth.seq_client);
1006                 trident->synth.seq_client = -1;
1007         }
1008         if (trident->synth.ilist)
1009                 snd_seq_instr_list_free(&trident->synth.ilist);
1010         return 0;
1011 }
1012 
1013 static int __init alsa_trident_synth_init(void)
1014 {
1015         static snd_seq_dev_ops_t ops =
1016         {
1017                 snd_trident_synth_new_device,
1018                 snd_trident_synth_delete_device
1019         };
1020 
1021         return snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_TRIDENT, &ops,
1022                                               sizeof(trident_t*));
1023 }
1024 
1025 static void __exit alsa_trident_synth_exit(void)
1026 {
1027         snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_TRIDENT);
1028 }
1029 
1030 module_init(alsa_trident_synth_init)
1031 module_exit(alsa_trident_synth_exit)
1032 
  This page was automatically generated by the LXR engine.