]>
Commit | Line | Data |
---|---|---|
f3d9478b JB |
1 | /* |
2 | * i2sbus driver -- pcm routines | |
3 | * | |
4 | * Copyright 2006 Johannes Berg <johannes@sipsolutions.net> | |
5 | * | |
6 | * GPL v2, can be found in COPYING. | |
7 | */ | |
8 | ||
9 | #include <asm/io.h> | |
10 | #include <linux/delay.h> | |
5a0e3ad6 | 11 | #include <linux/slab.h> |
f3d9478b JB |
12 | #include <sound/core.h> |
13 | #include <asm/macio.h> | |
14 | #include <linux/pci.h> | |
15 | #include "../soundbus.h" | |
16 | #include "i2sbus.h" | |
17 | ||
18 | static inline void get_pcm_info(struct i2sbus_dev *i2sdev, int in, | |
19 | struct pcm_info **pi, struct pcm_info **other) | |
20 | { | |
21 | if (in) { | |
22 | if (pi) | |
23 | *pi = &i2sdev->in; | |
24 | if (other) | |
25 | *other = &i2sdev->out; | |
26 | } else { | |
27 | if (pi) | |
28 | *pi = &i2sdev->out; | |
29 | if (other) | |
30 | *other = &i2sdev->in; | |
31 | } | |
32 | } | |
33 | ||
34 | static int clock_and_divisors(int mclk, int sclk, int rate, int *out) | |
35 | { | |
36 | /* sclk must be derived from mclk! */ | |
37 | if (mclk % sclk) | |
38 | return -1; | |
39 | /* derive sclk register value */ | |
40 | if (i2s_sf_sclkdiv(mclk / sclk, out)) | |
41 | return -1; | |
42 | ||
43 | if (I2S_CLOCK_SPEED_18MHz % (rate * mclk) == 0) { | |
44 | if (!i2s_sf_mclkdiv(I2S_CLOCK_SPEED_18MHz / (rate * mclk), out)) { | |
45 | *out |= I2S_SF_CLOCK_SOURCE_18MHz; | |
46 | return 0; | |
47 | } | |
48 | } | |
49 | if (I2S_CLOCK_SPEED_45MHz % (rate * mclk) == 0) { | |
50 | if (!i2s_sf_mclkdiv(I2S_CLOCK_SPEED_45MHz / (rate * mclk), out)) { | |
51 | *out |= I2S_SF_CLOCK_SOURCE_45MHz; | |
52 | return 0; | |
53 | } | |
54 | } | |
55 | if (I2S_CLOCK_SPEED_49MHz % (rate * mclk) == 0) { | |
56 | if (!i2s_sf_mclkdiv(I2S_CLOCK_SPEED_49MHz / (rate * mclk), out)) { | |
57 | *out |= I2S_SF_CLOCK_SOURCE_49MHz; | |
58 | return 0; | |
59 | } | |
60 | } | |
61 | return -1; | |
62 | } | |
63 | ||
64 | #define CHECK_RATE(rate) \ | |
65 | do { if (rates & SNDRV_PCM_RATE_ ##rate) { \ | |
66 | int dummy; \ | |
67 | if (clock_and_divisors(sysclock_factor, \ | |
68 | bus_factor, rate, &dummy)) \ | |
69 | rates &= ~SNDRV_PCM_RATE_ ##rate; \ | |
70 | } } while (0) | |
71 | ||
72 | static int i2sbus_pcm_open(struct i2sbus_dev *i2sdev, int in) | |
73 | { | |
74 | struct pcm_info *pi, *other; | |
75 | struct soundbus_dev *sdev; | |
76 | int masks_inited = 0, err; | |
77 | struct codec_info_item *cii, *rev; | |
78 | struct snd_pcm_hardware *hw; | |
79 | u64 formats = 0; | |
80 | unsigned int rates = 0; | |
81 | struct transfer_info v; | |
82 | int result = 0; | |
83 | int bus_factor = 0, sysclock_factor = 0; | |
84 | int found_this; | |
85 | ||
86 | mutex_lock(&i2sdev->lock); | |
87 | ||
88 | get_pcm_info(i2sdev, in, &pi, &other); | |
89 | ||
90 | hw = &pi->substream->runtime->hw; | |
91 | sdev = &i2sdev->sound; | |
92 | ||
93 | if (pi->active) { | |
94 | /* alsa messed up */ | |
95 | result = -EBUSY; | |
96 | goto out_unlock; | |
97 | } | |
98 | ||
99 | /* we now need to assign the hw */ | |
100 | list_for_each_entry(cii, &sdev->codec_list, list) { | |
101 | struct transfer_info *ti = cii->codec->transfers; | |
102 | bus_factor = cii->codec->bus_factor; | |
103 | sysclock_factor = cii->codec->sysclock_factor; | |
104 | while (ti->formats && ti->rates) { | |
105 | v = *ti; | |
106 | if (ti->transfer_in == in | |
107 | && cii->codec->usable(cii, ti, &v)) { | |
108 | if (masks_inited) { | |
109 | formats &= v.formats; | |
110 | rates &= v.rates; | |
111 | } else { | |
112 | formats = v.formats; | |
113 | rates = v.rates; | |
114 | masks_inited = 1; | |
115 | } | |
116 | } | |
117 | ti++; | |
118 | } | |
119 | } | |
120 | if (!masks_inited || !bus_factor || !sysclock_factor) { | |
121 | result = -ENODEV; | |
122 | goto out_unlock; | |
123 | } | |
124 | /* bus dependent stuff */ | |
125 | hw->info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | | |
547ac2ae PM |
126 | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_RESUME | |
127 | SNDRV_PCM_INFO_JOINT_DUPLEX; | |
f3d9478b JB |
128 | |
129 | CHECK_RATE(5512); | |
130 | CHECK_RATE(8000); | |
131 | CHECK_RATE(11025); | |
132 | CHECK_RATE(16000); | |
133 | CHECK_RATE(22050); | |
134 | CHECK_RATE(32000); | |
135 | CHECK_RATE(44100); | |
136 | CHECK_RATE(48000); | |
137 | CHECK_RATE(64000); | |
138 | CHECK_RATE(88200); | |
139 | CHECK_RATE(96000); | |
140 | CHECK_RATE(176400); | |
141 | CHECK_RATE(192000); | |
142 | hw->rates = rates; | |
143 | ||
144 | /* well. the codec might want 24 bits only, and we'll | |
145 | * ever only transfer 24 bits, but they are top-aligned! | |
146 | * So for alsa, we claim that we're doing full 32 bit | |
147 | * while in reality we'll ignore the lower 8 bits of | |
148 | * that when doing playback (they're transferred as 0 | |
149 | * as far as I know, no codecs we have are 32-bit capable | |
150 | * so I can't really test) and when doing recording we'll | |
151 | * always have those lower 8 bits recorded as 0 */ | |
152 | if (formats & SNDRV_PCM_FMTBIT_S24_BE) | |
153 | formats |= SNDRV_PCM_FMTBIT_S32_BE; | |
154 | if (formats & SNDRV_PCM_FMTBIT_U24_BE) | |
155 | formats |= SNDRV_PCM_FMTBIT_U32_BE; | |
156 | /* now mask off what we can support. I suppose we could | |
157 | * also support S24_3LE and some similar formats, but I | |
158 | * doubt there's a codec that would be able to use that, | |
159 | * so we don't support it here. */ | |
160 | hw->formats = formats & (SNDRV_PCM_FMTBIT_S16_BE | | |
161 | SNDRV_PCM_FMTBIT_U16_BE | | |
162 | SNDRV_PCM_FMTBIT_S32_BE | | |
163 | SNDRV_PCM_FMTBIT_U32_BE); | |
164 | ||
165 | /* we need to set the highest and lowest rate possible. | |
166 | * These are the highest and lowest rates alsa can | |
167 | * support properly in its bitfield. | |
168 | * Below, we'll use that to restrict to the rate | |
169 | * currently in use (if any). */ | |
170 | hw->rate_min = 5512; | |
171 | hw->rate_max = 192000; | |
172 | /* if the other stream is active, then we can only | |
173 | * support what it is currently using. | |
174 | * FIXME: I lied. This comment is wrong. We can support | |
175 | * anything that works with the same serial format, ie. | |
176 | * when recording 24 bit sound we can well play 16 bit | |
177 | * sound at the same time iff using the same transfer mode. | |
178 | */ | |
179 | if (other->active) { | |
180 | /* FIXME: is this guaranteed by the alsa api? */ | |
181 | hw->formats &= (1ULL << i2sdev->format); | |
182 | /* see above, restrict rates to the one we already have */ | |
183 | hw->rate_min = i2sdev->rate; | |
184 | hw->rate_max = i2sdev->rate; | |
185 | } | |
186 | ||
187 | hw->channels_min = 2; | |
188 | hw->channels_max = 2; | |
189 | /* these are somewhat arbitrary */ | |
190 | hw->buffer_bytes_max = 131072; | |
191 | hw->period_bytes_min = 256; | |
192 | hw->period_bytes_max = 16384; | |
193 | hw->periods_min = 3; | |
194 | hw->periods_max = MAX_DBDMA_COMMANDS; | |
df86d114 HL |
195 | err = snd_pcm_hw_constraint_integer(pi->substream->runtime, |
196 | SNDRV_PCM_HW_PARAM_PERIODS); | |
197 | if (err < 0) { | |
198 | result = err; | |
199 | goto out_unlock; | |
200 | } | |
f3d9478b JB |
201 | list_for_each_entry(cii, &sdev->codec_list, list) { |
202 | if (cii->codec->open) { | |
203 | err = cii->codec->open(cii, pi->substream); | |
204 | if (err) { | |
205 | result = err; | |
206 | /* unwind */ | |
207 | found_this = 0; | |
208 | list_for_each_entry_reverse(rev, | |
209 | &sdev->codec_list, list) { | |
210 | if (found_this && rev->codec->close) { | |
211 | rev->codec->close(rev, | |
212 | pi->substream); | |
213 | } | |
214 | if (rev == cii) | |
215 | found_this = 1; | |
216 | } | |
217 | goto out_unlock; | |
218 | } | |
219 | } | |
220 | } | |
221 | ||
222 | out_unlock: | |
223 | mutex_unlock(&i2sdev->lock); | |
224 | return result; | |
225 | } | |
226 | ||
227 | #undef CHECK_RATE | |
228 | ||
229 | static int i2sbus_pcm_close(struct i2sbus_dev *i2sdev, int in) | |
230 | { | |
231 | struct codec_info_item *cii; | |
232 | struct pcm_info *pi; | |
233 | int err = 0, tmp; | |
234 | ||
235 | mutex_lock(&i2sdev->lock); | |
236 | ||
237 | get_pcm_info(i2sdev, in, &pi, NULL); | |
238 | ||
239 | list_for_each_entry(cii, &i2sdev->sound.codec_list, list) { | |
240 | if (cii->codec->close) { | |
241 | tmp = cii->codec->close(cii, pi->substream); | |
242 | if (tmp) | |
243 | err = tmp; | |
244 | } | |
245 | } | |
246 | ||
247 | pi->substream = NULL; | |
248 | pi->active = 0; | |
249 | mutex_unlock(&i2sdev->lock); | |
250 | return err; | |
251 | } | |
252 | ||
547ac2ae PM |
253 | static void i2sbus_wait_for_stop(struct i2sbus_dev *i2sdev, |
254 | struct pcm_info *pi) | |
255 | { | |
256 | unsigned long flags; | |
257 | struct completion done; | |
258 | long timeout; | |
259 | ||
260 | spin_lock_irqsave(&i2sdev->low_lock, flags); | |
261 | if (pi->dbdma_ring.stopping) { | |
262 | init_completion(&done); | |
263 | pi->stop_completion = &done; | |
264 | spin_unlock_irqrestore(&i2sdev->low_lock, flags); | |
265 | timeout = wait_for_completion_timeout(&done, HZ); | |
266 | spin_lock_irqsave(&i2sdev->low_lock, flags); | |
267 | pi->stop_completion = NULL; | |
268 | if (timeout == 0) { | |
269 | /* timeout expired, stop dbdma forcefully */ | |
270 | printk(KERN_ERR "i2sbus_wait_for_stop: timed out\n"); | |
271 | /* make sure RUN, PAUSE and S0 bits are cleared */ | |
272 | out_le32(&pi->dbdma->control, (RUN | PAUSE | 1) << 16); | |
273 | pi->dbdma_ring.stopping = 0; | |
274 | timeout = 10; | |
275 | while (in_le32(&pi->dbdma->status) & ACTIVE) { | |
276 | if (--timeout <= 0) | |
277 | break; | |
278 | udelay(1); | |
279 | } | |
280 | } | |
281 | } | |
282 | spin_unlock_irqrestore(&i2sdev->low_lock, flags); | |
283 | } | |
284 | ||
285 | #ifdef CONFIG_PM | |
286 | void i2sbus_wait_for_stop_both(struct i2sbus_dev *i2sdev) | |
287 | { | |
288 | struct pcm_info *pi; | |
289 | ||
290 | get_pcm_info(i2sdev, 0, &pi, NULL); | |
291 | i2sbus_wait_for_stop(i2sdev, pi); | |
292 | get_pcm_info(i2sdev, 1, &pi, NULL); | |
293 | i2sbus_wait_for_stop(i2sdev, pi); | |
294 | } | |
295 | #endif | |
296 | ||
f3d9478b JB |
297 | static int i2sbus_hw_params(struct snd_pcm_substream *substream, |
298 | struct snd_pcm_hw_params *params) | |
299 | { | |
300 | return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params)); | |
301 | } | |
302 | ||
547ac2ae | 303 | static inline int i2sbus_hw_free(struct snd_pcm_substream *substream, int in) |
f3d9478b | 304 | { |
547ac2ae PM |
305 | struct i2sbus_dev *i2sdev = snd_pcm_substream_chip(substream); |
306 | struct pcm_info *pi; | |
307 | ||
308 | get_pcm_info(i2sdev, in, &pi, NULL); | |
309 | if (pi->dbdma_ring.stopping) | |
310 | i2sbus_wait_for_stop(i2sdev, pi); | |
f3d9478b JB |
311 | snd_pcm_lib_free_pages(substream); |
312 | return 0; | |
313 | } | |
314 | ||
547ac2ae PM |
315 | static int i2sbus_playback_hw_free(struct snd_pcm_substream *substream) |
316 | { | |
317 | return i2sbus_hw_free(substream, 0); | |
318 | } | |
319 | ||
320 | static int i2sbus_record_hw_free(struct snd_pcm_substream *substream) | |
321 | { | |
322 | return i2sbus_hw_free(substream, 1); | |
323 | } | |
324 | ||
f3d9478b JB |
325 | static int i2sbus_pcm_prepare(struct i2sbus_dev *i2sdev, int in) |
326 | { | |
327 | /* whee. Hard work now. The user has selected a bitrate | |
328 | * and bit format, so now we have to program our | |
329 | * I2S controller appropriately. */ | |
330 | struct snd_pcm_runtime *runtime; | |
331 | struct dbdma_cmd *command; | |
547ac2ae | 332 | int i, periodsize, nperiods; |
f3d9478b JB |
333 | dma_addr_t offset; |
334 | struct bus_info bi; | |
335 | struct codec_info_item *cii; | |
336 | int sfr = 0; /* serial format register */ | |
337 | int dws = 0; /* data word sizes reg */ | |
338 | int input_16bit; | |
339 | struct pcm_info *pi, *other; | |
340 | int cnt; | |
341 | int result = 0; | |
547ac2ae | 342 | unsigned int cmd, stopaddr; |
f3d9478b JB |
343 | |
344 | mutex_lock(&i2sdev->lock); | |
345 | ||
346 | get_pcm_info(i2sdev, in, &pi, &other); | |
347 | ||
348 | if (pi->dbdma_ring.running) { | |
349 | result = -EBUSY; | |
350 | goto out_unlock; | |
351 | } | |
547ac2ae PM |
352 | if (pi->dbdma_ring.stopping) |
353 | i2sbus_wait_for_stop(i2sdev, pi); | |
354 | ||
355 | if (!pi->substream || !pi->substream->runtime) { | |
356 | result = -EINVAL; | |
357 | goto out_unlock; | |
358 | } | |
f3d9478b JB |
359 | |
360 | runtime = pi->substream->runtime; | |
361 | pi->active = 1; | |
362 | if (other->active && | |
363 | ((i2sdev->format != runtime->format) | |
364 | || (i2sdev->rate != runtime->rate))) { | |
365 | result = -EINVAL; | |
366 | goto out_unlock; | |
367 | } | |
368 | ||
369 | i2sdev->format = runtime->format; | |
370 | i2sdev->rate = runtime->rate; | |
371 | ||
372 | periodsize = snd_pcm_lib_period_bytes(pi->substream); | |
547ac2ae | 373 | nperiods = pi->substream->runtime->periods; |
f3d9478b JB |
374 | pi->current_period = 0; |
375 | ||
376 | /* generate dbdma command ring first */ | |
377 | command = pi->dbdma_ring.cmds; | |
547ac2ae PM |
378 | memset(command, 0, (nperiods + 2) * sizeof(struct dbdma_cmd)); |
379 | ||
380 | /* commands to DMA to/from the ring */ | |
381 | /* | |
382 | * For input, we need to do a graceful stop; if we abort | |
383 | * the DMA, we end up with leftover bytes that corrupt | |
384 | * the next recording. To do this we set the S0 status | |
385 | * bit and wait for the DMA controller to stop. Each | |
386 | * command has a branch condition to | |
387 | * make it branch to a stop command if S0 is set. | |
388 | * On input we also need to wait for the S7 bit to be | |
389 | * set before turning off the DMA controller. | |
390 | * In fact we do the graceful stop for output as well. | |
391 | */ | |
f3d9478b | 392 | offset = runtime->dma_addr; |
547ac2ae PM |
393 | cmd = (in? INPUT_MORE: OUTPUT_MORE) | BR_IFSET | INTR_ALWAYS; |
394 | stopaddr = pi->dbdma_ring.bus_cmd_start + | |
395 | (nperiods + 1) * sizeof(struct dbdma_cmd); | |
396 | for (i = 0; i < nperiods; i++, command++, offset += periodsize) { | |
397 | command->command = cpu_to_le16(cmd); | |
398 | command->cmd_dep = cpu_to_le32(stopaddr); | |
f3d9478b JB |
399 | command->phy_addr = cpu_to_le32(offset); |
400 | command->req_count = cpu_to_le16(periodsize); | |
f3d9478b | 401 | } |
547ac2ae PM |
402 | |
403 | /* branch back to beginning of ring */ | |
404 | command->command = cpu_to_le16(DBDMA_NOP | BR_ALWAYS); | |
f3d9478b | 405 | command->cmd_dep = cpu_to_le32(pi->dbdma_ring.bus_cmd_start); |
547ac2ae PM |
406 | command++; |
407 | ||
408 | /* set stop command */ | |
409 | command->command = cpu_to_le16(DBDMA_STOP); | |
f3d9478b JB |
410 | |
411 | /* ok, let's set the serial format and stuff */ | |
412 | switch (runtime->format) { | |
413 | /* 16 bit formats */ | |
414 | case SNDRV_PCM_FORMAT_S16_BE: | |
415 | case SNDRV_PCM_FORMAT_U16_BE: | |
416 | /* FIXME: if we add different bus factors we need to | |
417 | * do more here!! */ | |
418 | bi.bus_factor = 0; | |
419 | list_for_each_entry(cii, &i2sdev->sound.codec_list, list) { | |
420 | bi.bus_factor = cii->codec->bus_factor; | |
421 | break; | |
422 | } | |
423 | if (!bi.bus_factor) { | |
424 | result = -ENODEV; | |
425 | goto out_unlock; | |
426 | } | |
427 | input_16bit = 1; | |
428 | break; | |
429 | case SNDRV_PCM_FORMAT_S32_BE: | |
430 | case SNDRV_PCM_FORMAT_U32_BE: | |
431 | /* force 64x bus speed, otherwise the data cannot be | |
432 | * transferred quickly enough! */ | |
433 | bi.bus_factor = 64; | |
434 | input_16bit = 0; | |
435 | break; | |
436 | default: | |
437 | result = -EINVAL; | |
438 | goto out_unlock; | |
439 | } | |
440 | /* we assume all sysclocks are the same! */ | |
441 | list_for_each_entry(cii, &i2sdev->sound.codec_list, list) { | |
442 | bi.sysclock_factor = cii->codec->sysclock_factor; | |
443 | break; | |
444 | } | |
445 | ||
446 | if (clock_and_divisors(bi.sysclock_factor, | |
447 | bi.bus_factor, | |
448 | runtime->rate, | |
449 | &sfr) < 0) { | |
450 | result = -EINVAL; | |
451 | goto out_unlock; | |
452 | } | |
453 | switch (bi.bus_factor) { | |
454 | case 32: | |
455 | sfr |= I2S_SF_SERIAL_FORMAT_I2S_32X; | |
456 | break; | |
457 | case 64: | |
458 | sfr |= I2S_SF_SERIAL_FORMAT_I2S_64X; | |
459 | break; | |
460 | } | |
461 | /* FIXME: THIS ASSUMES MASTER ALL THE TIME */ | |
462 | sfr |= I2S_SF_SCLK_MASTER; | |
463 | ||
464 | list_for_each_entry(cii, &i2sdev->sound.codec_list, list) { | |
465 | int err = 0; | |
466 | if (cii->codec->prepare) | |
467 | err = cii->codec->prepare(cii, &bi, pi->substream); | |
468 | if (err) { | |
469 | result = err; | |
470 | goto out_unlock; | |
471 | } | |
472 | } | |
473 | /* codecs are fine with it, so set our clocks */ | |
474 | if (input_16bit) | |
475 | dws = (2 << I2S_DWS_NUM_CHANNELS_IN_SHIFT) | | |
476 | (2 << I2S_DWS_NUM_CHANNELS_OUT_SHIFT) | | |
477 | I2S_DWS_DATA_IN_16BIT | I2S_DWS_DATA_OUT_16BIT; | |
478 | else | |
479 | dws = (2 << I2S_DWS_NUM_CHANNELS_IN_SHIFT) | | |
480 | (2 << I2S_DWS_NUM_CHANNELS_OUT_SHIFT) | | |
481 | I2S_DWS_DATA_IN_24BIT | I2S_DWS_DATA_OUT_24BIT; | |
482 | ||
483 | /* early exit if already programmed correctly */ | |
484 | /* not locking these is fine since we touch them only in this function */ | |
485 | if (in_le32(&i2sdev->intfregs->serial_format) == sfr | |
486 | && in_le32(&i2sdev->intfregs->data_word_sizes) == dws) | |
487 | goto out_unlock; | |
488 | ||
489 | /* let's notify the codecs about clocks going away. | |
490 | * For now we only do mastering on the i2s cell... */ | |
491 | list_for_each_entry(cii, &i2sdev->sound.codec_list, list) | |
492 | if (cii->codec->switch_clock) | |
493 | cii->codec->switch_clock(cii, CLOCK_SWITCH_PREPARE_SLAVE); | |
494 | ||
495 | i2sbus_control_enable(i2sdev->control, i2sdev); | |
496 | i2sbus_control_cell(i2sdev->control, i2sdev, 1); | |
497 | ||
498 | out_le32(&i2sdev->intfregs->intr_ctl, I2S_PENDING_CLOCKS_STOPPED); | |
499 | ||
500 | i2sbus_control_clock(i2sdev->control, i2sdev, 0); | |
501 | ||
502 | msleep(1); | |
503 | ||
504 | /* wait for clock stopped. This can apparently take a while... */ | |
505 | cnt = 100; | |
506 | while (cnt-- && | |
507 | !(in_le32(&i2sdev->intfregs->intr_ctl) & I2S_PENDING_CLOCKS_STOPPED)) { | |
508 | msleep(5); | |
509 | } | |
510 | out_le32(&i2sdev->intfregs->intr_ctl, I2S_PENDING_CLOCKS_STOPPED); | |
511 | ||
512 | /* not locking these is fine since we touch them only in this function */ | |
513 | out_le32(&i2sdev->intfregs->serial_format, sfr); | |
514 | out_le32(&i2sdev->intfregs->data_word_sizes, dws); | |
515 | ||
516 | i2sbus_control_enable(i2sdev->control, i2sdev); | |
517 | i2sbus_control_cell(i2sdev->control, i2sdev, 1); | |
518 | i2sbus_control_clock(i2sdev->control, i2sdev, 1); | |
519 | msleep(1); | |
520 | ||
521 | list_for_each_entry(cii, &i2sdev->sound.codec_list, list) | |
522 | if (cii->codec->switch_clock) | |
523 | cii->codec->switch_clock(cii, CLOCK_SWITCH_SLAVE); | |
524 | ||
525 | out_unlock: | |
526 | mutex_unlock(&i2sdev->lock); | |
527 | return result; | |
528 | } | |
529 | ||
547ac2ae PM |
530 | #ifdef CONFIG_PM |
531 | void i2sbus_pcm_prepare_both(struct i2sbus_dev *i2sdev) | |
532 | { | |
533 | i2sbus_pcm_prepare(i2sdev, 0); | |
534 | i2sbus_pcm_prepare(i2sdev, 1); | |
535 | } | |
536 | #endif | |
f3d9478b JB |
537 | |
538 | static int i2sbus_pcm_trigger(struct i2sbus_dev *i2sdev, int in, int cmd) | |
539 | { | |
540 | struct codec_info_item *cii; | |
541 | struct pcm_info *pi; | |
f3d9478b JB |
542 | int result = 0; |
543 | unsigned long flags; | |
544 | ||
545 | spin_lock_irqsave(&i2sdev->low_lock, flags); | |
546 | ||
547 | get_pcm_info(i2sdev, in, &pi, NULL); | |
548 | ||
549 | switch (cmd) { | |
550 | case SNDRV_PCM_TRIGGER_START: | |
551 | case SNDRV_PCM_TRIGGER_RESUME: | |
552 | if (pi->dbdma_ring.running) { | |
553 | result = -EALREADY; | |
554 | goto out_unlock; | |
555 | } | |
556 | list_for_each_entry(cii, &i2sdev->sound.codec_list, list) | |
557 | if (cii->codec->start) | |
558 | cii->codec->start(cii, pi->substream); | |
559 | pi->dbdma_ring.running = 1; | |
560 | ||
547ac2ae PM |
561 | if (pi->dbdma_ring.stopping) { |
562 | /* Clear the S0 bit, then see if we stopped yet */ | |
563 | out_le32(&pi->dbdma->control, 1 << 16); | |
564 | if (in_le32(&pi->dbdma->status) & ACTIVE) { | |
565 | /* possible race here? */ | |
566 | udelay(10); | |
567 | if (in_le32(&pi->dbdma->status) & ACTIVE) { | |
568 | pi->dbdma_ring.stopping = 0; | |
569 | goto out_unlock; /* keep running */ | |
570 | } | |
571 | } | |
f3d9478b JB |
572 | } |
573 | ||
547ac2ae PM |
574 | /* make sure RUN, PAUSE and S0 bits are cleared */ |
575 | out_le32(&pi->dbdma->control, (RUN | PAUSE | 1) << 16); | |
576 | ||
577 | /* set branch condition select register */ | |
578 | out_le32(&pi->dbdma->br_sel, (1 << 16) | 1); | |
579 | ||
f3d9478b JB |
580 | /* write dma command buffer address to the dbdma chip */ |
581 | out_le32(&pi->dbdma->cmdptr, pi->dbdma_ring.bus_cmd_start); | |
f3d9478b | 582 | |
547ac2ae PM |
583 | /* initialize the frame count and current period */ |
584 | pi->current_period = 0; | |
f3d9478b JB |
585 | pi->frame_count = in_le32(&i2sdev->intfregs->frame_count); |
586 | ||
547ac2ae PM |
587 | /* set the DMA controller running */ |
588 | out_le32(&pi->dbdma->control, (RUN << 16) | RUN); | |
589 | ||
f3d9478b JB |
590 | /* off you go! */ |
591 | break; | |
547ac2ae | 592 | |
f3d9478b JB |
593 | case SNDRV_PCM_TRIGGER_STOP: |
594 | case SNDRV_PCM_TRIGGER_SUSPEND: | |
595 | if (!pi->dbdma_ring.running) { | |
596 | result = -EALREADY; | |
597 | goto out_unlock; | |
598 | } | |
547ac2ae | 599 | pi->dbdma_ring.running = 0; |
f3d9478b | 600 | |
547ac2ae PM |
601 | /* Set the S0 bit to make the DMA branch to the stop cmd */ |
602 | out_le32(&pi->dbdma->control, (1 << 16) | 1); | |
603 | pi->dbdma_ring.stopping = 1; | |
f3d9478b | 604 | |
f3d9478b JB |
605 | list_for_each_entry(cii, &i2sdev->sound.codec_list, list) |
606 | if (cii->codec->stop) | |
607 | cii->codec->stop(cii, pi->substream); | |
608 | break; | |
609 | default: | |
610 | result = -EINVAL; | |
611 | goto out_unlock; | |
612 | } | |
613 | ||
614 | out_unlock: | |
615 | spin_unlock_irqrestore(&i2sdev->low_lock, flags); | |
616 | return result; | |
617 | } | |
618 | ||
619 | static snd_pcm_uframes_t i2sbus_pcm_pointer(struct i2sbus_dev *i2sdev, int in) | |
620 | { | |
621 | struct pcm_info *pi; | |
622 | u32 fc; | |
623 | ||
624 | get_pcm_info(i2sdev, in, &pi, NULL); | |
625 | ||
626 | fc = in_le32(&i2sdev->intfregs->frame_count); | |
627 | fc = fc - pi->frame_count; | |
628 | ||
547ac2ae PM |
629 | if (fc >= pi->substream->runtime->buffer_size) |
630 | fc %= pi->substream->runtime->buffer_size; | |
631 | return fc; | |
f3d9478b JB |
632 | } |
633 | ||
634 | static inline void handle_interrupt(struct i2sbus_dev *i2sdev, int in) | |
635 | { | |
636 | struct pcm_info *pi; | |
547ac2ae PM |
637 | u32 fc, nframes; |
638 | u32 status; | |
639 | int timeout, i; | |
640 | int dma_stopped = 0; | |
641 | struct snd_pcm_runtime *runtime; | |
f3d9478b JB |
642 | |
643 | spin_lock(&i2sdev->low_lock); | |
644 | get_pcm_info(i2sdev, in, &pi, NULL); | |
547ac2ae | 645 | if (!pi->dbdma_ring.running && !pi->dbdma_ring.stopping) |
f3d9478b | 646 | goto out_unlock; |
f3d9478b | 647 | |
547ac2ae PM |
648 | i = pi->current_period; |
649 | runtime = pi->substream->runtime; | |
650 | while (pi->dbdma_ring.cmds[i].xfer_status) { | |
651 | if (le16_to_cpu(pi->dbdma_ring.cmds[i].xfer_status) & BT) | |
652 | /* | |
653 | * BT is the branch taken bit. If it took a branch | |
654 | * it is because we set the S0 bit to make it | |
655 | * branch to the stop command. | |
656 | */ | |
657 | dma_stopped = 1; | |
658 | pi->dbdma_ring.cmds[i].xfer_status = 0; | |
659 | ||
660 | if (++i >= runtime->periods) { | |
661 | i = 0; | |
662 | pi->frame_count += runtime->buffer_size; | |
663 | } | |
664 | pi->current_period = i; | |
665 | ||
666 | /* | |
667 | * Check the frame count. The DMA tends to get a bit | |
668 | * ahead of the frame counter, which confuses the core. | |
669 | */ | |
670 | fc = in_le32(&i2sdev->intfregs->frame_count); | |
671 | nframes = i * runtime->period_size; | |
672 | if (fc < pi->frame_count + nframes) | |
673 | pi->frame_count = fc - nframes; | |
f3d9478b JB |
674 | } |
675 | ||
547ac2ae PM |
676 | if (dma_stopped) { |
677 | timeout = 1000; | |
678 | for (;;) { | |
679 | status = in_le32(&pi->dbdma->status); | |
680 | if (!(status & ACTIVE) && (!in || (status & 0x80))) | |
681 | break; | |
682 | if (--timeout <= 0) { | |
683 | printk(KERN_ERR "i2sbus: timed out " | |
684 | "waiting for DMA to stop!\n"); | |
685 | break; | |
686 | } | |
687 | udelay(1); | |
f3d9478b | 688 | } |
f3d9478b | 689 | |
547ac2ae PM |
690 | /* Turn off DMA controller, clear S0 bit */ |
691 | out_le32(&pi->dbdma->control, (RUN | PAUSE | 1) << 16); | |
f3d9478b | 692 | |
547ac2ae PM |
693 | pi->dbdma_ring.stopping = 0; |
694 | if (pi->stop_completion) | |
695 | complete(pi->stop_completion); | |
696 | } | |
697 | ||
698 | if (!pi->dbdma_ring.running) | |
699 | goto out_unlock; | |
f3d9478b JB |
700 | spin_unlock(&i2sdev->low_lock); |
701 | /* may call _trigger again, hence needs to be unlocked */ | |
702 | snd_pcm_period_elapsed(pi->substream); | |
703 | return; | |
547ac2ae | 704 | |
f3d9478b JB |
705 | out_unlock: |
706 | spin_unlock(&i2sdev->low_lock); | |
707 | } | |
708 | ||
7d12e780 | 709 | irqreturn_t i2sbus_tx_intr(int irq, void *devid) |
f3d9478b JB |
710 | { |
711 | handle_interrupt((struct i2sbus_dev *)devid, 0); | |
712 | return IRQ_HANDLED; | |
713 | } | |
714 | ||
7d12e780 | 715 | irqreturn_t i2sbus_rx_intr(int irq, void *devid) |
f3d9478b JB |
716 | { |
717 | handle_interrupt((struct i2sbus_dev *)devid, 1); | |
718 | return IRQ_HANDLED; | |
719 | } | |
720 | ||
721 | static int i2sbus_playback_open(struct snd_pcm_substream *substream) | |
722 | { | |
723 | struct i2sbus_dev *i2sdev = snd_pcm_substream_chip(substream); | |
724 | ||
725 | if (!i2sdev) | |
726 | return -EINVAL; | |
727 | i2sdev->out.substream = substream; | |
728 | return i2sbus_pcm_open(i2sdev, 0); | |
729 | } | |
730 | ||
731 | static int i2sbus_playback_close(struct snd_pcm_substream *substream) | |
732 | { | |
733 | struct i2sbus_dev *i2sdev = snd_pcm_substream_chip(substream); | |
734 | int err; | |
735 | ||
736 | if (!i2sdev) | |
737 | return -EINVAL; | |
738 | if (i2sdev->out.substream != substream) | |
739 | return -EINVAL; | |
740 | err = i2sbus_pcm_close(i2sdev, 0); | |
741 | if (!err) | |
742 | i2sdev->out.substream = NULL; | |
743 | return err; | |
744 | } | |
745 | ||
746 | static int i2sbus_playback_prepare(struct snd_pcm_substream *substream) | |
747 | { | |
748 | struct i2sbus_dev *i2sdev = snd_pcm_substream_chip(substream); | |
749 | ||
750 | if (!i2sdev) | |
751 | return -EINVAL; | |
752 | if (i2sdev->out.substream != substream) | |
753 | return -EINVAL; | |
754 | return i2sbus_pcm_prepare(i2sdev, 0); | |
755 | } | |
756 | ||
757 | static int i2sbus_playback_trigger(struct snd_pcm_substream *substream, int cmd) | |
758 | { | |
759 | struct i2sbus_dev *i2sdev = snd_pcm_substream_chip(substream); | |
760 | ||
761 | if (!i2sdev) | |
762 | return -EINVAL; | |
763 | if (i2sdev->out.substream != substream) | |
764 | return -EINVAL; | |
765 | return i2sbus_pcm_trigger(i2sdev, 0, cmd); | |
766 | } | |
767 | ||
768 | static snd_pcm_uframes_t i2sbus_playback_pointer(struct snd_pcm_substream | |
769 | *substream) | |
770 | { | |
771 | struct i2sbus_dev *i2sdev = snd_pcm_substream_chip(substream); | |
772 | ||
773 | if (!i2sdev) | |
774 | return -EINVAL; | |
775 | if (i2sdev->out.substream != substream) | |
776 | return 0; | |
777 | return i2sbus_pcm_pointer(i2sdev, 0); | |
778 | } | |
779 | ||
780 | static struct snd_pcm_ops i2sbus_playback_ops = { | |
781 | .open = i2sbus_playback_open, | |
782 | .close = i2sbus_playback_close, | |
783 | .ioctl = snd_pcm_lib_ioctl, | |
784 | .hw_params = i2sbus_hw_params, | |
547ac2ae | 785 | .hw_free = i2sbus_playback_hw_free, |
f3d9478b JB |
786 | .prepare = i2sbus_playback_prepare, |
787 | .trigger = i2sbus_playback_trigger, | |
788 | .pointer = i2sbus_playback_pointer, | |
789 | }; | |
790 | ||
791 | static int i2sbus_record_open(struct snd_pcm_substream *substream) | |
792 | { | |
793 | struct i2sbus_dev *i2sdev = snd_pcm_substream_chip(substream); | |
794 | ||
795 | if (!i2sdev) | |
796 | return -EINVAL; | |
797 | i2sdev->in.substream = substream; | |
798 | return i2sbus_pcm_open(i2sdev, 1); | |
799 | } | |
800 | ||
801 | static int i2sbus_record_close(struct snd_pcm_substream *substream) | |
802 | { | |
803 | struct i2sbus_dev *i2sdev = snd_pcm_substream_chip(substream); | |
804 | int err; | |
805 | ||
806 | if (!i2sdev) | |
807 | return -EINVAL; | |
808 | if (i2sdev->in.substream != substream) | |
809 | return -EINVAL; | |
810 | err = i2sbus_pcm_close(i2sdev, 1); | |
811 | if (!err) | |
812 | i2sdev->in.substream = NULL; | |
813 | return err; | |
814 | } | |
815 | ||
816 | static int i2sbus_record_prepare(struct snd_pcm_substream *substream) | |
817 | { | |
818 | struct i2sbus_dev *i2sdev = snd_pcm_substream_chip(substream); | |
819 | ||
820 | if (!i2sdev) | |
821 | return -EINVAL; | |
822 | if (i2sdev->in.substream != substream) | |
823 | return -EINVAL; | |
824 | return i2sbus_pcm_prepare(i2sdev, 1); | |
825 | } | |
826 | ||
827 | static int i2sbus_record_trigger(struct snd_pcm_substream *substream, int cmd) | |
828 | { | |
829 | struct i2sbus_dev *i2sdev = snd_pcm_substream_chip(substream); | |
830 | ||
831 | if (!i2sdev) | |
832 | return -EINVAL; | |
833 | if (i2sdev->in.substream != substream) | |
834 | return -EINVAL; | |
835 | return i2sbus_pcm_trigger(i2sdev, 1, cmd); | |
836 | } | |
837 | ||
838 | static snd_pcm_uframes_t i2sbus_record_pointer(struct snd_pcm_substream | |
839 | *substream) | |
840 | { | |
841 | struct i2sbus_dev *i2sdev = snd_pcm_substream_chip(substream); | |
842 | ||
843 | if (!i2sdev) | |
844 | return -EINVAL; | |
845 | if (i2sdev->in.substream != substream) | |
846 | return 0; | |
847 | return i2sbus_pcm_pointer(i2sdev, 1); | |
848 | } | |
849 | ||
850 | static struct snd_pcm_ops i2sbus_record_ops = { | |
851 | .open = i2sbus_record_open, | |
852 | .close = i2sbus_record_close, | |
853 | .ioctl = snd_pcm_lib_ioctl, | |
854 | .hw_params = i2sbus_hw_params, | |
547ac2ae | 855 | .hw_free = i2sbus_record_hw_free, |
f3d9478b JB |
856 | .prepare = i2sbus_record_prepare, |
857 | .trigger = i2sbus_record_trigger, | |
858 | .pointer = i2sbus_record_pointer, | |
859 | }; | |
860 | ||
861 | static void i2sbus_private_free(struct snd_pcm *pcm) | |
862 | { | |
863 | struct i2sbus_dev *i2sdev = snd_pcm_chip(pcm); | |
864 | struct codec_info_item *p, *tmp; | |
865 | ||
866 | i2sdev->sound.pcm = NULL; | |
867 | i2sdev->out.created = 0; | |
868 | i2sdev->in.created = 0; | |
869 | list_for_each_entry_safe(p, tmp, &i2sdev->sound.codec_list, list) { | |
870 | printk(KERN_ERR "i2sbus: a codec didn't unregister!\n"); | |
871 | list_del(&p->list); | |
872 | module_put(p->codec->owner); | |
873 | kfree(p); | |
874 | } | |
875 | soundbus_dev_put(&i2sdev->sound); | |
876 | module_put(THIS_MODULE); | |
877 | } | |
878 | ||
f3d9478b JB |
879 | int |
880 | i2sbus_attach_codec(struct soundbus_dev *dev, struct snd_card *card, | |
881 | struct codec_info *ci, void *data) | |
882 | { | |
883 | int err, in = 0, out = 0; | |
884 | struct transfer_info *tmp; | |
885 | struct i2sbus_dev *i2sdev = soundbus_dev_to_i2sbus_dev(dev); | |
886 | struct codec_info_item *cii; | |
887 | ||
888 | if (!dev->pcmname || dev->pcmid == -1) { | |
889 | printk(KERN_ERR "i2sbus: pcm name and id must be set!\n"); | |
890 | return -EINVAL; | |
891 | } | |
892 | ||
893 | list_for_each_entry(cii, &dev->codec_list, list) { | |
894 | if (cii->codec_data == data) | |
895 | return -EALREADY; | |
896 | } | |
897 | ||
898 | if (!ci->transfers || !ci->transfers->formats | |
899 | || !ci->transfers->rates || !ci->usable) | |
900 | return -EINVAL; | |
901 | ||
902 | /* we currently code the i2s transfer on the clock, and support only | |
903 | * 32 and 64 */ | |
904 | if (ci->bus_factor != 32 && ci->bus_factor != 64) | |
905 | return -EINVAL; | |
906 | ||
907 | /* If you want to fix this, you need to keep track of what transport infos | |
908 | * are to be used, which codecs they belong to, and then fix all the | |
909 | * sysclock/busclock stuff above to depend on which is usable */ | |
910 | list_for_each_entry(cii, &dev->codec_list, list) { | |
911 | if (cii->codec->sysclock_factor != ci->sysclock_factor) { | |
912 | printk(KERN_DEBUG | |
913 | "cannot yet handle multiple different sysclocks!\n"); | |
914 | return -EINVAL; | |
915 | } | |
916 | if (cii->codec->bus_factor != ci->bus_factor) { | |
917 | printk(KERN_DEBUG | |
918 | "cannot yet handle multiple different bus clocks!\n"); | |
919 | return -EINVAL; | |
920 | } | |
921 | } | |
922 | ||
923 | tmp = ci->transfers; | |
924 | while (tmp->formats && tmp->rates) { | |
925 | if (tmp->transfer_in) | |
926 | in = 1; | |
927 | else | |
928 | out = 1; | |
929 | tmp++; | |
930 | } | |
931 | ||
932 | cii = kzalloc(sizeof(struct codec_info_item), GFP_KERNEL); | |
933 | if (!cii) { | |
934 | printk(KERN_DEBUG "i2sbus: failed to allocate cii\n"); | |
935 | return -ENOMEM; | |
936 | } | |
937 | ||
938 | /* use the private data to point to the codec info */ | |
939 | cii->sdev = soundbus_dev_get(dev); | |
940 | cii->codec = ci; | |
941 | cii->codec_data = data; | |
942 | ||
943 | if (!cii->sdev) { | |
944 | printk(KERN_DEBUG | |
945 | "i2sbus: failed to get soundbus dev reference\n"); | |
d595ee7e JB |
946 | err = -ENODEV; |
947 | goto out_free_cii; | |
f3d9478b JB |
948 | } |
949 | ||
950 | if (!try_module_get(THIS_MODULE)) { | |
951 | printk(KERN_DEBUG "i2sbus: failed to get module reference!\n"); | |
d595ee7e JB |
952 | err = -EBUSY; |
953 | goto out_put_sdev; | |
f3d9478b JB |
954 | } |
955 | ||
956 | if (!try_module_get(ci->owner)) { | |
957 | printk(KERN_DEBUG | |
958 | "i2sbus: failed to get module reference to codec owner!\n"); | |
d595ee7e JB |
959 | err = -EBUSY; |
960 | goto out_put_this_module; | |
f3d9478b JB |
961 | } |
962 | ||
963 | if (!dev->pcm) { | |
73e85fe8 | 964 | err = snd_pcm_new(card, dev->pcmname, dev->pcmid, 0, 0, |
f3d9478b JB |
965 | &dev->pcm); |
966 | if (err) { | |
967 | printk(KERN_DEBUG "i2sbus: failed to create pcm\n"); | |
d595ee7e | 968 | goto out_put_ci_module; |
f3d9478b | 969 | } |
73e85fe8 | 970 | dev->pcm->dev = &dev->ofdev.dev; |
f3d9478b JB |
971 | } |
972 | ||
973 | /* ALSA yet again sucks. | |
974 | * If it is ever fixed, remove this line. See below. */ | |
975 | out = in = 1; | |
976 | ||
977 | if (!i2sdev->out.created && out) { | |
978 | if (dev->pcm->card != card) { | |
979 | /* eh? */ | |
980 | printk(KERN_ERR | |
981 | "Can't attach same bus to different cards!\n"); | |
d595ee7e JB |
982 | err = -EINVAL; |
983 | goto out_put_ci_module; | |
f3d9478b | 984 | } |
d595ee7e JB |
985 | err = snd_pcm_new_stream(dev->pcm, SNDRV_PCM_STREAM_PLAYBACK, 1); |
986 | if (err) | |
987 | goto out_put_ci_module; | |
f3d9478b JB |
988 | snd_pcm_set_ops(dev->pcm, SNDRV_PCM_STREAM_PLAYBACK, |
989 | &i2sbus_playback_ops); | |
990 | i2sdev->out.created = 1; | |
991 | } | |
992 | ||
993 | if (!i2sdev->in.created && in) { | |
994 | if (dev->pcm->card != card) { | |
995 | printk(KERN_ERR | |
996 | "Can't attach same bus to different cards!\n"); | |
3d3909ff | 997 | err = -EINVAL; |
d595ee7e | 998 | goto out_put_ci_module; |
f3d9478b | 999 | } |
d595ee7e JB |
1000 | err = snd_pcm_new_stream(dev->pcm, SNDRV_PCM_STREAM_CAPTURE, 1); |
1001 | if (err) | |
1002 | goto out_put_ci_module; | |
f3d9478b JB |
1003 | snd_pcm_set_ops(dev->pcm, SNDRV_PCM_STREAM_CAPTURE, |
1004 | &i2sbus_record_ops); | |
1005 | i2sdev->in.created = 1; | |
1006 | } | |
1007 | ||
1008 | /* so we have to register the pcm after adding any substream | |
1009 | * to it because alsa doesn't create the devices for the | |
1010 | * substreams when we add them later. | |
1011 | * Therefore, force in and out on both busses (above) and | |
1012 | * register the pcm now instead of just after creating it. | |
1013 | */ | |
1014 | err = snd_device_register(card, dev->pcm); | |
1015 | if (err) { | |
1016 | printk(KERN_ERR "i2sbus: error registering new pcm\n"); | |
d595ee7e | 1017 | goto out_put_ci_module; |
f3d9478b JB |
1018 | } |
1019 | /* no errors any more, so let's add this to our list */ | |
1020 | list_add(&cii->list, &dev->codec_list); | |
1021 | ||
1022 | dev->pcm->private_data = i2sdev; | |
1023 | dev->pcm->private_free = i2sbus_private_free; | |
1024 | ||
1025 | /* well, we really should support scatter/gather DMA */ | |
1026 | snd_pcm_lib_preallocate_pages_for_all( | |
1027 | dev->pcm, SNDRV_DMA_TYPE_DEV, | |
1028 | snd_dma_pci_data(macio_get_pci_dev(i2sdev->macio)), | |
1029 | 64 * 1024, 64 * 1024); | |
1030 | ||
1031 | return 0; | |
d595ee7e JB |
1032 | out_put_ci_module: |
1033 | module_put(ci->owner); | |
1034 | out_put_this_module: | |
1035 | module_put(THIS_MODULE); | |
1036 | out_put_sdev: | |
1037 | soundbus_dev_put(dev); | |
1038 | out_free_cii: | |
1039 | kfree(cii); | |
1040 | return err; | |
f3d9478b JB |
1041 | } |
1042 | ||
1043 | void i2sbus_detach_codec(struct soundbus_dev *dev, void *data) | |
1044 | { | |
1045 | struct codec_info_item *cii = NULL, *i; | |
1046 | ||
1047 | list_for_each_entry(i, &dev->codec_list, list) { | |
1048 | if (i->codec_data == data) { | |
1049 | cii = i; | |
1050 | break; | |
1051 | } | |
1052 | } | |
1053 | if (cii) { | |
1054 | list_del(&cii->list); | |
1055 | module_put(cii->codec->owner); | |
1056 | kfree(cii); | |
1057 | } | |
1058 | /* no more codecs, but still a pcm? */ | |
1059 | if (list_empty(&dev->codec_list) && dev->pcm) { | |
1060 | /* the actual cleanup is done by the callback above! */ | |
1061 | snd_device_free(dev->pcm->card, dev->pcm); | |
1062 | } | |
1063 | } |