]>
Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * Driver for Sound Core PDAudioCF soundcards | |
3 | * | |
4 | * PCM part | |
5 | * | |
c1017a4c | 6 | * Copyright (c) 2003 by Jaroslav Kysela <perex@perex.cz> |
1da177e4 LT |
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 | ||
1da177e4 LT |
23 | #include <linux/slab.h> |
24 | #include <linux/vmalloc.h> | |
25 | #include <linux/delay.h> | |
26 | #include <sound/core.h> | |
27 | #include <sound/asoundef.h> | |
28 | #include "pdaudiocf.h" | |
29 | ||
30 | ||
31 | /* | |
32 | * we use a vmalloc'ed (sg-)buffer | |
33 | */ | |
34 | ||
35 | /* get the physical page pointer on the given offset */ | |
db131548 | 36 | static struct page *snd_pcm_get_vmalloc_page(struct snd_pcm_substream *subs, unsigned long offset) |
1da177e4 LT |
37 | { |
38 | void *pageptr = subs->runtime->dma_area + offset; | |
39 | return vmalloc_to_page(pageptr); | |
40 | } | |
41 | ||
42 | /* | |
43 | * hw_params callback | |
44 | * NOTE: this may be called not only once per pcm open! | |
45 | */ | |
db131548 | 46 | static int snd_pcm_alloc_vmalloc_buffer(struct snd_pcm_substream *subs, size_t size) |
1da177e4 | 47 | { |
db131548 | 48 | struct snd_pcm_runtime *runtime = subs->runtime; |
1da177e4 LT |
49 | if (runtime->dma_area) { |
50 | if (runtime->dma_bytes >= size) | |
51 | return 0; /* already enough large */ | |
b1d5776d | 52 | vfree(runtime->dma_area); |
1da177e4 | 53 | } |
3e85fd61 | 54 | runtime->dma_area = vmalloc_32_user(size); |
1da177e4 LT |
55 | if (! runtime->dma_area) |
56 | return -ENOMEM; | |
57 | runtime->dma_bytes = size; | |
58 | return 0; | |
59 | } | |
60 | ||
61 | /* | |
62 | * hw_free callback | |
63 | * NOTE: this may be called not only once per pcm open! | |
64 | */ | |
db131548 | 65 | static int snd_pcm_free_vmalloc_buffer(struct snd_pcm_substream *subs) |
1da177e4 | 66 | { |
db131548 | 67 | struct snd_pcm_runtime *runtime = subs->runtime; |
2a578f3b JJ |
68 | |
69 | vfree(runtime->dma_area); | |
70 | runtime->dma_area = NULL; | |
1da177e4 LT |
71 | return 0; |
72 | } | |
73 | ||
74 | /* | |
75 | * clear the SRAM contents | |
76 | */ | |
db131548 | 77 | static int pdacf_pcm_clear_sram(struct snd_pdacf *chip) |
1da177e4 LT |
78 | { |
79 | int max_loop = 64 * 1024; | |
80 | ||
81 | while (inw(chip->port + PDAUDIOCF_REG_RDP) != inw(chip->port + PDAUDIOCF_REG_WDP)) { | |
82 | if (max_loop-- < 0) | |
83 | return -EIO; | |
84 | inw(chip->port + PDAUDIOCF_REG_MD); | |
85 | } | |
86 | return 0; | |
87 | } | |
88 | ||
89 | /* | |
90 | * pdacf_pcm_trigger - trigger callback for capture | |
91 | */ | |
db131548 | 92 | static int pdacf_pcm_trigger(struct snd_pcm_substream *subs, int cmd) |
1da177e4 | 93 | { |
db131548 TI |
94 | struct snd_pdacf *chip = snd_pcm_substream_chip(subs); |
95 | struct snd_pcm_runtime *runtime = subs->runtime; | |
1da177e4 LT |
96 | int inc, ret = 0, rate; |
97 | unsigned short mask, val, tmp; | |
98 | ||
99 | if (chip->chip_status & PDAUDIOCF_STAT_IS_STALE) | |
100 | return -EBUSY; | |
101 | ||
102 | switch (cmd) { | |
103 | case SNDRV_PCM_TRIGGER_START: | |
104 | chip->pcm_hwptr = 0; | |
105 | chip->pcm_tdone = 0; | |
106 | /* fall thru */ | |
107 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: | |
108 | case SNDRV_PCM_TRIGGER_RESUME: | |
109 | mask = 0; | |
110 | val = PDAUDIOCF_RECORD; | |
111 | inc = 1; | |
112 | rate = snd_ak4117_check_rate_and_errors(chip->ak4117, AK4117_CHECK_NO_STAT|AK4117_CHECK_NO_RATE); | |
113 | break; | |
114 | case SNDRV_PCM_TRIGGER_STOP: | |
115 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: | |
116 | case SNDRV_PCM_TRIGGER_SUSPEND: | |
117 | mask = PDAUDIOCF_RECORD; | |
118 | val = 0; | |
119 | inc = -1; | |
120 | rate = 0; | |
121 | break; | |
122 | default: | |
123 | return -EINVAL; | |
124 | } | |
125 | spin_lock(&chip->reg_lock); | |
126 | chip->pcm_running += inc; | |
127 | tmp = pdacf_reg_read(chip, PDAUDIOCF_REG_SCR); | |
128 | if (chip->pcm_running) { | |
129 | if ((chip->ak4117->rcs0 & AK4117_UNLCK) || runtime->rate != rate) { | |
130 | chip->pcm_running -= inc; | |
131 | ret = -EIO; | |
132 | goto __end; | |
133 | } | |
134 | } | |
135 | tmp &= ~mask; | |
136 | tmp |= val; | |
137 | pdacf_reg_write(chip, PDAUDIOCF_REG_SCR, tmp); | |
138 | __end: | |
139 | spin_unlock(&chip->reg_lock); | |
140 | snd_ak4117_check_rate_and_errors(chip->ak4117, AK4117_CHECK_NO_RATE); | |
141 | return ret; | |
142 | } | |
143 | ||
144 | /* | |
145 | * pdacf_pcm_hw_params - hw_params callback for playback and capture | |
146 | */ | |
db131548 TI |
147 | static int pdacf_pcm_hw_params(struct snd_pcm_substream *subs, |
148 | struct snd_pcm_hw_params *hw_params) | |
1da177e4 LT |
149 | { |
150 | return snd_pcm_alloc_vmalloc_buffer(subs, params_buffer_bytes(hw_params)); | |
151 | } | |
152 | ||
153 | /* | |
154 | * pdacf_pcm_hw_free - hw_free callback for playback and capture | |
155 | */ | |
db131548 | 156 | static int pdacf_pcm_hw_free(struct snd_pcm_substream *subs) |
1da177e4 LT |
157 | { |
158 | return snd_pcm_free_vmalloc_buffer(subs); | |
159 | } | |
160 | ||
161 | /* | |
162 | * pdacf_pcm_prepare - prepare callback for playback and capture | |
163 | */ | |
db131548 | 164 | static int pdacf_pcm_prepare(struct snd_pcm_substream *subs) |
1da177e4 | 165 | { |
db131548 TI |
166 | struct snd_pdacf *chip = snd_pcm_substream_chip(subs); |
167 | struct snd_pcm_runtime *runtime = subs->runtime; | |
1da177e4 LT |
168 | u16 val, nval, aval; |
169 | ||
170 | if (chip->chip_status & PDAUDIOCF_STAT_IS_STALE) | |
171 | return -EBUSY; | |
172 | ||
173 | chip->pcm_channels = runtime->channels; | |
174 | ||
175 | chip->pcm_little = snd_pcm_format_little_endian(runtime->format) > 0; | |
176 | #ifdef SNDRV_LITTLE_ENDIAN | |
177 | chip->pcm_swab = snd_pcm_format_big_endian(runtime->format) > 0; | |
178 | #else | |
179 | chip->pcm_swab = chip->pcm_little; | |
180 | #endif | |
181 | ||
182 | if (snd_pcm_format_unsigned(runtime->format)) | |
183 | chip->pcm_xor = 0x80008000; | |
184 | ||
185 | if (pdacf_pcm_clear_sram(chip) < 0) | |
186 | return -EIO; | |
187 | ||
188 | val = nval = pdacf_reg_read(chip, PDAUDIOCF_REG_SCR); | |
189 | nval &= ~(PDAUDIOCF_DATAFMT0|PDAUDIOCF_DATAFMT1); | |
190 | switch (runtime->format) { | |
191 | case SNDRV_PCM_FORMAT_S16_LE: | |
192 | case SNDRV_PCM_FORMAT_S16_BE: | |
193 | break; | |
194 | default: /* 24-bit */ | |
195 | nval |= PDAUDIOCF_DATAFMT0 | PDAUDIOCF_DATAFMT1; | |
196 | break; | |
197 | } | |
198 | aval = 0; | |
199 | chip->pcm_sample = 4; | |
200 | switch (runtime->format) { | |
201 | case SNDRV_PCM_FORMAT_S16_LE: | |
202 | case SNDRV_PCM_FORMAT_S16_BE: | |
203 | aval = AK4117_DIF_16R; | |
204 | chip->pcm_frame = 2; | |
205 | chip->pcm_sample = 2; | |
206 | break; | |
207 | case SNDRV_PCM_FORMAT_S24_3LE: | |
208 | case SNDRV_PCM_FORMAT_S24_3BE: | |
209 | chip->pcm_sample = 3; | |
93b1fae4 | 210 | /* fall through */ |
1da177e4 LT |
211 | default: /* 24-bit */ |
212 | aval = AK4117_DIF_24R; | |
213 | chip->pcm_frame = 3; | |
214 | chip->pcm_xor &= 0xffff0000; | |
215 | break; | |
216 | } | |
217 | ||
218 | if (val != nval) { | |
219 | snd_ak4117_reg_write(chip->ak4117, AK4117_REG_IO, AK4117_DIF2|AK4117_DIF1|AK4117_DIF0, aval); | |
220 | pdacf_reg_write(chip, PDAUDIOCF_REG_SCR, nval); | |
221 | } | |
222 | ||
223 | val = pdacf_reg_read(chip, PDAUDIOCF_REG_IER); | |
224 | val &= ~(PDAUDIOCF_IRQLVLEN1); | |
225 | val |= PDAUDIOCF_IRQLVLEN0; | |
226 | pdacf_reg_write(chip, PDAUDIOCF_REG_IER, val); | |
227 | ||
228 | chip->pcm_size = runtime->buffer_size; | |
229 | chip->pcm_period = runtime->period_size; | |
230 | chip->pcm_area = runtime->dma_area; | |
231 | ||
232 | return 0; | |
233 | } | |
234 | ||
235 | ||
236 | /* | |
237 | * capture hw information | |
238 | */ | |
239 | ||
db131548 | 240 | static struct snd_pcm_hardware pdacf_pcm_capture_hw = { |
1da177e4 LT |
241 | .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | |
242 | SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME | | |
2008f137 TI |
243 | SNDRV_PCM_INFO_MMAP_VALID | |
244 | SNDRV_PCM_INFO_BATCH), | |
1da177e4 LT |
245 | .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE | |
246 | SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE | | |
247 | SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE, | |
248 | .rates = SNDRV_PCM_RATE_32000 | | |
249 | SNDRV_PCM_RATE_44100 | | |
250 | SNDRV_PCM_RATE_48000 | | |
251 | SNDRV_PCM_RATE_88200 | | |
252 | SNDRV_PCM_RATE_96000 | | |
253 | SNDRV_PCM_RATE_176400 | | |
254 | SNDRV_PCM_RATE_192000, | |
255 | .rate_min = 32000, | |
256 | .rate_max = 192000, | |
257 | .channels_min = 1, | |
258 | .channels_max = 2, | |
259 | .buffer_bytes_max = (512*1024), | |
260 | .period_bytes_min = 8*1024, | |
261 | .period_bytes_max = (64*1024), | |
262 | .periods_min = 2, | |
263 | .periods_max = 128, | |
264 | .fifo_size = 0, | |
265 | }; | |
266 | ||
267 | ||
268 | /* | |
269 | * pdacf_pcm_capture_open - open callback for capture | |
270 | */ | |
db131548 | 271 | static int pdacf_pcm_capture_open(struct snd_pcm_substream *subs) |
1da177e4 | 272 | { |
db131548 TI |
273 | struct snd_pcm_runtime *runtime = subs->runtime; |
274 | struct snd_pdacf *chip = snd_pcm_substream_chip(subs); | |
1da177e4 LT |
275 | |
276 | if (chip->chip_status & PDAUDIOCF_STAT_IS_STALE) | |
277 | return -EBUSY; | |
278 | ||
279 | runtime->hw = pdacf_pcm_capture_hw; | |
280 | runtime->private_data = chip; | |
281 | chip->pcm_substream = subs; | |
282 | ||
283 | return 0; | |
284 | } | |
285 | ||
286 | /* | |
287 | * pdacf_pcm_capture_close - close callback for capture | |
288 | */ | |
db131548 | 289 | static int pdacf_pcm_capture_close(struct snd_pcm_substream *subs) |
1da177e4 | 290 | { |
db131548 | 291 | struct snd_pdacf *chip = snd_pcm_substream_chip(subs); |
1da177e4 LT |
292 | |
293 | if (!chip) | |
294 | return -EINVAL; | |
295 | pdacf_reinit(chip, 0); | |
296 | chip->pcm_substream = NULL; | |
297 | return 0; | |
298 | } | |
299 | ||
300 | ||
301 | /* | |
302 | * pdacf_pcm_capture_pointer - pointer callback for capture | |
303 | */ | |
db131548 | 304 | static snd_pcm_uframes_t pdacf_pcm_capture_pointer(struct snd_pcm_substream *subs) |
1da177e4 | 305 | { |
db131548 | 306 | struct snd_pdacf *chip = snd_pcm_substream_chip(subs); |
1da177e4 LT |
307 | return chip->pcm_hwptr; |
308 | } | |
309 | ||
310 | /* | |
311 | * operators for PCM capture | |
312 | */ | |
db131548 | 313 | static struct snd_pcm_ops pdacf_pcm_capture_ops = { |
1da177e4 LT |
314 | .open = pdacf_pcm_capture_open, |
315 | .close = pdacf_pcm_capture_close, | |
316 | .ioctl = snd_pcm_lib_ioctl, | |
317 | .hw_params = pdacf_pcm_hw_params, | |
318 | .hw_free = pdacf_pcm_hw_free, | |
319 | .prepare = pdacf_pcm_prepare, | |
320 | .trigger = pdacf_pcm_trigger, | |
321 | .pointer = pdacf_pcm_capture_pointer, | |
322 | .page = snd_pcm_get_vmalloc_page, | |
323 | }; | |
324 | ||
325 | ||
1da177e4 LT |
326 | /* |
327 | * snd_pdacf_pcm_new - create and initialize a pcm | |
328 | */ | |
db131548 | 329 | int snd_pdacf_pcm_new(struct snd_pdacf *chip) |
1da177e4 | 330 | { |
db131548 | 331 | struct snd_pcm *pcm; |
1da177e4 LT |
332 | int err; |
333 | ||
334 | err = snd_pcm_new(chip->card, "PDAudioCF", 0, 0, 1, &pcm); | |
335 | if (err < 0) | |
336 | return err; | |
337 | ||
338 | snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pdacf_pcm_capture_ops); | |
339 | ||
340 | pcm->private_data = chip; | |
1da177e4 LT |
341 | pcm->info_flags = 0; |
342 | strcpy(pcm->name, chip->card->shortname); | |
343 | chip->pcm = pcm; | |
344 | ||
345 | err = snd_ak4117_build(chip->ak4117, pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream); | |
346 | if (err < 0) | |
347 | return err; | |
348 | ||
349 | return 0; | |
350 | } |