]>
Commit | Line | Data |
---|---|---|
00e5a55c BS |
1 | From 2c5dd6425e72a6e97d9fb9fee9910a58f02d77df Mon Sep 17 00:00:00 2001 |
2 | From: Takashi Iwai <tiwai@suse.de> | |
3 | Subject: ALSA: ca0106 - Add power-amangement support | |
4 | Patch-mainline: | |
5 | References: bnc#447624 | |
6 | ||
7 | Added the missing PM support for snd-ca0106 driver. | |
8 | ||
9 | Signed-off-by: Takashi Iwai <tiwai@suse.de> | |
10 | ||
11 | --- | |
12 | ||
13 | --- a/sound/pci/ca0106/ca0106_mixer.c | |
14 | +++ b/sound/pci/ca0106/ca0106_mixer.c | |
15 | @@ -75,6 +75,84 @@ | |
16 | ||
17 | #include "ca0106.h" | |
18 | ||
19 | +static void ca0106_spdif_enable(struct snd_ca0106 *emu) | |
20 | +{ | |
21 | + unsigned int val; | |
22 | + | |
23 | + if (emu->spdif_enable) { | |
24 | + /* Digital */ | |
25 | + snd_ca0106_ptr_write(emu, SPDIF_SELECT1, 0, 0xf); | |
26 | + snd_ca0106_ptr_write(emu, SPDIF_SELECT2, 0, 0x0b000000); | |
27 | + val = snd_ca0106_ptr_read(emu, CAPTURE_CONTROL, 0) & ~0x1000; | |
28 | + snd_ca0106_ptr_write(emu, CAPTURE_CONTROL, 0, val); | |
29 | + val = inl(emu->port + GPIO) & ~0x101; | |
30 | + outl(val, emu->port + GPIO); | |
31 | + | |
32 | + } else { | |
33 | + /* Analog */ | |
34 | + snd_ca0106_ptr_write(emu, SPDIF_SELECT1, 0, 0xf); | |
35 | + snd_ca0106_ptr_write(emu, SPDIF_SELECT2, 0, 0x000f0000); | |
36 | + val = snd_ca0106_ptr_read(emu, CAPTURE_CONTROL, 0) | 0x1000; | |
37 | + snd_ca0106_ptr_write(emu, CAPTURE_CONTROL, 0, val); | |
38 | + val = inl(emu->port + GPIO) | 0x101; | |
39 | + outl(val, emu->port + GPIO); | |
40 | + } | |
41 | +} | |
42 | + | |
43 | +static void ca0106_set_capture_source(struct snd_ca0106 *emu) | |
44 | +{ | |
45 | + unsigned int val = emu->capture_source; | |
46 | + unsigned int source, mask; | |
47 | + source = (val << 28) | (val << 24) | (val << 20) | (val << 16); | |
48 | + mask = snd_ca0106_ptr_read(emu, CAPTURE_SOURCE, 0) & 0xffff; | |
49 | + snd_ca0106_ptr_write(emu, CAPTURE_SOURCE, 0, source | mask); | |
50 | +} | |
51 | + | |
52 | +static void ca0106_set_i2c_capture_source(struct snd_ca0106 *emu, | |
53 | + unsigned int val, int force) | |
54 | +{ | |
55 | + unsigned int ngain, ogain; | |
56 | + u32 source; | |
57 | + | |
58 | + snd_ca0106_i2c_write(emu, ADC_MUX, 0); /* Mute input */ | |
59 | + ngain = emu->i2c_capture_volume[val][0]; /* Left */ | |
60 | + ogain = emu->i2c_capture_volume[emu->i2c_capture_source][0]; /* Left */ | |
61 | + if (force || ngain != ogain) | |
62 | + snd_ca0106_i2c_write(emu, ADC_ATTEN_ADCL, ngain & 0xff); | |
63 | + ngain = emu->i2c_capture_volume[val][1]; /* Right */ | |
64 | + ogain = emu->i2c_capture_volume[emu->i2c_capture_source][1]; /* Right */ | |
65 | + if (force || ngain != ogain) | |
66 | + snd_ca0106_i2c_write(emu, ADC_ATTEN_ADCR, ngain & 0xff); | |
67 | + source = 1 << val; | |
68 | + snd_ca0106_i2c_write(emu, ADC_MUX, source); /* Set source */ | |
69 | + emu->i2c_capture_source = val; | |
70 | +} | |
71 | + | |
72 | +static void ca0106_set_capture_mic_line_in(struct snd_ca0106 *emu) | |
73 | +{ | |
74 | + u32 tmp; | |
75 | + | |
76 | + if (emu->capture_mic_line_in) { | |
77 | + /* snd_ca0106_i2c_write(emu, ADC_MUX, 0); */ /* Mute input */ | |
78 | + tmp = inl(emu->port+GPIO) & ~0x400; | |
79 | + tmp = tmp | 0x400; | |
80 | + outl(tmp, emu->port+GPIO); | |
81 | + /* snd_ca0106_i2c_write(emu, ADC_MUX, ADC_MUX_MIC); */ | |
82 | + } else { | |
83 | + /* snd_ca0106_i2c_write(emu, ADC_MUX, 0); */ /* Mute input */ | |
84 | + tmp = inl(emu->port+GPIO) & ~0x400; | |
85 | + outl(tmp, emu->port+GPIO); | |
86 | + /* snd_ca0106_i2c_write(emu, ADC_MUX, ADC_MUX_LINEIN); */ | |
87 | + } | |
88 | +} | |
89 | + | |
90 | +static void ca0106_set_spdif_bits(struct snd_ca0106 *emu, int idx) | |
91 | +{ | |
92 | + snd_ca0106_ptr_write(emu, SPCS0 + idx, 0, emu->spdif_bits[idx]); | |
93 | +} | |
94 | + | |
95 | +/* | |
96 | + */ | |
97 | static const DECLARE_TLV_DB_SCALE(snd_ca0106_db_scale1, -5175, 25, 1); | |
98 | static const DECLARE_TLV_DB_SCALE(snd_ca0106_db_scale2, -10350, 50, 1); | |
99 | ||
100 | @@ -95,30 +173,12 @@ static int snd_ca0106_shared_spdif_put(s | |
101 | struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); | |
102 | unsigned int val; | |
103 | int change = 0; | |
104 | - u32 mask; | |
105 | ||
106 | val = !!ucontrol->value.integer.value[0]; | |
107 | change = (emu->spdif_enable != val); | |
108 | if (change) { | |
109 | emu->spdif_enable = val; | |
110 | - if (val) { | |
111 | - /* Digital */ | |
112 | - snd_ca0106_ptr_write(emu, SPDIF_SELECT1, 0, 0xf); | |
113 | - snd_ca0106_ptr_write(emu, SPDIF_SELECT2, 0, 0x0b000000); | |
114 | - snd_ca0106_ptr_write(emu, CAPTURE_CONTROL, 0, | |
115 | - snd_ca0106_ptr_read(emu, CAPTURE_CONTROL, 0) & ~0x1000); | |
116 | - mask = inl(emu->port + GPIO) & ~0x101; | |
117 | - outl(mask, emu->port + GPIO); | |
118 | - | |
119 | - } else { | |
120 | - /* Analog */ | |
121 | - snd_ca0106_ptr_write(emu, SPDIF_SELECT1, 0, 0xf); | |
122 | - snd_ca0106_ptr_write(emu, SPDIF_SELECT2, 0, 0x000f0000); | |
123 | - snd_ca0106_ptr_write(emu, CAPTURE_CONTROL, 0, | |
124 | - snd_ca0106_ptr_read(emu, CAPTURE_CONTROL, 0) | 0x1000); | |
125 | - mask = inl(emu->port + GPIO) | 0x101; | |
126 | - outl(mask, emu->port + GPIO); | |
127 | - } | |
128 | + ca0106_spdif_enable(emu); | |
129 | } | |
130 | return change; | |
131 | } | |
132 | @@ -154,8 +214,6 @@ static int snd_ca0106_capture_source_put | |
133 | struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); | |
134 | unsigned int val; | |
135 | int change = 0; | |
136 | - u32 mask; | |
137 | - u32 source; | |
138 | ||
139 | val = ucontrol->value.enumerated.item[0] ; | |
140 | if (val >= 6) | |
141 | @@ -163,9 +221,7 @@ static int snd_ca0106_capture_source_put | |
142 | change = (emu->capture_source != val); | |
143 | if (change) { | |
144 | emu->capture_source = val; | |
145 | - source = (val << 28) | (val << 24) | (val << 20) | (val << 16); | |
146 | - mask = snd_ca0106_ptr_read(emu, CAPTURE_SOURCE, 0) & 0xffff; | |
147 | - snd_ca0106_ptr_write(emu, CAPTURE_SOURCE, 0, source | mask); | |
148 | + ca0106_set_capture_source(emu); | |
149 | } | |
150 | return change; | |
151 | } | |
152 | @@ -200,9 +256,7 @@ static int snd_ca0106_i2c_capture_source | |
153 | { | |
154 | struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); | |
155 | unsigned int source_id; | |
156 | - unsigned int ngain, ogain; | |
157 | int change = 0; | |
158 | - u32 source; | |
159 | /* If the capture source has changed, | |
160 | * update the capture volume from the cached value | |
161 | * for the particular source. | |
162 | @@ -212,18 +266,7 @@ static int snd_ca0106_i2c_capture_source | |
163 | return -EINVAL; | |
164 | change = (emu->i2c_capture_source != source_id); | |
165 | if (change) { | |
166 | - snd_ca0106_i2c_write(emu, ADC_MUX, 0); /* Mute input */ | |
167 | - ngain = emu->i2c_capture_volume[source_id][0]; /* Left */ | |
168 | - ogain = emu->i2c_capture_volume[emu->i2c_capture_source][0]; /* Left */ | |
169 | - if (ngain != ogain) | |
170 | - snd_ca0106_i2c_write(emu, ADC_ATTEN_ADCL, ((ngain) & 0xff)); | |
171 | - ngain = emu->i2c_capture_volume[source_id][1]; /* Left */ | |
172 | - ogain = emu->i2c_capture_volume[emu->i2c_capture_source][1]; /* Left */ | |
173 | - if (ngain != ogain) | |
174 | - snd_ca0106_i2c_write(emu, ADC_ATTEN_ADCR, ((ngain) & 0xff)); | |
175 | - source = 1 << source_id; | |
176 | - snd_ca0106_i2c_write(emu, ADC_MUX, source); /* Set source */ | |
177 | - emu->i2c_capture_source = source_id; | |
178 | + ca0106_set_i2c_capture_source(emu, source_id, 0); | |
179 | } | |
180 | return change; | |
181 | } | |
182 | @@ -271,7 +314,6 @@ static int snd_ca0106_capture_mic_line_i | |
183 | struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); | |
184 | unsigned int val; | |
185 | int change = 0; | |
186 | - u32 tmp; | |
187 | ||
188 | val = ucontrol->value.enumerated.item[0] ; | |
189 | if (val > 1) | |
190 | @@ -279,18 +321,7 @@ static int snd_ca0106_capture_mic_line_i | |
191 | change = (emu->capture_mic_line_in != val); | |
192 | if (change) { | |
193 | emu->capture_mic_line_in = val; | |
194 | - if (val) { | |
195 | - //snd_ca0106_i2c_write(emu, ADC_MUX, 0); /* Mute input */ | |
196 | - tmp = inl(emu->port+GPIO) & ~0x400; | |
197 | - tmp = tmp | 0x400; | |
198 | - outl(tmp, emu->port+GPIO); | |
199 | - //snd_ca0106_i2c_write(emu, ADC_MUX, ADC_MUX_MIC); | |
200 | - } else { | |
201 | - //snd_ca0106_i2c_write(emu, ADC_MUX, 0); /* Mute input */ | |
202 | - tmp = inl(emu->port+GPIO) & ~0x400; | |
203 | - outl(tmp, emu->port+GPIO); | |
204 | - //snd_ca0106_i2c_write(emu, ADC_MUX, ADC_MUX_LINEIN); | |
205 | - } | |
206 | + ca0106_set_capture_mic_line_in(emu); | |
207 | } | |
208 | return change; | |
209 | } | |
210 | @@ -359,8 +390,8 @@ static int snd_ca0106_spdif_put(struct s | |
211 | (ucontrol->value.iec958.status[3] << 24); | |
212 | change = val != emu->spdif_bits[idx]; | |
213 | if (change) { | |
214 | - snd_ca0106_ptr_write(emu, SPCS0 + idx, 0, val); | |
215 | emu->spdif_bits[idx] = val; | |
216 | + ca0106_set_spdif_bits(emu, idx); | |
217 | } | |
218 | return change; | |
219 | } | |
220 | @@ -773,3 +804,50 @@ int __devinit snd_ca0106_mixer(struct sn | |
221 | return 0; | |
222 | } | |
223 | ||
224 | +#ifdef CONFIG_PM | |
225 | +struct ca0106_vol_tbl { | |
226 | + unsigned int channel_id; | |
227 | + unsigned int reg; | |
228 | +}; | |
229 | + | |
230 | +static struct ca0106_vol_tbl saved_volumes[NUM_SAVED_VOLUMES] = { | |
231 | + { CONTROL_FRONT_CHANNEL, PLAYBACK_VOLUME2 }, | |
232 | + { CONTROL_REAR_CHANNEL, PLAYBACK_VOLUME2 }, | |
233 | + { CONTROL_CENTER_LFE_CHANNEL, PLAYBACK_VOLUME2 }, | |
234 | + { CONTROL_UNKNOWN_CHANNEL, PLAYBACK_VOLUME2 }, | |
235 | + { CONTROL_FRONT_CHANNEL, PLAYBACK_VOLUME1 }, | |
236 | + { CONTROL_REAR_CHANNEL, PLAYBACK_VOLUME1 }, | |
237 | + { CONTROL_CENTER_LFE_CHANNEL, PLAYBACK_VOLUME1 }, | |
238 | + { CONTROL_UNKNOWN_CHANNEL, PLAYBACK_VOLUME1 }, | |
239 | + { 1, CAPTURE_CONTROL }, | |
240 | +}; | |
241 | + | |
242 | +void snd_ca0106_mixer_suspend(struct snd_ca0106 *chip) | |
243 | +{ | |
244 | + int i; | |
245 | + | |
246 | + /* save volumes */ | |
247 | + for (i = 0; i < NUM_SAVED_VOLUMES; i++) | |
248 | + chip->saved_vol[i] = | |
249 | + snd_ca0106_ptr_read(chip, saved_volumes[i].reg, | |
250 | + saved_volumes[i].channel_id); | |
251 | +} | |
252 | + | |
253 | +void snd_ca0106_mixer_resume(struct snd_ca0106 *chip) | |
254 | +{ | |
255 | + int i; | |
256 | + | |
257 | + for (i = 0; i < NUM_SAVED_VOLUMES; i++) | |
258 | + snd_ca0106_ptr_write(chip, saved_volumes[i].reg, | |
259 | + saved_volumes[i].channel_id, | |
260 | + chip->saved_vol[i]); | |
261 | + | |
262 | + ca0106_spdif_enable(chip); | |
263 | + ca0106_set_capture_source(chip); | |
264 | + ca0106_set_i2c_capture_source(chip, chip->i2c_capture_source, 1); | |
265 | + for (i = 0; i < 4; i++) | |
266 | + ca0106_set_spdif_bits(chip, i); | |
267 | + if (chip->details->i2c_adc) | |
268 | + ca0106_set_capture_mic_line_in(chip); | |
269 | +} | |
270 | +#endif /* CONFIG_PM */ | |
271 | --- a/sound/pci/ca0106/ca0106.h | |
272 | +++ b/sound/pci/ca0106/ca0106.h | |
273 | @@ -686,7 +686,7 @@ struct snd_ca0106 { | |
274 | spinlock_t emu_lock; | |
275 | ||
276 | struct snd_ac97 *ac97; | |
277 | - struct snd_pcm *pcm; | |
278 | + struct snd_pcm *pcm[4]; | |
279 | ||
280 | struct snd_ca0106_channel playback_channels[4]; | |
281 | struct snd_ca0106_channel capture_channels[4]; | |
282 | @@ -703,6 +703,11 @@ struct snd_ca0106 { | |
283 | struct snd_ca_midi midi2; | |
284 | ||
285 | u16 spi_dac_reg[16]; | |
286 | + | |
287 | +#ifdef CONFIG_PM | |
288 | +#define NUM_SAVED_VOLUMES 9 | |
289 | + unsigned int saved_vol[NUM_SAVED_VOLUMES]; | |
290 | +#endif | |
291 | }; | |
292 | ||
293 | int snd_ca0106_mixer(struct snd_ca0106 *emu); | |
294 | @@ -721,3 +726,11 @@ int snd_ca0106_i2c_write(struct snd_ca01 | |
295 | ||
296 | int snd_ca0106_spi_write(struct snd_ca0106 * emu, | |
297 | unsigned int data); | |
298 | + | |
299 | +#ifdef CONFIG_PM | |
300 | +void snd_ca0106_mixer_suspend(struct snd_ca0106 *chip); | |
301 | +void snd_ca0106_mixer_resume(struct snd_ca0106 *chip); | |
302 | +#else | |
303 | +#define snd_ca0106_mixer_suspend(chip) do { } while (0) | |
304 | +#define snd_ca0106_mixer_resume(chip) do { } while (0) | |
305 | +#endif | |
306 | --- a/sound/pci/ca0106/ca0106_main.c | |
307 | +++ b/sound/pci/ca0106/ca0106_main.c | |
308 | @@ -848,15 +848,18 @@ static int snd_ca0106_pcm_trigger_playba | |
309 | struct snd_pcm_substream *s; | |
310 | u32 basic = 0; | |
311 | u32 extended = 0; | |
312 | - int running=0; | |
313 | + u32 bits; | |
314 | + int running = 0; | |
315 | ||
316 | switch (cmd) { | |
317 | case SNDRV_PCM_TRIGGER_START: | |
318 | - running=1; | |
319 | + case SNDRV_PCM_TRIGGER_RESUME: | |
320 | + running = 1; | |
321 | break; | |
322 | case SNDRV_PCM_TRIGGER_STOP: | |
323 | + case SNDRV_PCM_TRIGGER_SUSPEND: | |
324 | default: | |
325 | - running=0; | |
326 | + running = 0; | |
327 | break; | |
328 | } | |
329 | snd_pcm_group_for_each_entry(s, substream) { | |
330 | @@ -866,22 +869,32 @@ static int snd_ca0106_pcm_trigger_playba | |
331 | runtime = s->runtime; | |
332 | epcm = runtime->private_data; | |
333 | channel = epcm->channel_id; | |
334 | - //snd_printk("channel=%d\n",channel); | |
335 | + /* snd_printk("channel=%d\n",channel); */ | |
336 | epcm->running = running; | |
337 | - basic |= (0x1<<channel); | |
338 | - extended |= (0x10<<channel); | |
339 | + basic |= (0x1 << channel); | |
340 | + extended |= (0x10 << channel); | |
341 | snd_pcm_trigger_done(s, substream); | |
342 | } | |
343 | - //snd_printk("basic=0x%x, extended=0x%x\n",basic, extended); | |
344 | + /* snd_printk("basic=0x%x, extended=0x%x\n",basic, extended); */ | |
345 | ||
346 | switch (cmd) { | |
347 | case SNDRV_PCM_TRIGGER_START: | |
348 | - snd_ca0106_ptr_write(emu, EXTENDED_INT_MASK, 0, snd_ca0106_ptr_read(emu, EXTENDED_INT_MASK, 0) | (extended)); | |
349 | - snd_ca0106_ptr_write(emu, BASIC_INTERRUPT, 0, snd_ca0106_ptr_read(emu, BASIC_INTERRUPT, 0)|(basic)); | |
350 | + case SNDRV_PCM_TRIGGER_RESUME: | |
351 | + bits = snd_ca0106_ptr_read(emu, EXTENDED_INT_MASK, 0); | |
352 | + bits |= extended; | |
353 | + snd_ca0106_ptr_write(emu, EXTENDED_INT_MASK, 0, bits); | |
354 | + bits = snd_ca0106_ptr_read(emu, BASIC_INTERRUPT, 0); | |
355 | + bits |= basic; | |
356 | + snd_ca0106_ptr_write(emu, BASIC_INTERRUPT, 0, bits); | |
357 | break; | |
358 | case SNDRV_PCM_TRIGGER_STOP: | |
359 | - snd_ca0106_ptr_write(emu, BASIC_INTERRUPT, 0, snd_ca0106_ptr_read(emu, BASIC_INTERRUPT, 0) & ~(basic)); | |
360 | - snd_ca0106_ptr_write(emu, EXTENDED_INT_MASK, 0, snd_ca0106_ptr_read(emu, EXTENDED_INT_MASK, 0) & ~(extended)); | |
361 | + case SNDRV_PCM_TRIGGER_SUSPEND: | |
362 | + bits = snd_ca0106_ptr_read(emu, BASIC_INTERRUPT, 0); | |
363 | + bits &= ~basic; | |
364 | + snd_ca0106_ptr_write(emu, BASIC_INTERRUPT, 0, bits); | |
365 | + bits = snd_ca0106_ptr_read(emu, EXTENDED_INT_MASK, 0); | |
366 | + bits &= ~extended; | |
367 | + snd_ca0106_ptr_write(emu, EXTENDED_INT_MASK, 0, bits); | |
368 | break; | |
369 | default: | |
370 | result = -EINVAL; | |
371 | @@ -1104,21 +1117,13 @@ static int snd_ca0106_ac97(struct snd_ca | |
372 | return snd_ac97_mixer(pbus, &ac97, &chip->ac97); | |
373 | } | |
374 | ||
375 | +static void ca0106_stop_chip(struct snd_ca0106 *chip); | |
376 | + | |
377 | static int snd_ca0106_free(struct snd_ca0106 *chip) | |
378 | { | |
379 | - if (chip->res_port != NULL) { /* avoid access to already used hardware */ | |
380 | - // disable interrupts | |
381 | - snd_ca0106_ptr_write(chip, BASIC_INTERRUPT, 0, 0); | |
382 | - outl(0, chip->port + INTE); | |
383 | - snd_ca0106_ptr_write(chip, EXTENDED_INT_MASK, 0, 0); | |
384 | - udelay(1000); | |
385 | - // disable audio | |
386 | - //outl(HCFG_LOCKSOUNDCACHE, chip->port + HCFG); | |
387 | - outl(0, chip->port + HCFG); | |
388 | - /* FIXME: We need to stop and DMA transfers here. | |
389 | - * But as I am not sure how yet, we cannot from the dma pages. | |
390 | - * So we can fix: snd-malloc: Memory leak? pages not freed = 8 | |
391 | - */ | |
392 | + if (chip->res_port != NULL) { | |
393 | + /* avoid access to already used hardware */ | |
394 | + ca0106_stop_chip(chip); | |
395 | } | |
396 | if (chip->irq >= 0) | |
397 | free_irq(chip->irq, chip); | |
398 | @@ -1204,15 +1209,14 @@ static irqreturn_t snd_ca0106_interrupt( | |
399 | return IRQ_HANDLED; | |
400 | } | |
401 | ||
402 | -static int __devinit snd_ca0106_pcm(struct snd_ca0106 *emu, int device, struct snd_pcm **rpcm) | |
403 | +static int __devinit snd_ca0106_pcm(struct snd_ca0106 *emu, int device) | |
404 | { | |
405 | struct snd_pcm *pcm; | |
406 | struct snd_pcm_substream *substream; | |
407 | int err; | |
408 | ||
409 | - if (rpcm) | |
410 | - *rpcm = NULL; | |
411 | - if ((err = snd_pcm_new(emu->card, "ca0106", device, 1, 1, &pcm)) < 0) | |
412 | + err = snd_pcm_new(emu->card, "ca0106", device, 1, 1, &pcm); | |
413 | + if (err < 0) | |
414 | return err; | |
415 | ||
416 | pcm->private_data = emu; | |
417 | @@ -1239,7 +1243,6 @@ static int __devinit snd_ca0106_pcm(stru | |
418 | pcm->info_flags = 0; | |
419 | pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX; | |
420 | strcpy(pcm->name, "CA0106"); | |
421 | - emu->pcm = pcm; | |
422 | ||
423 | for(substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; | |
424 | substream; | |
425 | @@ -1261,8 +1264,7 @@ static int __devinit snd_ca0106_pcm(stru | |
426 | return err; | |
427 | } | |
428 | ||
429 | - if (rpcm) | |
430 | - *rpcm = pcm; | |
431 | + emu->pcm[device] = pcm; | |
432 | ||
433 | return 0; | |
434 | } | |
435 | @@ -1302,89 +1304,10 @@ static unsigned int i2c_adc_init[][2] = | |
436 | { 0x15, ADC_MUX_LINEIN }, /* ADC Mixer control */ | |
437 | }; | |
438 | ||
439 | -static int __devinit snd_ca0106_create(int dev, struct snd_card *card, | |
440 | - struct pci_dev *pci, | |
441 | - struct snd_ca0106 **rchip) | |
442 | +static void ca0106_init_chip(struct snd_ca0106 *chip, int resume) | |
443 | { | |
444 | - struct snd_ca0106 *chip; | |
445 | - struct snd_ca0106_details *c; | |
446 | - int err; | |
447 | int ch; | |
448 | - static struct snd_device_ops ops = { | |
449 | - .dev_free = snd_ca0106_dev_free, | |
450 | - }; | |
451 | - | |
452 | - *rchip = NULL; | |
453 | - | |
454 | - if ((err = pci_enable_device(pci)) < 0) | |
455 | - return err; | |
456 | - if (pci_set_dma_mask(pci, DMA_32BIT_MASK) < 0 || | |
457 | - pci_set_consistent_dma_mask(pci, DMA_32BIT_MASK) < 0) { | |
458 | - printk(KERN_ERR "error to set 32bit mask DMA\n"); | |
459 | - pci_disable_device(pci); | |
460 | - return -ENXIO; | |
461 | - } | |
462 | - | |
463 | - chip = kzalloc(sizeof(*chip), GFP_KERNEL); | |
464 | - if (chip == NULL) { | |
465 | - pci_disable_device(pci); | |
466 | - return -ENOMEM; | |
467 | - } | |
468 | - | |
469 | - chip->card = card; | |
470 | - chip->pci = pci; | |
471 | - chip->irq = -1; | |
472 | - | |
473 | - spin_lock_init(&chip->emu_lock); | |
474 | - | |
475 | - chip->port = pci_resource_start(pci, 0); | |
476 | - if ((chip->res_port = request_region(chip->port, 0x20, | |
477 | - "snd_ca0106")) == NULL) { | |
478 | - snd_ca0106_free(chip); | |
479 | - printk(KERN_ERR "cannot allocate the port\n"); | |
480 | - return -EBUSY; | |
481 | - } | |
482 | - | |
483 | - if (request_irq(pci->irq, snd_ca0106_interrupt, | |
484 | - IRQF_SHARED, "snd_ca0106", chip)) { | |
485 | - snd_ca0106_free(chip); | |
486 | - printk(KERN_ERR "cannot grab irq\n"); | |
487 | - return -EBUSY; | |
488 | - } | |
489 | - chip->irq = pci->irq; | |
490 | - | |
491 | - /* This stores the periods table. */ | |
492 | - if(snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci), 1024, &chip->buffer) < 0) { | |
493 | - snd_ca0106_free(chip); | |
494 | - return -ENOMEM; | |
495 | - } | |
496 | - | |
497 | - pci_set_master(pci); | |
498 | - /* read serial */ | |
499 | - pci_read_config_dword(pci, PCI_SUBSYSTEM_VENDOR_ID, &chip->serial); | |
500 | - pci_read_config_word(pci, PCI_SUBSYSTEM_ID, &chip->model); | |
501 | -#if 1 | |
502 | - printk(KERN_INFO "snd-ca0106: Model %04x Rev %08x Serial %08x\n", chip->model, | |
503 | - pci->revision, chip->serial); | |
504 | -#endif | |
505 | - strcpy(card->driver, "CA0106"); | |
506 | - strcpy(card->shortname, "CA0106"); | |
507 | - | |
508 | - for (c = ca0106_chip_details; c->serial; c++) { | |
509 | - if (subsystem[dev]) { | |
510 | - if (c->serial == subsystem[dev]) | |
511 | - break; | |
512 | - } else if (c->serial == chip->serial) | |
513 | - break; | |
514 | - } | |
515 | - chip->details = c; | |
516 | - if (subsystem[dev]) { | |
517 | - printk(KERN_INFO "snd-ca0106: Sound card name=%s, subsystem=0x%x. Forced to subsystem=0x%x\n", | |
518 | - c->name, chip->serial, subsystem[dev]); | |
519 | - } | |
520 | - | |
521 | - sprintf(card->longname, "%s at 0x%lx irq %i", | |
522 | - c->name, chip->port, chip->irq); | |
523 | + unsigned int def_bits; | |
524 | ||
525 | outl(0, chip->port + INTE); | |
526 | ||
527 | @@ -1402,31 +1325,22 @@ static int __devinit snd_ca0106_create(i | |
528 | * AN = 0 (Audio data) | |
529 | * P = 0 (Consumer) | |
530 | */ | |
531 | - snd_ca0106_ptr_write(chip, SPCS0, 0, | |
532 | - chip->spdif_bits[0] = | |
533 | - SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 | | |
534 | - SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC | | |
535 | - SPCS_GENERATIONSTATUS | 0x00001200 | | |
536 | - 0x00000000 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT); | |
537 | + def_bits = | |
538 | + SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 | | |
539 | + SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC | | |
540 | + SPCS_GENERATIONSTATUS | 0x00001200 | | |
541 | + 0x00000000 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT; | |
542 | + if (!resume) { | |
543 | + chip->spdif_bits[0] = def_bits; | |
544 | + chip->spdif_bits[1] = def_bits; | |
545 | + chip->spdif_bits[2] = def_bits; | |
546 | + chip->spdif_bits[3] = def_bits; | |
547 | + } | |
548 | /* Only SPCS1 has been tested */ | |
549 | - snd_ca0106_ptr_write(chip, SPCS1, 0, | |
550 | - chip->spdif_bits[1] = | |
551 | - SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 | | |
552 | - SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC | | |
553 | - SPCS_GENERATIONSTATUS | 0x00001200 | | |
554 | - 0x00000000 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT); | |
555 | - snd_ca0106_ptr_write(chip, SPCS2, 0, | |
556 | - chip->spdif_bits[2] = | |
557 | - SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 | | |
558 | - SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC | | |
559 | - SPCS_GENERATIONSTATUS | 0x00001200 | | |
560 | - 0x00000000 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT); | |
561 | - snd_ca0106_ptr_write(chip, SPCS3, 0, | |
562 | - chip->spdif_bits[3] = | |
563 | - SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 | | |
564 | - SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC | | |
565 | - SPCS_GENERATIONSTATUS | 0x00001200 | | |
566 | - 0x00000000 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT); | |
567 | + snd_ca0106_ptr_write(chip, SPCS1, 0, chip->spdif_bits[1]); | |
568 | + snd_ca0106_ptr_write(chip, SPCS0, 0, chip->spdif_bits[0]); | |
569 | + snd_ca0106_ptr_write(chip, SPCS2, 0, chip->spdif_bits[2]); | |
570 | + snd_ca0106_ptr_write(chip, SPCS3, 0, chip->spdif_bits[3]); | |
571 | ||
572 | snd_ca0106_ptr_write(chip, PLAYBACK_MUTE, 0, 0x00fc0000); | |
573 | snd_ca0106_ptr_write(chip, CAPTURE_MUTE, 0, 0x00fc0000); | |
574 | @@ -1434,92 +1348,124 @@ static int __devinit snd_ca0106_create(i | |
575 | /* Write 0x8000 to AC97_REC_GAIN to mute it. */ | |
576 | outb(AC97_REC_GAIN, chip->port + AC97ADDRESS); | |
577 | outw(0x8000, chip->port + AC97DATA); | |
578 | -#if 0 | |
579 | +#if 0 /* FIXME: what are these? */ | |
580 | snd_ca0106_ptr_write(chip, SPCS0, 0, 0x2108006); | |
581 | snd_ca0106_ptr_write(chip, 0x42, 0, 0x2108006); | |
582 | snd_ca0106_ptr_write(chip, 0x43, 0, 0x2108006); | |
583 | snd_ca0106_ptr_write(chip, 0x44, 0, 0x2108006); | |
584 | #endif | |
585 | ||
586 | - //snd_ca0106_ptr_write(chip, SPDIF_SELECT2, 0, 0xf0f003f); /* OSS drivers set this. */ | |
587 | + /* OSS drivers set this. */ | |
588 | + /* snd_ca0106_ptr_write(chip, SPDIF_SELECT2, 0, 0xf0f003f); */ | |
589 | + | |
590 | /* Analog or Digital output */ | |
591 | snd_ca0106_ptr_write(chip, SPDIF_SELECT1, 0, 0xf); | |
592 | - snd_ca0106_ptr_write(chip, SPDIF_SELECT2, 0, 0x000f0000); /* 0x0b000000 for digital, 0x000b0000 for analog, from win2000 drivers. Use 0x000f0000 for surround71 */ | |
593 | + /* 0x0b000000 for digital, 0x000b0000 for analog, from win2000 drivers. | |
594 | + * Use 0x000f0000 for surround71 | |
595 | + */ | |
596 | + snd_ca0106_ptr_write(chip, SPDIF_SELECT2, 0, 0x000f0000); | |
597 | + | |
598 | chip->spdif_enable = 0; /* Set digital SPDIF output off */ | |
599 | - //snd_ca0106_ptr_write(chip, 0x45, 0, 0); /* Analogue out */ | |
600 | - //snd_ca0106_ptr_write(chip, 0x45, 0, 0xf00); /* Digital out */ | |
601 | + /*snd_ca0106_ptr_write(chip, 0x45, 0, 0);*/ /* Analogue out */ | |
602 | + /*snd_ca0106_ptr_write(chip, 0x45, 0, 0xf00);*/ /* Digital out */ | |
603 | + | |
604 | + /* goes to 0x40c80000 when doing SPDIF IN/OUT */ | |
605 | + snd_ca0106_ptr_write(chip, CAPTURE_CONTROL, 0, 0x40c81000); | |
606 | + /* (Mute) CAPTURE feedback into PLAYBACK volume. | |
607 | + * Only lower 16 bits matter. | |
608 | + */ | |
609 | + snd_ca0106_ptr_write(chip, CAPTURE_CONTROL, 1, 0xffffffff); | |
610 | + /* SPDIF IN Volume */ | |
611 | + snd_ca0106_ptr_write(chip, CAPTURE_CONTROL, 2, 0x30300000); | |
612 | + /* SPDIF IN Volume, 0x70 = (vol & 0x3f) | 0x40 */ | |
613 | + snd_ca0106_ptr_write(chip, CAPTURE_CONTROL, 3, 0x00700000); | |
614 | ||
615 | - snd_ca0106_ptr_write(chip, CAPTURE_CONTROL, 0, 0x40c81000); /* goes to 0x40c80000 when doing SPDIF IN/OUT */ | |
616 | - snd_ca0106_ptr_write(chip, CAPTURE_CONTROL, 1, 0xffffffff); /* (Mute) CAPTURE feedback into PLAYBACK volume. Only lower 16 bits matter. */ | |
617 | - snd_ca0106_ptr_write(chip, CAPTURE_CONTROL, 2, 0x30300000); /* SPDIF IN Volume */ | |
618 | - snd_ca0106_ptr_write(chip, CAPTURE_CONTROL, 3, 0x00700000); /* SPDIF IN Volume, 0x70 = (vol & 0x3f) | 0x40 */ | |
619 | snd_ca0106_ptr_write(chip, PLAYBACK_ROUTING1, 0, 0x32765410); | |
620 | snd_ca0106_ptr_write(chip, PLAYBACK_ROUTING2, 0, 0x76767676); | |
621 | snd_ca0106_ptr_write(chip, CAPTURE_ROUTING1, 0, 0x32765410); | |
622 | snd_ca0106_ptr_write(chip, CAPTURE_ROUTING2, 0, 0x76767676); | |
623 | - for(ch = 0; ch < 4; ch++) { | |
624 | - snd_ca0106_ptr_write(chip, CAPTURE_VOLUME1, ch, 0x30303030); /* Only high 16 bits matter */ | |
625 | + | |
626 | + for (ch = 0; ch < 4; ch++) { | |
627 | + /* Only high 16 bits matter */ | |
628 | + snd_ca0106_ptr_write(chip, CAPTURE_VOLUME1, ch, 0x30303030); | |
629 | snd_ca0106_ptr_write(chip, CAPTURE_VOLUME2, ch, 0x30303030); | |
630 | - //snd_ca0106_ptr_write(chip, PLAYBACK_VOLUME1, ch, 0x40404040); /* Mute */ | |
631 | - //snd_ca0106_ptr_write(chip, PLAYBACK_VOLUME2, ch, 0x40404040); /* Mute */ | |
632 | - snd_ca0106_ptr_write(chip, PLAYBACK_VOLUME1, ch, 0xffffffff); /* Mute */ | |
633 | - snd_ca0106_ptr_write(chip, PLAYBACK_VOLUME2, ch, 0xffffffff); /* Mute */ | |
634 | +#if 0 /* Mute */ | |
635 | + snd_ca0106_ptr_write(chip, PLAYBACK_VOLUME1, ch, 0x40404040); | |
636 | + snd_ca0106_ptr_write(chip, PLAYBACK_VOLUME2, ch, 0x40404040); | |
637 | + snd_ca0106_ptr_write(chip, PLAYBACK_VOLUME1, ch, 0xffffffff); | |
638 | + snd_ca0106_ptr_write(chip, PLAYBACK_VOLUME2, ch, 0xffffffff); | |
639 | +#endif | |
640 | } | |
641 | if (chip->details->i2c_adc == 1) { | |
642 | /* Select MIC, Line in, TAD in, AUX in */ | |
643 | snd_ca0106_ptr_write(chip, CAPTURE_SOURCE, 0x0, 0x333300e4); | |
644 | /* Default to CAPTURE_SOURCE to i2s in */ | |
645 | - chip->capture_source = 3; | |
646 | + if (!resume) | |
647 | + chip->capture_source = 3; | |
648 | } else if (chip->details->ac97 == 1) { | |
649 | /* Default to AC97 in */ | |
650 | snd_ca0106_ptr_write(chip, CAPTURE_SOURCE, 0x0, 0x444400e4); | |
651 | /* Default to CAPTURE_SOURCE to AC97 in */ | |
652 | - chip->capture_source = 4; | |
653 | + if (!resume) | |
654 | + chip->capture_source = 4; | |
655 | } else { | |
656 | /* Select MIC, Line in, TAD in, AUX in */ | |
657 | snd_ca0106_ptr_write(chip, CAPTURE_SOURCE, 0x0, 0x333300e4); | |
658 | /* Default to Set CAPTURE_SOURCE to i2s in */ | |
659 | - chip->capture_source = 3; | |
660 | + if (!resume) | |
661 | + chip->capture_source = 3; | |
662 | } | |
663 | ||
664 | - if (chip->details->gpio_type == 2) { /* The SB0438 use GPIO differently. */ | |
665 | - /* FIXME: Still need to find out what the other GPIO bits do. E.g. For digital spdif out. */ | |
666 | + if (chip->details->gpio_type == 2) { | |
667 | + /* The SB0438 use GPIO differently. */ | |
668 | + /* FIXME: Still need to find out what the other GPIO bits do. | |
669 | + * E.g. For digital spdif out. | |
670 | + */ | |
671 | outl(0x0, chip->port+GPIO); | |
672 | - //outl(0x00f0e000, chip->port+GPIO); /* Analog */ | |
673 | + /* outl(0x00f0e000, chip->port+GPIO); */ /* Analog */ | |
674 | outl(0x005f5301, chip->port+GPIO); /* Analog */ | |
675 | - } else if (chip->details->gpio_type == 1) { /* The SB0410 and SB0413 use GPIO differently. */ | |
676 | - /* FIXME: Still need to find out what the other GPIO bits do. E.g. For digital spdif out. */ | |
677 | + } else if (chip->details->gpio_type == 1) { | |
678 | + /* The SB0410 and SB0413 use GPIO differently. */ | |
679 | + /* FIXME: Still need to find out what the other GPIO bits do. | |
680 | + * E.g. For digital spdif out. | |
681 | + */ | |
682 | outl(0x0, chip->port+GPIO); | |
683 | - //outl(0x00f0e000, chip->port+GPIO); /* Analog */ | |
684 | + /* outl(0x00f0e000, chip->port+GPIO); */ /* Analog */ | |
685 | outl(0x005f5301, chip->port+GPIO); /* Analog */ | |
686 | } else { | |
687 | outl(0x0, chip->port+GPIO); | |
688 | outl(0x005f03a3, chip->port+GPIO); /* Analog */ | |
689 | - //outl(0x005f02a2, chip->port+GPIO); /* SPDIF */ | |
690 | + /* outl(0x005f02a2, chip->port+GPIO); */ /* SPDIF */ | |
691 | } | |
692 | snd_ca0106_intr_enable(chip, 0x105); /* Win2000 uses 0x1e0 */ | |
693 | ||
694 | - //outl(HCFG_LOCKSOUNDCACHE|HCFG_AUDIOENABLE, chip->port+HCFG); | |
695 | - //outl(0x00001409, chip->port+HCFG); /* 0x1000 causes AC3 to fails. Maybe it effects 24 bit output. */ | |
696 | - //outl(0x00000009, chip->port+HCFG); | |
697 | - outl(HCFG_AC97 | HCFG_AUDIOENABLE, chip->port+HCFG); /* AC97 2.0, Enable outputs. */ | |
698 | + /* outl(HCFG_LOCKSOUNDCACHE|HCFG_AUDIOENABLE, chip->port+HCFG); */ | |
699 | + /* 0x1000 causes AC3 to fails. Maybe it effects 24 bit output. */ | |
700 | + /* outl(0x00001409, chip->port+HCFG); */ | |
701 | + /* outl(0x00000009, chip->port+HCFG); */ | |
702 | + /* AC97 2.0, Enable outputs. */ | |
703 | + outl(HCFG_AC97 | HCFG_AUDIOENABLE, chip->port+HCFG); | |
704 | ||
705 | - if (chip->details->i2c_adc == 1) { /* The SB0410 and SB0413 use I2C to control ADC. */ | |
706 | + if (chip->details->i2c_adc == 1) { | |
707 | + /* The SB0410 and SB0413 use I2C to control ADC. */ | |
708 | int size, n; | |
709 | ||
710 | size = ARRAY_SIZE(i2c_adc_init); | |
711 | - //snd_printk("I2C:array size=0x%x\n", size); | |
712 | - for (n=0; n < size; n++) { | |
713 | - snd_ca0106_i2c_write(chip, i2c_adc_init[n][0], i2c_adc_init[n][1]); | |
714 | - } | |
715 | - for (n=0; n < 4; n++) { | |
716 | - chip->i2c_capture_volume[n][0]= 0xcf; | |
717 | - chip->i2c_capture_volume[n][1]= 0xcf; | |
718 | + /* snd_printk("I2C:array size=0x%x\n", size); */ | |
719 | + for (n = 0; n < size; n++) | |
720 | + snd_ca0106_i2c_write(chip, i2c_adc_init[n][0], | |
721 | + i2c_adc_init[n][1]); | |
722 | + for (n = 0; n < 4; n++) { | |
723 | + chip->i2c_capture_volume[n][0] = 0xcf; | |
724 | + chip->i2c_capture_volume[n][1] = 0xcf; | |
725 | } | |
726 | - chip->i2c_capture_source=2; /* Line in */ | |
727 | - //snd_ca0106_i2c_write(chip, ADC_MUX, ADC_MUX_LINEIN); /* Enable Line-in capture. MIC in currently untested. */ | |
728 | + chip->i2c_capture_source = 2; /* Line in */ | |
729 | + /* Enable Line-in capture. MIC in currently untested. */ | |
730 | + /* snd_ca0106_i2c_write(chip, ADC_MUX, ADC_MUX_LINEIN); */ | |
731 | } | |
732 | - if (chip->details->spi_dac == 1) { /* The SB0570 use SPI to control DAC. */ | |
733 | + | |
734 | + if (chip->details->spi_dac == 1) { | |
735 | + /* The SB0570 use SPI to control DAC. */ | |
736 | int size, n; | |
737 | ||
738 | size = ARRAY_SIZE(spi_dac_init); | |
739 | @@ -1531,9 +1477,112 @@ static int __devinit snd_ca0106_create(i | |
740 | chip->spi_dac_reg[reg] = spi_dac_init[n]; | |
741 | } | |
742 | } | |
743 | +} | |
744 | + | |
745 | +static void ca0106_stop_chip(struct snd_ca0106 *chip) | |
746 | +{ | |
747 | + /* disable interrupts */ | |
748 | + snd_ca0106_ptr_write(chip, BASIC_INTERRUPT, 0, 0); | |
749 | + outl(0, chip->port + INTE); | |
750 | + snd_ca0106_ptr_write(chip, EXTENDED_INT_MASK, 0, 0); | |
751 | + udelay(1000); | |
752 | + /* disable audio */ | |
753 | + /* outl(HCFG_LOCKSOUNDCACHE, chip->port + HCFG); */ | |
754 | + outl(0, chip->port + HCFG); | |
755 | + /* FIXME: We need to stop and DMA transfers here. | |
756 | + * But as I am not sure how yet, we cannot from the dma pages. | |
757 | + * So we can fix: snd-malloc: Memory leak? pages not freed = 8 | |
758 | + */ | |
759 | +} | |
760 | ||
761 | - if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, | |
762 | - chip, &ops)) < 0) { | |
763 | +static int __devinit snd_ca0106_create(int dev, struct snd_card *card, | |
764 | + struct pci_dev *pci, | |
765 | + struct snd_ca0106 **rchip) | |
766 | +{ | |
767 | + struct snd_ca0106 *chip; | |
768 | + struct snd_ca0106_details *c; | |
769 | + int err; | |
770 | + static struct snd_device_ops ops = { | |
771 | + .dev_free = snd_ca0106_dev_free, | |
772 | + }; | |
773 | + | |
774 | + *rchip = NULL; | |
775 | + | |
776 | + err = pci_enable_device(pci); | |
777 | + if (err < 0) | |
778 | + return err; | |
779 | + if (pci_set_dma_mask(pci, DMA_32BIT_MASK) < 0 || | |
780 | + pci_set_consistent_dma_mask(pci, DMA_32BIT_MASK) < 0) { | |
781 | + printk(KERN_ERR "error to set 32bit mask DMA\n"); | |
782 | + pci_disable_device(pci); | |
783 | + return -ENXIO; | |
784 | + } | |
785 | + | |
786 | + chip = kzalloc(sizeof(*chip), GFP_KERNEL); | |
787 | + if (chip == NULL) { | |
788 | + pci_disable_device(pci); | |
789 | + return -ENOMEM; | |
790 | + } | |
791 | + | |
792 | + chip->card = card; | |
793 | + chip->pci = pci; | |
794 | + chip->irq = -1; | |
795 | + | |
796 | + spin_lock_init(&chip->emu_lock); | |
797 | + | |
798 | + chip->port = pci_resource_start(pci, 0); | |
799 | + chip->res_port = request_region(chip->port, 0x20, "snd_ca0106"); | |
800 | + if (!chip->res_port) { | |
801 | + snd_ca0106_free(chip); | |
802 | + printk(KERN_ERR "cannot allocate the port\n"); | |
803 | + return -EBUSY; | |
804 | + } | |
805 | + | |
806 | + if (request_irq(pci->irq, snd_ca0106_interrupt, | |
807 | + IRQF_SHARED, "snd_ca0106", chip)) { | |
808 | + snd_ca0106_free(chip); | |
809 | + printk(KERN_ERR "cannot grab irq\n"); | |
810 | + return -EBUSY; | |
811 | + } | |
812 | + chip->irq = pci->irq; | |
813 | + | |
814 | + /* This stores the periods table. */ | |
815 | + if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci), | |
816 | + 1024, &chip->buffer) < 0) { | |
817 | + snd_ca0106_free(chip); | |
818 | + return -ENOMEM; | |
819 | + } | |
820 | + | |
821 | + pci_set_master(pci); | |
822 | + /* read serial */ | |
823 | + pci_read_config_dword(pci, PCI_SUBSYSTEM_VENDOR_ID, &chip->serial); | |
824 | + pci_read_config_word(pci, PCI_SUBSYSTEM_ID, &chip->model); | |
825 | + printk(KERN_INFO "snd-ca0106: Model %04x Rev %08x Serial %08x\n", | |
826 | + chip->model, pci->revision, chip->serial); | |
827 | + strcpy(card->driver, "CA0106"); | |
828 | + strcpy(card->shortname, "CA0106"); | |
829 | + | |
830 | + for (c = ca0106_chip_details; c->serial; c++) { | |
831 | + if (subsystem[dev]) { | |
832 | + if (c->serial == subsystem[dev]) | |
833 | + break; | |
834 | + } else if (c->serial == chip->serial) | |
835 | + break; | |
836 | + } | |
837 | + chip->details = c; | |
838 | + if (subsystem[dev]) { | |
839 | + printk(KERN_INFO "snd-ca0106: Sound card name=%s, " | |
840 | + "subsystem=0x%x. Forced to subsystem=0x%x\n", | |
841 | + c->name, chip->serial, subsystem[dev]); | |
842 | + } | |
843 | + | |
844 | + sprintf(card->longname, "%s at 0x%lx irq %i", | |
845 | + c->name, chip->port, chip->irq); | |
846 | + | |
847 | + ca0106_init_chip(chip, 0); | |
848 | + | |
849 | + err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops); | |
850 | + if (err < 0) { | |
851 | snd_ca0106_free(chip); | |
852 | return err; | |
853 | } | |
854 | @@ -1630,7 +1679,7 @@ static int __devinit snd_ca0106_probe(st | |
855 | static int dev; | |
856 | struct snd_card *card; | |
857 | struct snd_ca0106 *chip; | |
858 | - int err; | |
859 | + int i, err; | |
860 | ||
861 | if (dev >= SNDRV_CARDS) | |
862 | return -ENODEV; | |
863 | @@ -1643,44 +1692,31 @@ static int __devinit snd_ca0106_probe(st | |
864 | if (card == NULL) | |
865 | return -ENOMEM; | |
866 | ||
867 | - if ((err = snd_ca0106_create(dev, card, pci, &chip)) < 0) { | |
868 | - snd_card_free(card); | |
869 | - return err; | |
870 | - } | |
871 | - | |
872 | - if ((err = snd_ca0106_pcm(chip, 0, NULL)) < 0) { | |
873 | - snd_card_free(card); | |
874 | - return err; | |
875 | - } | |
876 | - if ((err = snd_ca0106_pcm(chip, 1, NULL)) < 0) { | |
877 | - snd_card_free(card); | |
878 | - return err; | |
879 | - } | |
880 | - if ((err = snd_ca0106_pcm(chip, 2, NULL)) < 0) { | |
881 | - snd_card_free(card); | |
882 | - return err; | |
883 | - } | |
884 | - if ((err = snd_ca0106_pcm(chip, 3, NULL)) < 0) { | |
885 | - snd_card_free(card); | |
886 | - return err; | |
887 | - } | |
888 | - if (chip->details->ac97 == 1) { /* The SB0410 and SB0413 do not have an AC97 chip. */ | |
889 | - if ((err = snd_ca0106_ac97(chip)) < 0) { | |
890 | - snd_card_free(card); | |
891 | - return err; | |
892 | - } | |
893 | - } | |
894 | - if ((err = snd_ca0106_mixer(chip)) < 0) { | |
895 | - snd_card_free(card); | |
896 | - return err; | |
897 | - } | |
898 | + err = snd_ca0106_create(dev, card, pci, &chip); | |
899 | + if (err < 0) | |
900 | + goto error; | |
901 | + card->private_data = chip; | |
902 | + | |
903 | + for (i = 0; i < 4; i++) { | |
904 | + err = snd_ca0106_pcm(chip, i); | |
905 | + if (err < 0) | |
906 | + goto error; | |
907 | + } | |
908 | + | |
909 | + if (chip->details->ac97 == 1) { | |
910 | + /* The SB0410 and SB0413 do not have an AC97 chip. */ | |
911 | + err = snd_ca0106_ac97(chip); | |
912 | + if (err < 0) | |
913 | + goto error; | |
914 | + } | |
915 | + err = snd_ca0106_mixer(chip); | |
916 | + if (err < 0) | |
917 | + goto error; | |
918 | ||
919 | snd_printdd("ca0106: probe for MIDI channel A ..."); | |
920 | - if ((err = snd_ca0106_midi(chip,CA0106_MIDI_CHAN_A)) < 0) { | |
921 | - snd_card_free(card); | |
922 | - snd_printdd(" failed, err=0x%x\n",err); | |
923 | - return err; | |
924 | - } | |
925 | + err = snd_ca0106_midi(chip, CA0106_MIDI_CHAN_A); | |
926 | + if (err < 0) | |
927 | + goto error; | |
928 | snd_printdd(" done.\n"); | |
929 | ||
930 | #ifdef CONFIG_PROC_FS | |
931 | @@ -1689,14 +1725,17 @@ static int __devinit snd_ca0106_probe(st | |
932 | ||
933 | snd_card_set_dev(card, &pci->dev); | |
934 | ||
935 | - if ((err = snd_card_register(card)) < 0) { | |
936 | - snd_card_free(card); | |
937 | - return err; | |
938 | - } | |
939 | + err = snd_card_register(card); | |
940 | + if (err < 0) | |
941 | + goto error; | |
942 | ||
943 | pci_set_drvdata(pci, card); | |
944 | dev++; | |
945 | return 0; | |
946 | + | |
947 | + error: | |
948 | + snd_card_free(card); | |
949 | + return err; | |
950 | } | |
951 | ||
952 | static void __devexit snd_ca0106_remove(struct pci_dev *pci) | |
953 | @@ -1705,6 +1744,59 @@ static void __devexit snd_ca0106_remove( | |
954 | pci_set_drvdata(pci, NULL); | |
955 | } | |
956 | ||
957 | +#ifdef CONFIG_PM | |
958 | +static int snd_ca0106_suspend(struct pci_dev *pci, pm_message_t state) | |
959 | +{ | |
960 | + struct snd_card *card = pci_get_drvdata(pci); | |
961 | + struct snd_ca0106 *chip = card->private_data; | |
962 | + int i; | |
963 | + | |
964 | + snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); | |
965 | + for (i = 0; i < 4; i++) | |
966 | + snd_pcm_suspend_all(chip->pcm[i]); | |
967 | + if (chip->details->ac97) | |
968 | + snd_ac97_suspend(chip->ac97); | |
969 | + snd_ca0106_mixer_suspend(chip); | |
970 | + | |
971 | + ca0106_stop_chip(chip); | |
972 | + | |
973 | + pci_disable_device(pci); | |
974 | + pci_save_state(pci); | |
975 | + pci_set_power_state(pci, pci_choose_state(pci, state)); | |
976 | + return 0; | |
977 | +} | |
978 | + | |
979 | +static int snd_ca0106_resume(struct pci_dev *pci) | |
980 | +{ | |
981 | + struct snd_card *card = pci_get_drvdata(pci); | |
982 | + struct snd_ca0106 *chip = card->private_data; | |
983 | + int i; | |
984 | + | |
985 | + pci_set_power_state(pci, PCI_D0); | |
986 | + pci_restore_state(pci); | |
987 | + | |
988 | + if (pci_enable_device(pci) < 0) { | |
989 | + snd_card_disconnect(card); | |
990 | + return -EIO; | |
991 | + } | |
992 | + | |
993 | + pci_set_master(pci); | |
994 | + | |
995 | + ca0106_init_chip(chip, 1); | |
996 | + | |
997 | + if (chip->details->ac97) | |
998 | + snd_ac97_resume(chip->ac97); | |
999 | + snd_ca0106_mixer_resume(chip); | |
1000 | + if (chip->details->spi_dac) { | |
1001 | + for (i = 0; i < ARRAY_SIZE(chip->spi_dac_reg); i++) | |
1002 | + snd_ca0106_spi_write(chip, chip->spi_dac_reg[i]); | |
1003 | + } | |
1004 | + | |
1005 | + snd_power_change_state(card, SNDRV_CTL_POWER_D0); | |
1006 | + return 0; | |
1007 | +} | |
1008 | +#endif | |
1009 | + | |
1010 | // PCI IDs | |
1011 | static struct pci_device_id snd_ca0106_ids[] = { | |
1012 | { 0x1102, 0x0007, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* Audigy LS or Live 24bit */ | |
1013 | @@ -1718,6 +1810,8 @@ static struct pci_driver driver = { | |
1014 | .id_table = snd_ca0106_ids, | |
1015 | .probe = snd_ca0106_probe, | |
1016 | .remove = __devexit_p(snd_ca0106_remove), | |
1017 | + .suspend = snd_ca0106_suspend, | |
1018 | + .resume = snd_ca0106_resume, | |
1019 | }; | |
1020 | ||
1021 | // initialization of the module |