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