diff options
author | Wai Yew CHAY <wychay@ctl.creative.com> | 2009-05-14 02:05:58 -0400 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2009-05-14 02:24:10 -0400 |
commit | 8cc72361481f00253f1e468ade5795427386d593 (patch) | |
tree | ec6f3ea304f90fa9c99abb1bf2354fc5d357db27 /sound/pci/ctxfi/ctpcm.c | |
parent | 091bf7624d1c90cec9e578a18529f615213ff847 (diff) |
ALSA: SB X-Fi driver merge
The Sound Blaster X-Fi driver supports Creative solutions based on
20K1 and 20K2 chipsets.
Supported hardware :
Creative Sound Blaster X-Fi Titanium Fatal1ty® Champion Series
Creative Sound Blaster X-Fi Titanium Fatal1ty Professional Series
Creative Sound Blaster X-Fi Titanium Professional Audio
Creative Sound Blaster X-Fi Titanium
Creative Sound Blaster X-Fi Elite Pro
Creative Sound Blaster X-Fi Platinum
Creative Sound Blaster X-Fi Fatal1ty
Creative Sound Blaster X-Fi XtremeGamer
Creative Sound Blaster X-Fi XtremeMusic
Current release features:
* ALSA PCM Playback
* ALSA Record
* ALSA Mixer
Note:
* External I/O modules detection not included.
Signed-off-by: Wai Yew CHAY <wychay@ctl.creative.com>
Singed-off-by: Ryan RICHARDS <ryan_richards@creativelabs.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/pci/ctxfi/ctpcm.c')
-rw-r--r-- | sound/pci/ctxfi/ctpcm.c | 499 |
1 files changed, 499 insertions, 0 deletions
diff --git a/sound/pci/ctxfi/ctpcm.c b/sound/pci/ctxfi/ctpcm.c new file mode 100644 index 000000000000..73d4fdbbb9f4 --- /dev/null +++ b/sound/pci/ctxfi/ctpcm.c | |||
@@ -0,0 +1,499 @@ | |||
1 | /** | ||
2 | * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved. | ||
3 | * | ||
4 | * This source file is released under GPL v2 license (no other versions). | ||
5 | * See the COPYING file included in the main directory of this source | ||
6 | * distribution for the license terms and conditions. | ||
7 | * | ||
8 | * @File ctpcm.c | ||
9 | * | ||
10 | * @Brief | ||
11 | * This file contains the definition of the pcm device functions. | ||
12 | * | ||
13 | * @Author Liu Chun | ||
14 | * @Date Apr 2 2008 | ||
15 | * | ||
16 | */ | ||
17 | |||
18 | #include "ctpcm.h" | ||
19 | #include <sound/pcm.h> | ||
20 | |||
21 | /* Hardware descriptions for playback */ | ||
22 | static struct snd_pcm_hardware ct_pcm_playback_hw = { | ||
23 | .info = (SNDRV_PCM_INFO_MMAP | | ||
24 | SNDRV_PCM_INFO_INTERLEAVED | | ||
25 | SNDRV_PCM_INFO_BLOCK_TRANSFER | | ||
26 | SNDRV_PCM_INFO_MMAP_VALID | | ||
27 | SNDRV_PCM_INFO_PAUSE), | ||
28 | .formats = (SNDRV_PCM_FMTBIT_U8 | | ||
29 | SNDRV_PCM_FMTBIT_S8 | | ||
30 | SNDRV_PCM_FMTBIT_S16_LE | | ||
31 | SNDRV_PCM_FMTBIT_U16_LE | | ||
32 | SNDRV_PCM_FMTBIT_S24_3LE | | ||
33 | SNDRV_PCM_FMTBIT_S24_LE | | ||
34 | SNDRV_PCM_FMTBIT_S32_LE), | ||
35 | .rates = (SNDRV_PCM_RATE_CONTINUOUS | | ||
36 | SNDRV_PCM_RATE_8000_192000), | ||
37 | .rate_min = 8000, | ||
38 | .rate_max = 192000, | ||
39 | .channels_min = 1, | ||
40 | .channels_max = 2, | ||
41 | .buffer_bytes_max = (128*1024), | ||
42 | .period_bytes_min = (64), | ||
43 | .period_bytes_max = (128*1024), | ||
44 | .periods_min = 1, | ||
45 | .periods_max = 1024, | ||
46 | .fifo_size = 0, | ||
47 | }; | ||
48 | |||
49 | static struct snd_pcm_hardware ct_spdif_passthru_playback_hw = { | ||
50 | .info = (SNDRV_PCM_INFO_MMAP | | ||
51 | SNDRV_PCM_INFO_INTERLEAVED | | ||
52 | SNDRV_PCM_INFO_BLOCK_TRANSFER | | ||
53 | SNDRV_PCM_INFO_MMAP_VALID | | ||
54 | SNDRV_PCM_INFO_PAUSE), | ||
55 | .formats = (SNDRV_PCM_FMTBIT_S16_LE | | ||
56 | SNDRV_PCM_FMTBIT_U16_LE), | ||
57 | .rates = (SNDRV_PCM_RATE_48000 | | ||
58 | SNDRV_PCM_RATE_44100 | | ||
59 | SNDRV_PCM_RATE_32000), | ||
60 | .rate_min = 32000, | ||
61 | .rate_max = 48000, | ||
62 | .channels_min = 2, | ||
63 | .channels_max = 2, | ||
64 | .buffer_bytes_max = (128*1024), | ||
65 | .period_bytes_min = (64), | ||
66 | .period_bytes_max = (128*1024), | ||
67 | .periods_min = 1, | ||
68 | .periods_max = 1024, | ||
69 | .fifo_size = 0, | ||
70 | }; | ||
71 | |||
72 | /* Hardware descriptions for capture */ | ||
73 | static struct snd_pcm_hardware ct_pcm_capture_hw = { | ||
74 | .info = (SNDRV_PCM_INFO_MMAP | | ||
75 | SNDRV_PCM_INFO_INTERLEAVED | | ||
76 | SNDRV_PCM_INFO_BLOCK_TRANSFER | | ||
77 | SNDRV_PCM_INFO_PAUSE | | ||
78 | SNDRV_PCM_INFO_MMAP_VALID), | ||
79 | .formats = (SNDRV_PCM_FMTBIT_U8 | | ||
80 | SNDRV_PCM_FMTBIT_S8 | | ||
81 | SNDRV_PCM_FMTBIT_S16_LE | | ||
82 | SNDRV_PCM_FMTBIT_U16_LE | | ||
83 | SNDRV_PCM_FMTBIT_S24_3LE | | ||
84 | SNDRV_PCM_FMTBIT_S24_LE | | ||
85 | SNDRV_PCM_FMTBIT_S32_LE), | ||
86 | .rates = (SNDRV_PCM_RATE_CONTINUOUS | | ||
87 | SNDRV_PCM_RATE_8000_96000), | ||
88 | .rate_min = 8000, | ||
89 | .rate_max = 96000, | ||
90 | .channels_min = 1, | ||
91 | .channels_max = 2, | ||
92 | .buffer_bytes_max = (128*1024), | ||
93 | .period_bytes_min = (384), | ||
94 | .period_bytes_max = (64*1024), | ||
95 | .periods_min = 2, | ||
96 | .periods_max = 1024, | ||
97 | .fifo_size = 0, | ||
98 | }; | ||
99 | |||
100 | static void ct_atc_pcm_interrupt(struct ct_atc_pcm *atc_pcm) | ||
101 | { | ||
102 | struct ct_atc_pcm *apcm = atc_pcm; | ||
103 | |||
104 | if (NULL == apcm->substream) | ||
105 | return; | ||
106 | |||
107 | snd_pcm_period_elapsed(apcm->substream); | ||
108 | } | ||
109 | |||
110 | static void ct_atc_pcm_free_substream(struct snd_pcm_runtime *runtime) | ||
111 | { | ||
112 | struct ct_atc_pcm *apcm = runtime->private_data; | ||
113 | struct ct_atc *atc = snd_pcm_substream_chip(apcm->substream); | ||
114 | |||
115 | atc->pcm_release_resources(atc, apcm); | ||
116 | kfree(apcm); | ||
117 | runtime->private_data = NULL; | ||
118 | } | ||
119 | |||
120 | /* pcm playback operations */ | ||
121 | static int ct_pcm_playback_open(struct snd_pcm_substream *substream) | ||
122 | { | ||
123 | struct ct_atc *atc = snd_pcm_substream_chip(substream); | ||
124 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
125 | struct ct_atc_pcm *apcm; | ||
126 | int err; | ||
127 | |||
128 | apcm = kzalloc(sizeof(*apcm), GFP_KERNEL); | ||
129 | if (NULL == apcm) | ||
130 | return -ENOMEM; | ||
131 | |||
132 | spin_lock_init(&apcm->timer_lock); | ||
133 | apcm->stop_timer = 0; | ||
134 | apcm->substream = substream; | ||
135 | apcm->interrupt = ct_atc_pcm_interrupt; | ||
136 | runtime->private_data = apcm; | ||
137 | runtime->private_free = ct_atc_pcm_free_substream; | ||
138 | if (IEC958 == substream->pcm->device) { | ||
139 | runtime->hw = ct_spdif_passthru_playback_hw; | ||
140 | atc->spdif_out_passthru(atc, 1); | ||
141 | } else { | ||
142 | runtime->hw = ct_pcm_playback_hw; | ||
143 | if (FRONT == substream->pcm->device) | ||
144 | runtime->hw.channels_max = 8; | ||
145 | } | ||
146 | |||
147 | err = snd_pcm_hw_constraint_integer(runtime, | ||
148 | SNDRV_PCM_HW_PARAM_PERIODS); | ||
149 | if (err < 0) { | ||
150 | kfree(apcm); | ||
151 | return err; | ||
152 | } | ||
153 | err = snd_pcm_hw_constraint_minmax(runtime, | ||
154 | SNDRV_PCM_HW_PARAM_BUFFER_BYTES, | ||
155 | 1024, UINT_MAX); | ||
156 | if (err < 0) { | ||
157 | kfree(apcm); | ||
158 | return err; | ||
159 | } | ||
160 | |||
161 | return 0; | ||
162 | } | ||
163 | |||
164 | static int ct_pcm_playback_close(struct snd_pcm_substream *substream) | ||
165 | { | ||
166 | struct ct_atc *atc = snd_pcm_substream_chip(substream); | ||
167 | |||
168 | /* TODO: Notify mixer inactive. */ | ||
169 | if (IEC958 == substream->pcm->device) | ||
170 | atc->spdif_out_passthru(atc, 0); | ||
171 | |||
172 | /* The ct_atc_pcm object will be freed by runtime->private_free */ | ||
173 | |||
174 | return 0; | ||
175 | } | ||
176 | |||
177 | static int ct_pcm_hw_params(struct snd_pcm_substream *substream, | ||
178 | struct snd_pcm_hw_params *hw_params) | ||
179 | { | ||
180 | return snd_pcm_lib_malloc_pages(substream, | ||
181 | params_buffer_bytes(hw_params)); | ||
182 | } | ||
183 | |||
184 | static int ct_pcm_hw_free(struct snd_pcm_substream *substream) | ||
185 | { | ||
186 | /* Free snd-allocated pages */ | ||
187 | return snd_pcm_lib_free_pages(substream); | ||
188 | } | ||
189 | |||
190 | static void ct_pcm_timer_callback(unsigned long data) | ||
191 | { | ||
192 | struct ct_atc_pcm *apcm = (struct ct_atc_pcm *)data; | ||
193 | struct snd_pcm_substream *substream = apcm->substream; | ||
194 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
195 | unsigned int period_size = runtime->period_size; | ||
196 | unsigned int buffer_size = runtime->buffer_size; | ||
197 | unsigned long flags; | ||
198 | unsigned int position = 0, dist = 0, interval = 0; | ||
199 | |||
200 | position = substream->ops->pointer(substream); | ||
201 | dist = (position + buffer_size - apcm->position) % buffer_size; | ||
202 | if ((dist >= period_size) || | ||
203 | (position/period_size != apcm->position/period_size)) { | ||
204 | apcm->interrupt(apcm); | ||
205 | apcm->position = position; | ||
206 | } | ||
207 | /* Add extra HZ*5/1000 to avoid overrun issue when recording | ||
208 | * at 8kHz in 8-bit format or at 88kHz in 24-bit format. */ | ||
209 | interval = ((period_size - (position % period_size)) | ||
210 | * HZ + (runtime->rate - 1)) / runtime->rate + HZ * 5 / 1000; | ||
211 | spin_lock_irqsave(&apcm->timer_lock, flags); | ||
212 | apcm->timer.expires = jiffies + interval; | ||
213 | if (!apcm->stop_timer) | ||
214 | add_timer(&apcm->timer); | ||
215 | |||
216 | spin_unlock_irqrestore(&apcm->timer_lock, flags); | ||
217 | } | ||
218 | |||
219 | static int ct_pcm_timer_prepare(struct ct_atc_pcm *apcm) | ||
220 | { | ||
221 | unsigned long flags; | ||
222 | |||
223 | spin_lock_irqsave(&apcm->timer_lock, flags); | ||
224 | if (timer_pending(&apcm->timer)) { | ||
225 | /* The timer has already been started. */ | ||
226 | spin_unlock_irqrestore(&apcm->timer_lock, flags); | ||
227 | return 0; | ||
228 | } | ||
229 | |||
230 | init_timer(&apcm->timer); | ||
231 | apcm->timer.data = (unsigned long)apcm; | ||
232 | apcm->timer.function = ct_pcm_timer_callback; | ||
233 | spin_unlock_irqrestore(&apcm->timer_lock, flags); | ||
234 | apcm->position = 0; | ||
235 | |||
236 | return 0; | ||
237 | } | ||
238 | |||
239 | static int ct_pcm_timer_start(struct ct_atc_pcm *apcm) | ||
240 | { | ||
241 | struct snd_pcm_runtime *runtime = apcm->substream->runtime; | ||
242 | unsigned long flags; | ||
243 | |||
244 | spin_lock_irqsave(&apcm->timer_lock, flags); | ||
245 | if (timer_pending(&apcm->timer)) { | ||
246 | /* The timer has already been started. */ | ||
247 | spin_unlock_irqrestore(&apcm->timer_lock, flags); | ||
248 | return 0; | ||
249 | } | ||
250 | |||
251 | apcm->timer.expires = jiffies + (runtime->period_size * HZ + | ||
252 | (runtime->rate - 1)) / runtime->rate; | ||
253 | apcm->stop_timer = 0; | ||
254 | add_timer(&apcm->timer); | ||
255 | spin_unlock_irqrestore(&apcm->timer_lock, flags); | ||
256 | |||
257 | return 0; | ||
258 | } | ||
259 | |||
260 | static int ct_pcm_timer_stop(struct ct_atc_pcm *apcm) | ||
261 | { | ||
262 | unsigned long flags; | ||
263 | |||
264 | spin_lock_irqsave(&apcm->timer_lock, flags); | ||
265 | apcm->stop_timer = 1; | ||
266 | del_timer(&apcm->timer); | ||
267 | spin_unlock_irqrestore(&apcm->timer_lock, flags); | ||
268 | |||
269 | try_to_del_timer_sync(&apcm->timer); | ||
270 | |||
271 | return 0; | ||
272 | } | ||
273 | |||
274 | static int ct_pcm_playback_prepare(struct snd_pcm_substream *substream) | ||
275 | { | ||
276 | int err; | ||
277 | struct ct_atc *atc = snd_pcm_substream_chip(substream); | ||
278 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
279 | struct ct_atc_pcm *apcm = runtime->private_data; | ||
280 | |||
281 | if (IEC958 == substream->pcm->device) | ||
282 | err = atc->spdif_passthru_playback_prepare(atc, apcm); | ||
283 | else | ||
284 | err = atc->pcm_playback_prepare(atc, apcm); | ||
285 | |||
286 | if (err < 0) { | ||
287 | printk(KERN_ERR "Preparing pcm playback failed!!!\n"); | ||
288 | return err; | ||
289 | } | ||
290 | |||
291 | ct_pcm_timer_prepare(apcm); | ||
292 | |||
293 | return 0; | ||
294 | } | ||
295 | |||
296 | static int | ||
297 | ct_pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd) | ||
298 | { | ||
299 | struct ct_atc *atc = snd_pcm_substream_chip(substream); | ||
300 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
301 | struct ct_atc_pcm *apcm = runtime->private_data; | ||
302 | |||
303 | switch (cmd) { | ||
304 | case SNDRV_PCM_TRIGGER_START: | ||
305 | case SNDRV_PCM_TRIGGER_RESUME: | ||
306 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: | ||
307 | atc->pcm_playback_start(atc, apcm); | ||
308 | ct_pcm_timer_start(apcm); | ||
309 | break; | ||
310 | case SNDRV_PCM_TRIGGER_STOP: | ||
311 | case SNDRV_PCM_TRIGGER_SUSPEND: | ||
312 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: | ||
313 | ct_pcm_timer_stop(apcm); | ||
314 | atc->pcm_playback_stop(atc, apcm); | ||
315 | break; | ||
316 | default: | ||
317 | break; | ||
318 | } | ||
319 | |||
320 | return 0; | ||
321 | } | ||
322 | |||
323 | static snd_pcm_uframes_t | ||
324 | ct_pcm_playback_pointer(struct snd_pcm_substream *substream) | ||
325 | { | ||
326 | unsigned long position; | ||
327 | struct ct_atc *atc = snd_pcm_substream_chip(substream); | ||
328 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
329 | struct ct_atc_pcm *apcm = runtime->private_data; | ||
330 | |||
331 | /* Read out playback position */ | ||
332 | position = atc->pcm_playback_position(atc, apcm); | ||
333 | position = bytes_to_frames(runtime, position); | ||
334 | return position; | ||
335 | } | ||
336 | |||
337 | /* pcm capture operations */ | ||
338 | static int ct_pcm_capture_open(struct snd_pcm_substream *substream) | ||
339 | { | ||
340 | struct ct_atc *atc = snd_pcm_substream_chip(substream); | ||
341 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
342 | struct ct_atc_pcm *apcm; | ||
343 | int err; | ||
344 | |||
345 | apcm = kzalloc(sizeof(*apcm), GFP_KERNEL); | ||
346 | if (NULL == apcm) | ||
347 | return -ENOMEM; | ||
348 | |||
349 | spin_lock_init(&apcm->timer_lock); | ||
350 | apcm->started = 0; | ||
351 | apcm->stop_timer = 0; | ||
352 | apcm->substream = substream; | ||
353 | apcm->interrupt = ct_atc_pcm_interrupt; | ||
354 | runtime->private_data = apcm; | ||
355 | runtime->private_free = ct_atc_pcm_free_substream; | ||
356 | runtime->hw = ct_pcm_capture_hw; | ||
357 | runtime->hw.rate_max = atc->rsr * atc->msr; | ||
358 | |||
359 | err = snd_pcm_hw_constraint_integer(runtime, | ||
360 | SNDRV_PCM_HW_PARAM_PERIODS); | ||
361 | if (err < 0) { | ||
362 | kfree(apcm); | ||
363 | return err; | ||
364 | } | ||
365 | err = snd_pcm_hw_constraint_minmax(runtime, | ||
366 | SNDRV_PCM_HW_PARAM_BUFFER_BYTES, | ||
367 | 1024, UINT_MAX); | ||
368 | if (err < 0) { | ||
369 | kfree(apcm); | ||
370 | return err; | ||
371 | } | ||
372 | |||
373 | return 0; | ||
374 | } | ||
375 | |||
376 | static int ct_pcm_capture_close(struct snd_pcm_substream *substream) | ||
377 | { | ||
378 | /* The ct_atc_pcm object will be freed by runtime->private_free */ | ||
379 | /* TODO: Notify mixer inactive. */ | ||
380 | return 0; | ||
381 | } | ||
382 | |||
383 | static int ct_pcm_capture_prepare(struct snd_pcm_substream *substream) | ||
384 | { | ||
385 | int err; | ||
386 | struct ct_atc *atc = snd_pcm_substream_chip(substream); | ||
387 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
388 | struct ct_atc_pcm *apcm = runtime->private_data; | ||
389 | |||
390 | err = atc->pcm_capture_prepare(atc, apcm); | ||
391 | if (err < 0) { | ||
392 | printk(KERN_ERR "Preparing pcm capture failed!!!\n"); | ||
393 | return err; | ||
394 | } | ||
395 | |||
396 | ct_pcm_timer_prepare(apcm); | ||
397 | |||
398 | return 0; | ||
399 | } | ||
400 | |||
401 | static int | ||
402 | ct_pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd) | ||
403 | { | ||
404 | struct ct_atc *atc = snd_pcm_substream_chip(substream); | ||
405 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
406 | struct ct_atc_pcm *apcm = runtime->private_data; | ||
407 | |||
408 | switch (cmd) { | ||
409 | case SNDRV_PCM_TRIGGER_START: | ||
410 | atc->pcm_capture_start(atc, apcm); | ||
411 | ct_pcm_timer_start(apcm); | ||
412 | break; | ||
413 | case SNDRV_PCM_TRIGGER_STOP: | ||
414 | ct_pcm_timer_stop(apcm); | ||
415 | atc->pcm_capture_stop(atc, apcm); | ||
416 | break; | ||
417 | default: | ||
418 | ct_pcm_timer_stop(apcm); | ||
419 | atc->pcm_capture_stop(atc, apcm); | ||
420 | break; | ||
421 | } | ||
422 | |||
423 | return 0; | ||
424 | } | ||
425 | |||
426 | static snd_pcm_uframes_t | ||
427 | ct_pcm_capture_pointer(struct snd_pcm_substream *substream) | ||
428 | { | ||
429 | unsigned long position; | ||
430 | struct ct_atc *atc = snd_pcm_substream_chip(substream); | ||
431 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
432 | struct ct_atc_pcm *apcm = runtime->private_data; | ||
433 | |||
434 | /* Read out playback position */ | ||
435 | position = atc->pcm_capture_position(atc, apcm); | ||
436 | position = bytes_to_frames(runtime, position); | ||
437 | return position; | ||
438 | } | ||
439 | |||
440 | /* PCM operators for playback */ | ||
441 | static struct snd_pcm_ops ct_pcm_playback_ops = { | ||
442 | .open = ct_pcm_playback_open, | ||
443 | .close = ct_pcm_playback_close, | ||
444 | .ioctl = snd_pcm_lib_ioctl, | ||
445 | .hw_params = ct_pcm_hw_params, | ||
446 | .hw_free = ct_pcm_hw_free, | ||
447 | .prepare = ct_pcm_playback_prepare, | ||
448 | .trigger = ct_pcm_playback_trigger, | ||
449 | .pointer = ct_pcm_playback_pointer, | ||
450 | }; | ||
451 | |||
452 | /* PCM operators for capture */ | ||
453 | static struct snd_pcm_ops ct_pcm_capture_ops = { | ||
454 | .open = ct_pcm_capture_open, | ||
455 | .close = ct_pcm_capture_close, | ||
456 | .ioctl = snd_pcm_lib_ioctl, | ||
457 | .hw_params = ct_pcm_hw_params, | ||
458 | .hw_free = ct_pcm_hw_free, | ||
459 | .prepare = ct_pcm_capture_prepare, | ||
460 | .trigger = ct_pcm_capture_trigger, | ||
461 | .pointer = ct_pcm_capture_pointer, | ||
462 | }; | ||
463 | |||
464 | /* Create ALSA pcm device */ | ||
465 | int ct_alsa_pcm_create(struct ct_atc *atc, | ||
466 | enum CTALSADEVS device, | ||
467 | const char *device_name) | ||
468 | { | ||
469 | struct snd_pcm *pcm; | ||
470 | int err; | ||
471 | int playback_count, capture_count; | ||
472 | char name[128]; | ||
473 | |||
474 | strncpy(name, device_name, sizeof(name)); | ||
475 | playback_count = (IEC958 == device) ? 1 : 8; | ||
476 | capture_count = (FRONT == device) ? 1 : 0; | ||
477 | err = snd_pcm_new(atc->card, name, device, | ||
478 | playback_count, capture_count, &pcm); | ||
479 | if (err < 0) { | ||
480 | printk(KERN_ERR "snd_pcm_new failed!! Err=%d\n", err); | ||
481 | return err; | ||
482 | } | ||
483 | |||
484 | pcm->private_data = atc; | ||
485 | pcm->info_flags = 0; | ||
486 | pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX; | ||
487 | strcpy(pcm->name, device_name); | ||
488 | |||
489 | snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &ct_pcm_playback_ops); | ||
490 | |||
491 | if (FRONT == device) | ||
492 | snd_pcm_set_ops(pcm, | ||
493 | SNDRV_PCM_STREAM_CAPTURE, &ct_pcm_capture_ops); | ||
494 | |||
495 | snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, | ||
496 | snd_dma_pci_data(atc->pci), 128*1024, 128*1024); | ||
497 | |||
498 | return 0; | ||
499 | } | ||