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