diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /sound/drivers/vx/vx_pcm.c |
Linux-2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'sound/drivers/vx/vx_pcm.c')
-rw-r--r-- | sound/drivers/vx/vx_pcm.c | 1312 |
1 files changed, 1312 insertions, 0 deletions
diff --git a/sound/drivers/vx/vx_pcm.c b/sound/drivers/vx/vx_pcm.c new file mode 100644 index 000000000000..98587176b327 --- /dev/null +++ b/sound/drivers/vx/vx_pcm.c | |||
@@ -0,0 +1,1312 @@ | |||
1 | /* | ||
2 | * Driver for Digigram VX soundcards | ||
3 | * | ||
4 | * PCM part | ||
5 | * | ||
6 | * Copyright (c) 2002,2003 by Takashi Iwai <tiwai@suse.de> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published by | ||
10 | * the Free Software Foundation; either version 2 of the License, or | ||
11 | * (at your option) any later version. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, | ||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | * GNU General Public License for more details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU General Public License | ||
19 | * along with this program; if not, write to the Free Software | ||
20 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
21 | * | ||
22 | * | ||
23 | * STRATEGY | ||
24 | * for playback, we send series of "chunks", which size is equal with the | ||
25 | * IBL size, typically 126 samples. at each end of chunk, the end-of-buffer | ||
26 | * interrupt is notified, and the interrupt handler will feed the next chunk. | ||
27 | * | ||
28 | * the current position is calculated from the sample count RMH. | ||
29 | * pipe->transferred is the counter of data which has been already transferred. | ||
30 | * if this counter reaches to the period size, snd_pcm_period_elapsed() will | ||
31 | * be issued. | ||
32 | * | ||
33 | * for capture, the situation is much easier. | ||
34 | * to get a low latency response, we'll check the capture streams at each | ||
35 | * interrupt (capture stream has no EOB notification). if the pending | ||
36 | * data is accumulated to the period size, snd_pcm_period_elapsed() is | ||
37 | * called and the pointer is updated. | ||
38 | * | ||
39 | * the current point of read buffer is kept in pipe->hw_ptr. note that | ||
40 | * this is in bytes. | ||
41 | * | ||
42 | * | ||
43 | * TODO | ||
44 | * - linked trigger for full-duplex mode. | ||
45 | * - scheduled action on the stream. | ||
46 | */ | ||
47 | |||
48 | #include <sound/driver.h> | ||
49 | #include <linux/slab.h> | ||
50 | #include <linux/vmalloc.h> | ||
51 | #include <linux/delay.h> | ||
52 | #include <sound/core.h> | ||
53 | #include <sound/asoundef.h> | ||
54 | #include <sound/pcm.h> | ||
55 | #include <sound/vx_core.h> | ||
56 | #include "vx_cmd.h" | ||
57 | |||
58 | |||
59 | /* | ||
60 | * we use a vmalloc'ed (sg-)buffer | ||
61 | */ | ||
62 | |||
63 | /* get the physical page pointer on the given offset */ | ||
64 | static struct page *snd_pcm_get_vmalloc_page(snd_pcm_substream_t *subs, unsigned long offset) | ||
65 | { | ||
66 | void *pageptr = subs->runtime->dma_area + offset; | ||
67 | return vmalloc_to_page(pageptr); | ||
68 | } | ||
69 | |||
70 | /* | ||
71 | * allocate a buffer via vmalloc_32(). | ||
72 | * called from hw_params | ||
73 | * NOTE: this may be called not only once per pcm open! | ||
74 | */ | ||
75 | static int snd_pcm_alloc_vmalloc_buffer(snd_pcm_substream_t *subs, size_t size) | ||
76 | { | ||
77 | snd_pcm_runtime_t *runtime = subs->runtime; | ||
78 | if (runtime->dma_area) { | ||
79 | /* already allocated */ | ||
80 | if (runtime->dma_bytes >= size) | ||
81 | return 0; /* already enough large */ | ||
82 | vfree_nocheck(runtime->dma_area); /* bypass the memory wrapper */ | ||
83 | } | ||
84 | runtime->dma_area = vmalloc_32(size); | ||
85 | if (! runtime->dma_area) | ||
86 | return -ENOMEM; | ||
87 | memset(runtime->dma_area, 0, size); | ||
88 | runtime->dma_bytes = size; | ||
89 | return 1; /* changed */ | ||
90 | } | ||
91 | |||
92 | /* | ||
93 | * free the buffer. | ||
94 | * called from hw_free callback | ||
95 | * NOTE: this may be called not only once per pcm open! | ||
96 | */ | ||
97 | static int snd_pcm_free_vmalloc_buffer(snd_pcm_substream_t *subs) | ||
98 | { | ||
99 | snd_pcm_runtime_t *runtime = subs->runtime; | ||
100 | if (runtime->dma_area) { | ||
101 | vfree_nocheck(runtime->dma_area); /* bypass the memory wrapper */ | ||
102 | runtime->dma_area = NULL; | ||
103 | } | ||
104 | return 0; | ||
105 | } | ||
106 | |||
107 | |||
108 | /* | ||
109 | * read three pending pcm bytes via inb() | ||
110 | */ | ||
111 | static void vx_pcm_read_per_bytes(vx_core_t *chip, snd_pcm_runtime_t *runtime, vx_pipe_t *pipe) | ||
112 | { | ||
113 | int offset = pipe->hw_ptr; | ||
114 | unsigned char *buf = (unsigned char *)(runtime->dma_area + offset); | ||
115 | *buf++ = vx_inb(chip, RXH); | ||
116 | if (++offset >= pipe->buffer_bytes) { | ||
117 | offset = 0; | ||
118 | buf = (unsigned char *)runtime->dma_area; | ||
119 | } | ||
120 | *buf++ = vx_inb(chip, RXM); | ||
121 | if (++offset >= pipe->buffer_bytes) { | ||
122 | offset = 0; | ||
123 | buf = (unsigned char *)runtime->dma_area; | ||
124 | } | ||
125 | *buf++ = vx_inb(chip, RXL); | ||
126 | if (++offset >= pipe->buffer_bytes) { | ||
127 | offset = 0; | ||
128 | buf = (unsigned char *)runtime->dma_area; | ||
129 | } | ||
130 | pipe->hw_ptr = offset; | ||
131 | } | ||
132 | |||
133 | /* | ||
134 | * vx_set_pcx_time - convert from the PC time to the RMH status time. | ||
135 | * @pc_time: the pointer for the PC-time to set | ||
136 | * @dsp_time: the pointer for RMH status time array | ||
137 | */ | ||
138 | static void vx_set_pcx_time(vx_core_t *chip, pcx_time_t *pc_time, unsigned int *dsp_time) | ||
139 | { | ||
140 | dsp_time[0] = (unsigned int)((*pc_time) >> 24) & PCX_TIME_HI_MASK; | ||
141 | dsp_time[1] = (unsigned int)(*pc_time) & MASK_DSP_WORD; | ||
142 | } | ||
143 | |||
144 | /* | ||
145 | * vx_set_differed_time - set the differed time if specified | ||
146 | * @rmh: the rmh record to modify | ||
147 | * @pipe: the pipe to be checked | ||
148 | * | ||
149 | * if the pipe is programmed with the differed time, set the DSP time | ||
150 | * on the rmh and changes its command length. | ||
151 | * | ||
152 | * returns the increase of the command length. | ||
153 | */ | ||
154 | static int vx_set_differed_time(vx_core_t *chip, struct vx_rmh *rmh, vx_pipe_t *pipe) | ||
155 | { | ||
156 | /* Update The length added to the RMH command by the timestamp */ | ||
157 | if (! (pipe->differed_type & DC_DIFFERED_DELAY)) | ||
158 | return 0; | ||
159 | |||
160 | /* Set the T bit */ | ||
161 | rmh->Cmd[0] |= DSP_DIFFERED_COMMAND_MASK; | ||
162 | |||
163 | /* Time stamp is the 1st following parameter */ | ||
164 | vx_set_pcx_time(chip, &pipe->pcx_time, &rmh->Cmd[1]); | ||
165 | |||
166 | /* Add the flags to a notified differed command */ | ||
167 | if (pipe->differed_type & DC_NOTIFY_DELAY) | ||
168 | rmh->Cmd[1] |= NOTIFY_MASK_TIME_HIGH ; | ||
169 | |||
170 | /* Add the flags to a multiple differed command */ | ||
171 | if (pipe->differed_type & DC_MULTIPLE_DELAY) | ||
172 | rmh->Cmd[1] |= MULTIPLE_MASK_TIME_HIGH; | ||
173 | |||
174 | /* Add the flags to a stream-time differed command */ | ||
175 | if (pipe->differed_type & DC_STREAM_TIME_DELAY) | ||
176 | rmh->Cmd[1] |= STREAM_MASK_TIME_HIGH; | ||
177 | |||
178 | rmh->LgCmd += 2; | ||
179 | return 2; | ||
180 | } | ||
181 | |||
182 | /* | ||
183 | * vx_set_stream_format - send the stream format command | ||
184 | * @pipe: the affected pipe | ||
185 | * @data: format bitmask | ||
186 | */ | ||
187 | static int vx_set_stream_format(vx_core_t *chip, vx_pipe_t *pipe, unsigned int data) | ||
188 | { | ||
189 | struct vx_rmh rmh; | ||
190 | |||
191 | vx_init_rmh(&rmh, pipe->is_capture ? | ||
192 | CMD_FORMAT_STREAM_IN : CMD_FORMAT_STREAM_OUT); | ||
193 | rmh.Cmd[0] |= pipe->number << FIELD_SIZE; | ||
194 | |||
195 | /* Command might be longer since we may have to add a timestamp */ | ||
196 | vx_set_differed_time(chip, &rmh, pipe); | ||
197 | |||
198 | rmh.Cmd[rmh.LgCmd] = (data & 0xFFFFFF00) >> 8; | ||
199 | rmh.Cmd[rmh.LgCmd + 1] = (data & 0xFF) << 16 /*| (datal & 0xFFFF00) >> 8*/; | ||
200 | rmh.LgCmd += 2; | ||
201 | |||
202 | return vx_send_msg(chip, &rmh); | ||
203 | } | ||
204 | |||
205 | |||
206 | /* | ||
207 | * vx_set_format - set the format of a pipe | ||
208 | * @pipe: the affected pipe | ||
209 | * @runtime: pcm runtime instance to be referred | ||
210 | * | ||
211 | * returns 0 if successful, or a negative error code. | ||
212 | */ | ||
213 | static int vx_set_format(vx_core_t *chip, vx_pipe_t *pipe, | ||
214 | snd_pcm_runtime_t *runtime) | ||
215 | { | ||
216 | unsigned int header = HEADER_FMT_BASE; | ||
217 | |||
218 | if (runtime->channels == 1) | ||
219 | header |= HEADER_FMT_MONO; | ||
220 | if (snd_pcm_format_little_endian(runtime->format)) | ||
221 | header |= HEADER_FMT_INTEL; | ||
222 | if (runtime->rate < 32000 && runtime->rate > 11025) | ||
223 | header |= HEADER_FMT_UPTO32; | ||
224 | else if (runtime->rate <= 11025) | ||
225 | header |= HEADER_FMT_UPTO11; | ||
226 | |||
227 | switch (snd_pcm_format_physical_width(runtime->format)) { | ||
228 | // case 8: break; | ||
229 | case 16: header |= HEADER_FMT_16BITS; break; | ||
230 | case 24: header |= HEADER_FMT_24BITS; break; | ||
231 | default : | ||
232 | snd_BUG(); | ||
233 | return -EINVAL; | ||
234 | }; | ||
235 | |||
236 | return vx_set_stream_format(chip, pipe, header); | ||
237 | } | ||
238 | |||
239 | /* | ||
240 | * set / query the IBL size | ||
241 | */ | ||
242 | static int vx_set_ibl(vx_core_t *chip, struct vx_ibl_info *info) | ||
243 | { | ||
244 | int err; | ||
245 | struct vx_rmh rmh; | ||
246 | |||
247 | vx_init_rmh(&rmh, CMD_IBL); | ||
248 | rmh.Cmd[0] |= info->size & 0x03ffff; | ||
249 | err = vx_send_msg(chip, &rmh); | ||
250 | if (err < 0) | ||
251 | return err; | ||
252 | info->size = rmh.Stat[0]; | ||
253 | info->max_size = rmh.Stat[1]; | ||
254 | info->min_size = rmh.Stat[2]; | ||
255 | info->granularity = rmh.Stat[3]; | ||
256 | snd_printdd(KERN_DEBUG "vx_set_ibl: size = %d, max = %d, min = %d, gran = %d\n", | ||
257 | info->size, info->max_size, info->min_size, info->granularity); | ||
258 | return 0; | ||
259 | } | ||
260 | |||
261 | |||
262 | /* | ||
263 | * vx_get_pipe_state - get the state of a pipe | ||
264 | * @pipe: the pipe to be checked | ||
265 | * @state: the pointer for the returned state | ||
266 | * | ||
267 | * checks the state of a given pipe, and stores the state (1 = running, | ||
268 | * 0 = paused) on the given pointer. | ||
269 | * | ||
270 | * called from trigger callback only | ||
271 | */ | ||
272 | static int vx_get_pipe_state(vx_core_t *chip, vx_pipe_t *pipe, int *state) | ||
273 | { | ||
274 | int err; | ||
275 | struct vx_rmh rmh; | ||
276 | |||
277 | vx_init_rmh(&rmh, CMD_PIPE_STATE); | ||
278 | vx_set_pipe_cmd_params(&rmh, pipe->is_capture, pipe->number, 0); | ||
279 | err = vx_send_msg_nolock(chip, &rmh); /* no lock needed for trigger */ | ||
280 | if (! err) | ||
281 | *state = (rmh.Stat[0] & (1 << pipe->number)) ? 1 : 0; | ||
282 | return err; | ||
283 | } | ||
284 | |||
285 | |||
286 | /* | ||
287 | * vx_query_hbuffer_size - query available h-buffer size in bytes | ||
288 | * @pipe: the pipe to be checked | ||
289 | * | ||
290 | * return the available size on h-buffer in bytes, | ||
291 | * or a negative error code. | ||
292 | * | ||
293 | * NOTE: calling this function always switches to the stream mode. | ||
294 | * you'll need to disconnect the host to get back to the | ||
295 | * normal mode. | ||
296 | */ | ||
297 | static int vx_query_hbuffer_size(vx_core_t *chip, vx_pipe_t *pipe) | ||
298 | { | ||
299 | int result; | ||
300 | struct vx_rmh rmh; | ||
301 | |||
302 | vx_init_rmh(&rmh, CMD_SIZE_HBUFFER); | ||
303 | vx_set_pipe_cmd_params(&rmh, pipe->is_capture, pipe->number, 0); | ||
304 | if (pipe->is_capture) | ||
305 | rmh.Cmd[0] |= 0x00000001; | ||
306 | result = vx_send_msg(chip, &rmh); | ||
307 | if (! result) | ||
308 | result = rmh.Stat[0] & 0xffff; | ||
309 | return result; | ||
310 | } | ||
311 | |||
312 | |||
313 | /* | ||
314 | * vx_pipe_can_start - query whether a pipe is ready for start | ||
315 | * @pipe: the pipe to be checked | ||
316 | * | ||
317 | * return 1 if ready, 0 if not ready, and negative value on error. | ||
318 | * | ||
319 | * called from trigger callback only | ||
320 | */ | ||
321 | static int vx_pipe_can_start(vx_core_t *chip, vx_pipe_t *pipe) | ||
322 | { | ||
323 | int err; | ||
324 | struct vx_rmh rmh; | ||
325 | |||
326 | vx_init_rmh(&rmh, CMD_CAN_START_PIPE); | ||
327 | vx_set_pipe_cmd_params(&rmh, pipe->is_capture, pipe->number, 0); | ||
328 | rmh.Cmd[0] |= 1; | ||
329 | |||
330 | err = vx_send_msg_nolock(chip, &rmh); /* no lock needed for trigger */ | ||
331 | if (! err) { | ||
332 | if (rmh.Stat[0]) | ||
333 | err = 1; | ||
334 | } | ||
335 | return err; | ||
336 | } | ||
337 | |||
338 | /* | ||
339 | * vx_conf_pipe - tell the pipe to stand by and wait for IRQA. | ||
340 | * @pipe: the pipe to be configured | ||
341 | */ | ||
342 | static int vx_conf_pipe(vx_core_t *chip, vx_pipe_t *pipe) | ||
343 | { | ||
344 | struct vx_rmh rmh; | ||
345 | |||
346 | vx_init_rmh(&rmh, CMD_CONF_PIPE); | ||
347 | if (pipe->is_capture) | ||
348 | rmh.Cmd[0] |= COMMAND_RECORD_MASK; | ||
349 | rmh.Cmd[1] = 1 << pipe->number; | ||
350 | return vx_send_msg_nolock(chip, &rmh); /* no lock needed for trigger */ | ||
351 | } | ||
352 | |||
353 | /* | ||
354 | * vx_send_irqa - trigger IRQA | ||
355 | */ | ||
356 | static int vx_send_irqa(vx_core_t *chip) | ||
357 | { | ||
358 | struct vx_rmh rmh; | ||
359 | |||
360 | vx_init_rmh(&rmh, CMD_SEND_IRQA); | ||
361 | return vx_send_msg_nolock(chip, &rmh); /* no lock needed for trigger */ | ||
362 | } | ||
363 | |||
364 | |||
365 | #define MAX_WAIT_FOR_DSP 250 | ||
366 | /* | ||
367 | * vx boards do not support inter-card sync, besides | ||
368 | * only 126 samples require to be prepared before a pipe can start | ||
369 | */ | ||
370 | #define CAN_START_DELAY 2 /* wait 2ms only before asking if the pipe is ready*/ | ||
371 | #define WAIT_STATE_DELAY 2 /* wait 2ms after irqA was requested and check if the pipe state toggled*/ | ||
372 | |||
373 | /* | ||
374 | * vx_toggle_pipe - start / pause a pipe | ||
375 | * @pipe: the pipe to be triggered | ||
376 | * @state: start = 1, pause = 0 | ||
377 | * | ||
378 | * called from trigger callback only | ||
379 | * | ||
380 | */ | ||
381 | static int vx_toggle_pipe(vx_core_t *chip, vx_pipe_t *pipe, int state) | ||
382 | { | ||
383 | int err, i, cur_state; | ||
384 | |||
385 | /* Check the pipe is not already in the requested state */ | ||
386 | if (vx_get_pipe_state(chip, pipe, &cur_state) < 0) | ||
387 | return -EBADFD; | ||
388 | if (state == cur_state) | ||
389 | return 0; | ||
390 | |||
391 | /* If a start is requested, ask the DSP to get prepared | ||
392 | * and wait for a positive acknowledge (when there are | ||
393 | * enough sound buffer for this pipe) | ||
394 | */ | ||
395 | if (state) { | ||
396 | for (i = 0 ; i < MAX_WAIT_FOR_DSP; i++) { | ||
397 | err = vx_pipe_can_start(chip, pipe); | ||
398 | if (err > 0) | ||
399 | break; | ||
400 | /* Wait for a few, before asking again | ||
401 | * to avoid flooding the DSP with our requests | ||
402 | */ | ||
403 | mdelay(1); | ||
404 | } | ||
405 | } | ||
406 | |||
407 | if ((err = vx_conf_pipe(chip, pipe)) < 0) | ||
408 | return err; | ||
409 | |||
410 | if ((err = vx_send_irqa(chip)) < 0) | ||
411 | return err; | ||
412 | |||
413 | /* If it completes successfully, wait for the pipes | ||
414 | * reaching the expected state before returning | ||
415 | * Check one pipe only (since they are synchronous) | ||
416 | */ | ||
417 | for (i = 0; i < MAX_WAIT_FOR_DSP; i++) { | ||
418 | err = vx_get_pipe_state(chip, pipe, &cur_state); | ||
419 | if (err < 0 || cur_state == state) | ||
420 | break; | ||
421 | err = -EIO; | ||
422 | mdelay(1); | ||
423 | } | ||
424 | return err < 0 ? -EIO : 0; | ||
425 | } | ||
426 | |||
427 | |||
428 | /* | ||
429 | * vx_stop_pipe - stop a pipe | ||
430 | * @pipe: the pipe to be stopped | ||
431 | * | ||
432 | * called from trigger callback only | ||
433 | */ | ||
434 | static int vx_stop_pipe(vx_core_t *chip, vx_pipe_t *pipe) | ||
435 | { | ||
436 | struct vx_rmh rmh; | ||
437 | vx_init_rmh(&rmh, CMD_STOP_PIPE); | ||
438 | vx_set_pipe_cmd_params(&rmh, pipe->is_capture, pipe->number, 0); | ||
439 | return vx_send_msg_nolock(chip, &rmh); /* no lock needed for trigger */ | ||
440 | } | ||
441 | |||
442 | |||
443 | /* | ||
444 | * vx_alloc_pipe - allocate a pipe and initialize the pipe instance | ||
445 | * @capture: 0 = playback, 1 = capture operation | ||
446 | * @audioid: the audio id to be assigned | ||
447 | * @num_audio: number of audio channels | ||
448 | * @pipep: the returned pipe instance | ||
449 | * | ||
450 | * return 0 on success, or a negative error code. | ||
451 | */ | ||
452 | static int vx_alloc_pipe(vx_core_t *chip, int capture, | ||
453 | int audioid, int num_audio, | ||
454 | vx_pipe_t **pipep) | ||
455 | { | ||
456 | int err; | ||
457 | vx_pipe_t *pipe; | ||
458 | struct vx_rmh rmh; | ||
459 | int data_mode; | ||
460 | |||
461 | *pipep = NULL; | ||
462 | vx_init_rmh(&rmh, CMD_RES_PIPE); | ||
463 | vx_set_pipe_cmd_params(&rmh, capture, audioid, num_audio); | ||
464 | #if 0 // NYI | ||
465 | if (underrun_skip_sound) | ||
466 | rmh.Cmd[0] |= BIT_SKIP_SOUND; | ||
467 | #endif // NYI | ||
468 | data_mode = (chip->uer_bits & IEC958_AES0_NONAUDIO) != 0; | ||
469 | if (! capture && data_mode) | ||
470 | rmh.Cmd[0] |= BIT_DATA_MODE; | ||
471 | err = vx_send_msg(chip, &rmh); | ||
472 | if (err < 0) | ||
473 | return err; | ||
474 | |||
475 | /* initialize the pipe record */ | ||
476 | pipe = kcalloc(1, sizeof(*pipe), GFP_KERNEL); | ||
477 | if (! pipe) { | ||
478 | /* release the pipe */ | ||
479 | vx_init_rmh(&rmh, CMD_FREE_PIPE); | ||
480 | vx_set_pipe_cmd_params(&rmh, capture, audioid, 0); | ||
481 | vx_send_msg(chip, &rmh); | ||
482 | return -ENOMEM; | ||
483 | } | ||
484 | |||
485 | /* the pipe index should be identical with the audio index */ | ||
486 | pipe->number = audioid; | ||
487 | pipe->is_capture = capture; | ||
488 | pipe->channels = num_audio; | ||
489 | pipe->differed_type = 0; | ||
490 | pipe->pcx_time = 0; | ||
491 | pipe->data_mode = data_mode; | ||
492 | *pipep = pipe; | ||
493 | |||
494 | return 0; | ||
495 | } | ||
496 | |||
497 | |||
498 | /* | ||
499 | * vx_free_pipe - release a pipe | ||
500 | * @pipe: pipe to be released | ||
501 | */ | ||
502 | static int vx_free_pipe(vx_core_t *chip, vx_pipe_t *pipe) | ||
503 | { | ||
504 | struct vx_rmh rmh; | ||
505 | |||
506 | vx_init_rmh(&rmh, CMD_FREE_PIPE); | ||
507 | vx_set_pipe_cmd_params(&rmh, pipe->is_capture, pipe->number, 0); | ||
508 | vx_send_msg(chip, &rmh); | ||
509 | |||
510 | kfree(pipe); | ||
511 | return 0; | ||
512 | } | ||
513 | |||
514 | |||
515 | /* | ||
516 | * vx_start_stream - start the stream | ||
517 | * | ||
518 | * called from trigger callback only | ||
519 | */ | ||
520 | static int vx_start_stream(vx_core_t *chip, vx_pipe_t *pipe) | ||
521 | { | ||
522 | struct vx_rmh rmh; | ||
523 | |||
524 | vx_init_rmh(&rmh, CMD_START_ONE_STREAM); | ||
525 | vx_set_stream_cmd_params(&rmh, pipe->is_capture, pipe->number); | ||
526 | vx_set_differed_time(chip, &rmh, pipe); | ||
527 | return vx_send_msg_nolock(chip, &rmh); /* no lock needed for trigger */ | ||
528 | } | ||
529 | |||
530 | |||
531 | /* | ||
532 | * vx_stop_stream - stop the stream | ||
533 | * | ||
534 | * called from trigger callback only | ||
535 | */ | ||
536 | static int vx_stop_stream(vx_core_t *chip, vx_pipe_t *pipe) | ||
537 | { | ||
538 | struct vx_rmh rmh; | ||
539 | |||
540 | vx_init_rmh(&rmh, CMD_STOP_STREAM); | ||
541 | vx_set_stream_cmd_params(&rmh, pipe->is_capture, pipe->number); | ||
542 | return vx_send_msg_nolock(chip, &rmh); /* no lock needed for trigger */ | ||
543 | } | ||
544 | |||
545 | |||
546 | /* | ||
547 | * playback hw information | ||
548 | */ | ||
549 | |||
550 | static snd_pcm_hardware_t vx_pcm_playback_hw = { | ||
551 | .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | | ||
552 | SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_MMAP_VALID | | ||
553 | SNDRV_PCM_INFO_RESUME), | ||
554 | .formats = /*SNDRV_PCM_FMTBIT_U8 |*/ SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_3LE, | ||
555 | .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, | ||
556 | .rate_min = 5000, | ||
557 | .rate_max = 48000, | ||
558 | .channels_min = 1, | ||
559 | .channels_max = 2, | ||
560 | .buffer_bytes_max = (128*1024), | ||
561 | .period_bytes_min = 126, | ||
562 | .period_bytes_max = (128*1024), | ||
563 | .periods_min = 2, | ||
564 | .periods_max = VX_MAX_PERIODS, | ||
565 | .fifo_size = 126, | ||
566 | }; | ||
567 | |||
568 | |||
569 | static void vx_pcm_delayed_start(unsigned long arg); | ||
570 | |||
571 | /* | ||
572 | * vx_pcm_playback_open - open callback for playback | ||
573 | */ | ||
574 | static int vx_pcm_playback_open(snd_pcm_substream_t *subs) | ||
575 | { | ||
576 | snd_pcm_runtime_t *runtime = subs->runtime; | ||
577 | vx_core_t *chip = snd_pcm_substream_chip(subs); | ||
578 | vx_pipe_t *pipe = NULL; | ||
579 | unsigned int audio; | ||
580 | int err; | ||
581 | |||
582 | if (chip->chip_status & VX_STAT_IS_STALE) | ||
583 | return -EBUSY; | ||
584 | |||
585 | audio = subs->pcm->device * 2; | ||
586 | snd_assert(audio < chip->audio_outs, return -EINVAL); | ||
587 | |||
588 | /* playback pipe may have been already allocated for monitoring */ | ||
589 | pipe = chip->playback_pipes[audio]; | ||
590 | if (! pipe) { | ||
591 | /* not allocated yet */ | ||
592 | err = vx_alloc_pipe(chip, 0, audio, 2, &pipe); /* stereo playback */ | ||
593 | if (err < 0) | ||
594 | return err; | ||
595 | chip->playback_pipes[audio] = pipe; | ||
596 | } | ||
597 | /* open for playback */ | ||
598 | pipe->references++; | ||
599 | |||
600 | pipe->substream = subs; | ||
601 | tasklet_init(&pipe->start_tq, vx_pcm_delayed_start, (unsigned long)subs); | ||
602 | chip->playback_pipes[audio] = pipe; | ||
603 | |||
604 | runtime->hw = vx_pcm_playback_hw; | ||
605 | runtime->hw.period_bytes_min = chip->ibl.size; | ||
606 | runtime->private_data = pipe; | ||
607 | |||
608 | /* align to 4 bytes (otherwise will be problematic when 24bit is used) */ | ||
609 | snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 4); | ||
610 | snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 4); | ||
611 | |||
612 | return 0; | ||
613 | } | ||
614 | |||
615 | /* | ||
616 | * vx_pcm_playback_close - close callback for playback | ||
617 | */ | ||
618 | static int vx_pcm_playback_close(snd_pcm_substream_t *subs) | ||
619 | { | ||
620 | vx_core_t *chip = snd_pcm_substream_chip(subs); | ||
621 | vx_pipe_t *pipe; | ||
622 | |||
623 | if (! subs->runtime->private_data) | ||
624 | return -EINVAL; | ||
625 | |||
626 | pipe = subs->runtime->private_data; | ||
627 | |||
628 | if (--pipe->references == 0) { | ||
629 | chip->playback_pipes[pipe->number] = NULL; | ||
630 | vx_free_pipe(chip, pipe); | ||
631 | } | ||
632 | |||
633 | return 0; | ||
634 | |||
635 | } | ||
636 | |||
637 | |||
638 | /* | ||
639 | * vx_notify_end_of_buffer - send "end-of-buffer" notifier at the given pipe | ||
640 | * @pipe: the pipe to notify | ||
641 | * | ||
642 | * NB: call with a certain lock. | ||
643 | */ | ||
644 | static int vx_notify_end_of_buffer(vx_core_t *chip, vx_pipe_t *pipe) | ||
645 | { | ||
646 | int err; | ||
647 | struct vx_rmh rmh; /* use a temporary rmh here */ | ||
648 | |||
649 | /* Toggle Dsp Host Interface into Message mode */ | ||
650 | vx_send_rih_nolock(chip, IRQ_PAUSE_START_CONNECT); | ||
651 | vx_init_rmh(&rmh, CMD_NOTIFY_END_OF_BUFFER); | ||
652 | vx_set_stream_cmd_params(&rmh, 0, pipe->number); | ||
653 | err = vx_send_msg_nolock(chip, &rmh); | ||
654 | if (err < 0) | ||
655 | return err; | ||
656 | /* Toggle Dsp Host Interface back to sound transfer mode */ | ||
657 | vx_send_rih_nolock(chip, IRQ_PAUSE_START_CONNECT); | ||
658 | return 0; | ||
659 | } | ||
660 | |||
661 | /* | ||
662 | * vx_pcm_playback_transfer_chunk - transfer a single chunk | ||
663 | * @subs: substream | ||
664 | * @pipe: the pipe to transfer | ||
665 | * @size: chunk size in bytes | ||
666 | * | ||
667 | * transfer a single buffer chunk. EOB notificaton is added after that. | ||
668 | * called from the interrupt handler, too. | ||
669 | * | ||
670 | * return 0 if ok. | ||
671 | */ | ||
672 | static int vx_pcm_playback_transfer_chunk(vx_core_t *chip, snd_pcm_runtime_t *runtime, vx_pipe_t *pipe, int size) | ||
673 | { | ||
674 | int space, err = 0; | ||
675 | |||
676 | space = vx_query_hbuffer_size(chip, pipe); | ||
677 | if (space < 0) { | ||
678 | /* disconnect the host, SIZE_HBUF command always switches to the stream mode */ | ||
679 | vx_send_rih(chip, IRQ_CONNECT_STREAM_NEXT); | ||
680 | snd_printd("error hbuffer\n"); | ||
681 | return space; | ||
682 | } | ||
683 | if (space < size) { | ||
684 | vx_send_rih(chip, IRQ_CONNECT_STREAM_NEXT); | ||
685 | snd_printd("no enough hbuffer space %d\n", space); | ||
686 | return -EIO; /* XRUN */ | ||
687 | } | ||
688 | |||
689 | /* we don't need irqsave here, because this function | ||
690 | * is called from either trigger callback or irq handler | ||
691 | */ | ||
692 | spin_lock(&chip->lock); | ||
693 | vx_pseudo_dma_write(chip, runtime, pipe, size); | ||
694 | err = vx_notify_end_of_buffer(chip, pipe); | ||
695 | /* disconnect the host, SIZE_HBUF command always switches to the stream mode */ | ||
696 | vx_send_rih_nolock(chip, IRQ_CONNECT_STREAM_NEXT); | ||
697 | spin_unlock(&chip->lock); | ||
698 | return err; | ||
699 | } | ||
700 | |||
701 | /* | ||
702 | * update the position of the given pipe. | ||
703 | * pipe->position is updated and wrapped within the buffer size. | ||
704 | * pipe->transferred is updated, too, but the size is not wrapped, | ||
705 | * so that the caller can check the total transferred size later | ||
706 | * (to call snd_pcm_period_elapsed). | ||
707 | */ | ||
708 | static int vx_update_pipe_position(vx_core_t *chip, snd_pcm_runtime_t *runtime, vx_pipe_t *pipe) | ||
709 | { | ||
710 | struct vx_rmh rmh; | ||
711 | int err, update; | ||
712 | u64 count; | ||
713 | |||
714 | vx_init_rmh(&rmh, CMD_STREAM_SAMPLE_COUNT); | ||
715 | vx_set_pipe_cmd_params(&rmh, pipe->is_capture, pipe->number, 0); | ||
716 | err = vx_send_msg(chip, &rmh); | ||
717 | if (err < 0) | ||
718 | return err; | ||
719 | |||
720 | count = ((u64)(rmh.Stat[0] & 0xfffff) << 24) | (u64)rmh.Stat[1]; | ||
721 | update = (int)(count - pipe->cur_count); | ||
722 | pipe->cur_count = count; | ||
723 | pipe->position += update; | ||
724 | if (pipe->position >= (int)runtime->buffer_size) | ||
725 | pipe->position %= runtime->buffer_size; | ||
726 | pipe->transferred += update; | ||
727 | return 0; | ||
728 | } | ||
729 | |||
730 | /* | ||
731 | * transfer the pending playback buffer data to DSP | ||
732 | * called from interrupt handler | ||
733 | */ | ||
734 | static void vx_pcm_playback_transfer(vx_core_t *chip, snd_pcm_substream_t *subs, vx_pipe_t *pipe, int nchunks) | ||
735 | { | ||
736 | int i, err; | ||
737 | snd_pcm_runtime_t *runtime = subs->runtime; | ||
738 | |||
739 | if (! pipe->prepared || (chip->chip_status & VX_STAT_IS_STALE)) | ||
740 | return; | ||
741 | for (i = 0; i < nchunks; i++) { | ||
742 | if ((err = vx_pcm_playback_transfer_chunk(chip, runtime, pipe, | ||
743 | chip->ibl.size)) < 0) | ||
744 | return; | ||
745 | } | ||
746 | } | ||
747 | |||
748 | /* | ||
749 | * update the playback position and call snd_pcm_period_elapsed() if necessary | ||
750 | * called from interrupt handler | ||
751 | */ | ||
752 | static void vx_pcm_playback_update(vx_core_t *chip, snd_pcm_substream_t *subs, vx_pipe_t *pipe) | ||
753 | { | ||
754 | int err; | ||
755 | snd_pcm_runtime_t *runtime = subs->runtime; | ||
756 | |||
757 | if (pipe->running && ! (chip->chip_status & VX_STAT_IS_STALE)) { | ||
758 | if ((err = vx_update_pipe_position(chip, runtime, pipe)) < 0) | ||
759 | return; | ||
760 | if (pipe->transferred >= (int)runtime->period_size) { | ||
761 | pipe->transferred %= runtime->period_size; | ||
762 | snd_pcm_period_elapsed(subs); | ||
763 | } | ||
764 | } | ||
765 | } | ||
766 | |||
767 | /* | ||
768 | * start the stream and pipe. | ||
769 | * this function is called from tasklet, which is invoked by the trigger | ||
770 | * START callback. | ||
771 | */ | ||
772 | static void vx_pcm_delayed_start(unsigned long arg) | ||
773 | { | ||
774 | snd_pcm_substream_t *subs = (snd_pcm_substream_t *)arg; | ||
775 | vx_core_t *chip = subs->pcm->private_data; | ||
776 | vx_pipe_t *pipe = subs->runtime->private_data; | ||
777 | int err; | ||
778 | |||
779 | /* printk( KERN_DEBUG "DDDD tasklet delayed start jiffies = %ld\n", jiffies);*/ | ||
780 | |||
781 | if ((err = vx_start_stream(chip, pipe)) < 0) { | ||
782 | snd_printk(KERN_ERR "vx: cannot start stream\n"); | ||
783 | return; | ||
784 | } | ||
785 | if ((err = vx_toggle_pipe(chip, pipe, 1)) < 0) { | ||
786 | snd_printk(KERN_ERR "vx: cannot start pipe\n"); | ||
787 | return; | ||
788 | } | ||
789 | /* printk( KERN_DEBUG "dddd tasklet delayed start jiffies = %ld \n", jiffies);*/ | ||
790 | } | ||
791 | |||
792 | /* | ||
793 | * vx_pcm_playback_trigger - trigger callback for playback | ||
794 | */ | ||
795 | static int vx_pcm_trigger(snd_pcm_substream_t *subs, int cmd) | ||
796 | { | ||
797 | vx_core_t *chip = snd_pcm_substream_chip(subs); | ||
798 | vx_pipe_t *pipe = subs->runtime->private_data; | ||
799 | int err; | ||
800 | |||
801 | if (chip->chip_status & VX_STAT_IS_STALE) | ||
802 | return -EBUSY; | ||
803 | |||
804 | switch (cmd) { | ||
805 | case SNDRV_PCM_TRIGGER_START: | ||
806 | case SNDRV_PCM_TRIGGER_RESUME: | ||
807 | if (! pipe->is_capture) | ||
808 | vx_pcm_playback_transfer(chip, subs, pipe, 2); | ||
809 | /* FIXME: | ||
810 | * we trigger the pipe using tasklet, so that the interrupts are | ||
811 | * issued surely after the trigger is completed. | ||
812 | */ | ||
813 | tasklet_hi_schedule(&pipe->start_tq); | ||
814 | chip->pcm_running++; | ||
815 | pipe->running = 1; | ||
816 | break; | ||
817 | case SNDRV_PCM_TRIGGER_STOP: | ||
818 | case SNDRV_PCM_TRIGGER_SUSPEND: | ||
819 | vx_toggle_pipe(chip, pipe, 0); | ||
820 | vx_stop_pipe(chip, pipe); | ||
821 | vx_stop_stream(chip, pipe); | ||
822 | chip->pcm_running--; | ||
823 | pipe->running = 0; | ||
824 | break; | ||
825 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: | ||
826 | if ((err = vx_toggle_pipe(chip, pipe, 0)) < 0) | ||
827 | return err; | ||
828 | break; | ||
829 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: | ||
830 | if ((err = vx_toggle_pipe(chip, pipe, 1)) < 0) | ||
831 | return err; | ||
832 | break; | ||
833 | default: | ||
834 | return -EINVAL; | ||
835 | } | ||
836 | return 0; | ||
837 | } | ||
838 | |||
839 | /* | ||
840 | * vx_pcm_playback_pointer - pointer callback for playback | ||
841 | */ | ||
842 | static snd_pcm_uframes_t vx_pcm_playback_pointer(snd_pcm_substream_t *subs) | ||
843 | { | ||
844 | snd_pcm_runtime_t *runtime = subs->runtime; | ||
845 | vx_pipe_t *pipe = runtime->private_data; | ||
846 | return pipe->position; | ||
847 | } | ||
848 | |||
849 | /* | ||
850 | * vx_pcm_hw_params - hw_params callback for playback and capture | ||
851 | */ | ||
852 | static int vx_pcm_hw_params(snd_pcm_substream_t *subs, | ||
853 | snd_pcm_hw_params_t *hw_params) | ||
854 | { | ||
855 | return snd_pcm_alloc_vmalloc_buffer(subs, params_buffer_bytes(hw_params)); | ||
856 | } | ||
857 | |||
858 | /* | ||
859 | * vx_pcm_hw_free - hw_free callback for playback and capture | ||
860 | */ | ||
861 | static int vx_pcm_hw_free(snd_pcm_substream_t *subs) | ||
862 | { | ||
863 | return snd_pcm_free_vmalloc_buffer(subs); | ||
864 | } | ||
865 | |||
866 | /* | ||
867 | * vx_pcm_prepare - prepare callback for playback and capture | ||
868 | */ | ||
869 | static int vx_pcm_prepare(snd_pcm_substream_t *subs) | ||
870 | { | ||
871 | vx_core_t *chip = snd_pcm_substream_chip(subs); | ||
872 | snd_pcm_runtime_t *runtime = subs->runtime; | ||
873 | vx_pipe_t *pipe = runtime->private_data; | ||
874 | int err, data_mode; | ||
875 | // int max_size, nchunks; | ||
876 | |||
877 | if (chip->chip_status & VX_STAT_IS_STALE) | ||
878 | return -EBUSY; | ||
879 | |||
880 | data_mode = (chip->uer_bits & IEC958_AES0_NONAUDIO) != 0; | ||
881 | if (data_mode != pipe->data_mode && ! pipe->is_capture) { | ||
882 | /* IEC958 status (raw-mode) was changed */ | ||
883 | /* we reopen the pipe */ | ||
884 | struct vx_rmh rmh; | ||
885 | snd_printdd(KERN_DEBUG "reopen the pipe with data_mode = %d\n", data_mode); | ||
886 | vx_init_rmh(&rmh, CMD_FREE_PIPE); | ||
887 | vx_set_pipe_cmd_params(&rmh, 0, pipe->number, 0); | ||
888 | if ((err = vx_send_msg(chip, &rmh)) < 0) | ||
889 | return err; | ||
890 | vx_init_rmh(&rmh, CMD_RES_PIPE); | ||
891 | vx_set_pipe_cmd_params(&rmh, 0, pipe->number, pipe->channels); | ||
892 | if (data_mode) | ||
893 | rmh.Cmd[0] |= BIT_DATA_MODE; | ||
894 | if ((err = vx_send_msg(chip, &rmh)) < 0) | ||
895 | return err; | ||
896 | pipe->data_mode = data_mode; | ||
897 | } | ||
898 | |||
899 | if (chip->pcm_running && chip->freq != runtime->rate) { | ||
900 | snd_printk(KERN_ERR "vx: cannot set different clock %d from the current %d\n", runtime->rate, chip->freq); | ||
901 | return -EINVAL; | ||
902 | } | ||
903 | vx_set_clock(chip, runtime->rate); | ||
904 | |||
905 | if ((err = vx_set_format(chip, pipe, runtime)) < 0) | ||
906 | return err; | ||
907 | |||
908 | if (vx_is_pcmcia(chip)) { | ||
909 | pipe->align = 2; /* 16bit word */ | ||
910 | } else { | ||
911 | pipe->align = 4; /* 32bit word */ | ||
912 | } | ||
913 | |||
914 | pipe->buffer_bytes = frames_to_bytes(runtime, runtime->buffer_size); | ||
915 | pipe->period_bytes = frames_to_bytes(runtime, runtime->period_size); | ||
916 | pipe->hw_ptr = 0; | ||
917 | |||
918 | /* set the timestamp */ | ||
919 | vx_update_pipe_position(chip, runtime, pipe); | ||
920 | /* clear again */ | ||
921 | pipe->transferred = 0; | ||
922 | pipe->position = 0; | ||
923 | |||
924 | pipe->prepared = 1; | ||
925 | |||
926 | return 0; | ||
927 | } | ||
928 | |||
929 | |||
930 | /* | ||
931 | * operators for PCM playback | ||
932 | */ | ||
933 | static snd_pcm_ops_t vx_pcm_playback_ops = { | ||
934 | .open = vx_pcm_playback_open, | ||
935 | .close = vx_pcm_playback_close, | ||
936 | .ioctl = snd_pcm_lib_ioctl, | ||
937 | .hw_params = vx_pcm_hw_params, | ||
938 | .hw_free = vx_pcm_hw_free, | ||
939 | .prepare = vx_pcm_prepare, | ||
940 | .trigger = vx_pcm_trigger, | ||
941 | .pointer = vx_pcm_playback_pointer, | ||
942 | .page = snd_pcm_get_vmalloc_page, | ||
943 | }; | ||
944 | |||
945 | |||
946 | /* | ||
947 | * playback hw information | ||
948 | */ | ||
949 | |||
950 | static snd_pcm_hardware_t vx_pcm_capture_hw = { | ||
951 | .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | | ||
952 | SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_MMAP_VALID | | ||
953 | SNDRV_PCM_INFO_RESUME), | ||
954 | .formats = /*SNDRV_PCM_FMTBIT_U8 |*/ SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_3LE, | ||
955 | .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, | ||
956 | .rate_min = 5000, | ||
957 | .rate_max = 48000, | ||
958 | .channels_min = 1, | ||
959 | .channels_max = 2, | ||
960 | .buffer_bytes_max = (128*1024), | ||
961 | .period_bytes_min = 126, | ||
962 | .period_bytes_max = (128*1024), | ||
963 | .periods_min = 2, | ||
964 | .periods_max = VX_MAX_PERIODS, | ||
965 | .fifo_size = 126, | ||
966 | }; | ||
967 | |||
968 | |||
969 | /* | ||
970 | * vx_pcm_capture_open - open callback for capture | ||
971 | */ | ||
972 | static int vx_pcm_capture_open(snd_pcm_substream_t *subs) | ||
973 | { | ||
974 | snd_pcm_runtime_t *runtime = subs->runtime; | ||
975 | vx_core_t *chip = snd_pcm_substream_chip(subs); | ||
976 | vx_pipe_t *pipe; | ||
977 | vx_pipe_t *pipe_out_monitoring = NULL; | ||
978 | unsigned int audio; | ||
979 | int err; | ||
980 | |||
981 | if (chip->chip_status & VX_STAT_IS_STALE) | ||
982 | return -EBUSY; | ||
983 | |||
984 | audio = subs->pcm->device * 2; | ||
985 | snd_assert(audio < chip->audio_ins, return -EINVAL); | ||
986 | err = vx_alloc_pipe(chip, 1, audio, 2, &pipe); | ||
987 | if (err < 0) | ||
988 | return err; | ||
989 | pipe->substream = subs; | ||
990 | tasklet_init(&pipe->start_tq, vx_pcm_delayed_start, (unsigned long)subs); | ||
991 | chip->capture_pipes[audio] = pipe; | ||
992 | |||
993 | /* check if monitoring is needed */ | ||
994 | if (chip->audio_monitor_active[audio]) { | ||
995 | pipe_out_monitoring = chip->playback_pipes[audio]; | ||
996 | if (! pipe_out_monitoring) { | ||
997 | /* allocate a pipe */ | ||
998 | err = vx_alloc_pipe(chip, 0, audio, 2, &pipe_out_monitoring); | ||
999 | if (err < 0) | ||
1000 | return err; | ||
1001 | chip->playback_pipes[audio] = pipe_out_monitoring; | ||
1002 | } | ||
1003 | pipe_out_monitoring->references++; | ||
1004 | /* | ||
1005 | if an output pipe is available, it's audios still may need to be | ||
1006 | unmuted. hence we'll have to call a mixer entry point. | ||
1007 | */ | ||
1008 | vx_set_monitor_level(chip, audio, chip->audio_monitor[audio], chip->audio_monitor_active[audio]); | ||
1009 | /* assuming stereo */ | ||
1010 | vx_set_monitor_level(chip, audio+1, chip->audio_monitor[audio+1], chip->audio_monitor_active[audio+1]); | ||
1011 | } | ||
1012 | |||
1013 | pipe->monitoring_pipe = pipe_out_monitoring; /* default value NULL */ | ||
1014 | |||
1015 | runtime->hw = vx_pcm_capture_hw; | ||
1016 | runtime->hw.period_bytes_min = chip->ibl.size; | ||
1017 | runtime->private_data = pipe; | ||
1018 | |||
1019 | /* align to 4 bytes (otherwise will be problematic when 24bit is used) */ | ||
1020 | snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 4); | ||
1021 | snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 4); | ||
1022 | |||
1023 | return 0; | ||
1024 | } | ||
1025 | |||
1026 | /* | ||
1027 | * vx_pcm_capture_close - close callback for capture | ||
1028 | */ | ||
1029 | static int vx_pcm_capture_close(snd_pcm_substream_t *subs) | ||
1030 | { | ||
1031 | vx_core_t *chip = snd_pcm_substream_chip(subs); | ||
1032 | vx_pipe_t *pipe; | ||
1033 | vx_pipe_t *pipe_out_monitoring; | ||
1034 | |||
1035 | if (! subs->runtime->private_data) | ||
1036 | return -EINVAL; | ||
1037 | pipe = subs->runtime->private_data; | ||
1038 | chip->capture_pipes[pipe->number] = NULL; | ||
1039 | |||
1040 | pipe_out_monitoring = pipe->monitoring_pipe; | ||
1041 | |||
1042 | /* | ||
1043 | if an output pipe is attached to this input, | ||
1044 | check if it needs to be released. | ||
1045 | */ | ||
1046 | if (pipe_out_monitoring) { | ||
1047 | if (--pipe_out_monitoring->references == 0) { | ||
1048 | vx_free_pipe(chip, pipe_out_monitoring); | ||
1049 | chip->playback_pipes[pipe->number] = NULL; | ||
1050 | pipe->monitoring_pipe = NULL; | ||
1051 | } | ||
1052 | } | ||
1053 | |||
1054 | vx_free_pipe(chip, pipe); | ||
1055 | return 0; | ||
1056 | } | ||
1057 | |||
1058 | |||
1059 | |||
1060 | #define DMA_READ_ALIGN 6 /* hardware alignment for read */ | ||
1061 | |||
1062 | /* | ||
1063 | * vx_pcm_capture_update - update the capture buffer | ||
1064 | */ | ||
1065 | static void vx_pcm_capture_update(vx_core_t *chip, snd_pcm_substream_t *subs, vx_pipe_t *pipe) | ||
1066 | { | ||
1067 | int size, space, count; | ||
1068 | snd_pcm_runtime_t *runtime = subs->runtime; | ||
1069 | |||
1070 | if (! pipe->prepared || (chip->chip_status & VX_STAT_IS_STALE)) | ||
1071 | return; | ||
1072 | |||
1073 | size = runtime->buffer_size - snd_pcm_capture_avail(runtime); | ||
1074 | if (! size) | ||
1075 | return; | ||
1076 | size = frames_to_bytes(runtime, size); | ||
1077 | space = vx_query_hbuffer_size(chip, pipe); | ||
1078 | if (space < 0) | ||
1079 | goto _error; | ||
1080 | if (size > space) | ||
1081 | size = space; | ||
1082 | size = (size / 3) * 3; /* align to 3 bytes */ | ||
1083 | if (size < DMA_READ_ALIGN) | ||
1084 | goto _error; | ||
1085 | |||
1086 | /* keep the last 6 bytes, they will be read after disconnection */ | ||
1087 | count = size - DMA_READ_ALIGN; | ||
1088 | /* read bytes until the current pointer reaches to the aligned position | ||
1089 | * for word-transfer | ||
1090 | */ | ||
1091 | while (count > 0) { | ||
1092 | if ((pipe->hw_ptr % pipe->align) == 0) | ||
1093 | break; | ||
1094 | if (vx_wait_for_rx_full(chip) < 0) | ||
1095 | goto _error; | ||
1096 | vx_pcm_read_per_bytes(chip, runtime, pipe); | ||
1097 | count -= 3; | ||
1098 | } | ||
1099 | if (count > 0) { | ||
1100 | /* ok, let's accelerate! */ | ||
1101 | int align = pipe->align * 3; | ||
1102 | space = (count / align) * align; | ||
1103 | vx_pseudo_dma_read(chip, runtime, pipe, space); | ||
1104 | count -= space; | ||
1105 | } | ||
1106 | /* read the rest of bytes */ | ||
1107 | while (count > 0) { | ||
1108 | if (vx_wait_for_rx_full(chip) < 0) | ||
1109 | goto _error; | ||
1110 | vx_pcm_read_per_bytes(chip, runtime, pipe); | ||
1111 | count -= 3; | ||
1112 | } | ||
1113 | /* disconnect the host, SIZE_HBUF command always switches to the stream mode */ | ||
1114 | vx_send_rih_nolock(chip, IRQ_CONNECT_STREAM_NEXT); | ||
1115 | /* read the last pending 6 bytes */ | ||
1116 | count = DMA_READ_ALIGN; | ||
1117 | while (count > 0) { | ||
1118 | vx_pcm_read_per_bytes(chip, runtime, pipe); | ||
1119 | count -= 3; | ||
1120 | } | ||
1121 | /* update the position */ | ||
1122 | pipe->transferred += size; | ||
1123 | if (pipe->transferred >= pipe->period_bytes) { | ||
1124 | pipe->transferred %= pipe->period_bytes; | ||
1125 | snd_pcm_period_elapsed(subs); | ||
1126 | } | ||
1127 | return; | ||
1128 | |||
1129 | _error: | ||
1130 | /* disconnect the host, SIZE_HBUF command always switches to the stream mode */ | ||
1131 | vx_send_rih_nolock(chip, IRQ_CONNECT_STREAM_NEXT); | ||
1132 | return; | ||
1133 | } | ||
1134 | |||
1135 | /* | ||
1136 | * vx_pcm_capture_pointer - pointer callback for capture | ||
1137 | */ | ||
1138 | static snd_pcm_uframes_t vx_pcm_capture_pointer(snd_pcm_substream_t *subs) | ||
1139 | { | ||
1140 | snd_pcm_runtime_t *runtime = subs->runtime; | ||
1141 | vx_pipe_t *pipe = runtime->private_data; | ||
1142 | return bytes_to_frames(runtime, pipe->hw_ptr); | ||
1143 | } | ||
1144 | |||
1145 | /* | ||
1146 | * operators for PCM capture | ||
1147 | */ | ||
1148 | static snd_pcm_ops_t vx_pcm_capture_ops = { | ||
1149 | .open = vx_pcm_capture_open, | ||
1150 | .close = vx_pcm_capture_close, | ||
1151 | .ioctl = snd_pcm_lib_ioctl, | ||
1152 | .hw_params = vx_pcm_hw_params, | ||
1153 | .hw_free = vx_pcm_hw_free, | ||
1154 | .prepare = vx_pcm_prepare, | ||
1155 | .trigger = vx_pcm_trigger, | ||
1156 | .pointer = vx_pcm_capture_pointer, | ||
1157 | .page = snd_pcm_get_vmalloc_page, | ||
1158 | }; | ||
1159 | |||
1160 | |||
1161 | /* | ||
1162 | * interrupt handler for pcm streams | ||
1163 | */ | ||
1164 | void vx_pcm_update_intr(vx_core_t *chip, unsigned int events) | ||
1165 | { | ||
1166 | unsigned int i; | ||
1167 | vx_pipe_t *pipe; | ||
1168 | |||
1169 | #define EVENT_MASK (END_OF_BUFFER_EVENTS_PENDING|ASYNC_EVENTS_PENDING) | ||
1170 | |||
1171 | if (events & EVENT_MASK) { | ||
1172 | vx_init_rmh(&chip->irq_rmh, CMD_ASYNC); | ||
1173 | if (events & ASYNC_EVENTS_PENDING) | ||
1174 | chip->irq_rmh.Cmd[0] |= 0x00000001; /* SEL_ASYNC_EVENTS */ | ||
1175 | if (events & END_OF_BUFFER_EVENTS_PENDING) | ||
1176 | chip->irq_rmh.Cmd[0] |= 0x00000002; /* SEL_END_OF_BUF_EVENTS */ | ||
1177 | |||
1178 | if (vx_send_msg(chip, &chip->irq_rmh) < 0) { | ||
1179 | snd_printdd(KERN_ERR "msg send error!!\n"); | ||
1180 | return; | ||
1181 | } | ||
1182 | |||
1183 | i = 1; | ||
1184 | while (i < chip->irq_rmh.LgStat) { | ||
1185 | int p, buf, capture, eob; | ||
1186 | p = chip->irq_rmh.Stat[i] & MASK_FIRST_FIELD; | ||
1187 | capture = (chip->irq_rmh.Stat[i] & 0x400000) ? 1 : 0; | ||
1188 | eob = (chip->irq_rmh.Stat[i] & 0x800000) ? 1 : 0; | ||
1189 | i++; | ||
1190 | if (events & ASYNC_EVENTS_PENDING) | ||
1191 | i++; | ||
1192 | buf = 1; /* force to transfer */ | ||
1193 | if (events & END_OF_BUFFER_EVENTS_PENDING) { | ||
1194 | if (eob) | ||
1195 | buf = chip->irq_rmh.Stat[i]; | ||
1196 | i++; | ||
1197 | } | ||
1198 | if (capture) | ||
1199 | continue; | ||
1200 | snd_assert(p >= 0 && (unsigned int)p < chip->audio_outs,); | ||
1201 | pipe = chip->playback_pipes[p]; | ||
1202 | if (pipe && pipe->substream) { | ||
1203 | vx_pcm_playback_update(chip, pipe->substream, pipe); | ||
1204 | vx_pcm_playback_transfer(chip, pipe->substream, pipe, buf); | ||
1205 | } | ||
1206 | } | ||
1207 | } | ||
1208 | |||
1209 | /* update the capture pcm pointers as frequently as possible */ | ||
1210 | for (i = 0; i < chip->audio_ins; i++) { | ||
1211 | pipe = chip->capture_pipes[i]; | ||
1212 | if (pipe && pipe->substream) | ||
1213 | vx_pcm_capture_update(chip, pipe->substream, pipe); | ||
1214 | } | ||
1215 | } | ||
1216 | |||
1217 | |||
1218 | /* | ||
1219 | * vx_init_audio_io - check the availabe audio i/o and allocate pipe arrays | ||
1220 | */ | ||
1221 | static int vx_init_audio_io(vx_core_t *chip) | ||
1222 | { | ||
1223 | struct vx_rmh rmh; | ||
1224 | int preferred; | ||
1225 | |||
1226 | vx_init_rmh(&rmh, CMD_SUPPORTED); | ||
1227 | if (vx_send_msg(chip, &rmh) < 0) { | ||
1228 | snd_printk(KERN_ERR "vx: cannot get the supported audio data\n"); | ||
1229 | return -ENXIO; | ||
1230 | } | ||
1231 | |||
1232 | chip->audio_outs = rmh.Stat[0] & MASK_FIRST_FIELD; | ||
1233 | chip->audio_ins = (rmh.Stat[0] >> (FIELD_SIZE*2)) & MASK_FIRST_FIELD; | ||
1234 | chip->audio_info = rmh.Stat[1]; | ||
1235 | |||
1236 | /* allocate pipes */ | ||
1237 | chip->playback_pipes = kmalloc(sizeof(vx_pipe_t *) * chip->audio_outs, GFP_KERNEL); | ||
1238 | chip->capture_pipes = kmalloc(sizeof(vx_pipe_t *) * chip->audio_ins, GFP_KERNEL); | ||
1239 | if (! chip->playback_pipes || ! chip->capture_pipes) | ||
1240 | return -ENOMEM; | ||
1241 | |||
1242 | memset(chip->playback_pipes, 0, sizeof(vx_pipe_t *) * chip->audio_outs); | ||
1243 | memset(chip->capture_pipes, 0, sizeof(vx_pipe_t *) * chip->audio_ins); | ||
1244 | |||
1245 | preferred = chip->ibl.size; | ||
1246 | chip->ibl.size = 0; | ||
1247 | vx_set_ibl(chip, &chip->ibl); /* query the info */ | ||
1248 | if (preferred > 0) { | ||
1249 | chip->ibl.size = ((preferred + chip->ibl.granularity - 1) / chip->ibl.granularity) * chip->ibl.granularity; | ||
1250 | if (chip->ibl.size > chip->ibl.max_size) | ||
1251 | chip->ibl.size = chip->ibl.max_size; | ||
1252 | } else | ||
1253 | chip->ibl.size = chip->ibl.min_size; /* set to the minimum */ | ||
1254 | vx_set_ibl(chip, &chip->ibl); | ||
1255 | |||
1256 | return 0; | ||
1257 | } | ||
1258 | |||
1259 | |||
1260 | /* | ||
1261 | * free callback for pcm | ||
1262 | */ | ||
1263 | static void snd_vx_pcm_free(snd_pcm_t *pcm) | ||
1264 | { | ||
1265 | vx_core_t *chip = pcm->private_data; | ||
1266 | chip->pcm[pcm->device] = NULL; | ||
1267 | if (chip->playback_pipes) { | ||
1268 | kfree(chip->playback_pipes); | ||
1269 | chip->playback_pipes = NULL; | ||
1270 | } | ||
1271 | if (chip->capture_pipes) { | ||
1272 | kfree(chip->capture_pipes); | ||
1273 | chip->capture_pipes = NULL; | ||
1274 | } | ||
1275 | } | ||
1276 | |||
1277 | /* | ||
1278 | * snd_vx_pcm_new - create and initialize a pcm | ||
1279 | */ | ||
1280 | int snd_vx_pcm_new(vx_core_t *chip) | ||
1281 | { | ||
1282 | snd_pcm_t *pcm; | ||
1283 | unsigned int i; | ||
1284 | int err; | ||
1285 | |||
1286 | if ((err = vx_init_audio_io(chip)) < 0) | ||
1287 | return err; | ||
1288 | |||
1289 | for (i = 0; i < chip->hw->num_codecs; i++) { | ||
1290 | unsigned int outs, ins; | ||
1291 | outs = chip->audio_outs > i * 2 ? 1 : 0; | ||
1292 | ins = chip->audio_ins > i * 2 ? 1 : 0; | ||
1293 | if (! outs && ! ins) | ||
1294 | break; | ||
1295 | err = snd_pcm_new(chip->card, "VX PCM", i, | ||
1296 | outs, ins, &pcm); | ||
1297 | if (err < 0) | ||
1298 | return err; | ||
1299 | if (outs) | ||
1300 | snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &vx_pcm_playback_ops); | ||
1301 | if (ins) | ||
1302 | snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &vx_pcm_capture_ops); | ||
1303 | |||
1304 | pcm->private_data = chip; | ||
1305 | pcm->private_free = snd_vx_pcm_free; | ||
1306 | pcm->info_flags = 0; | ||
1307 | strcpy(pcm->name, chip->card->shortname); | ||
1308 | chip->pcm[i] = pcm; | ||
1309 | } | ||
1310 | |||
1311 | return 0; | ||
1312 | } | ||