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 | ||
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; |
2a578f3b JJ |
69 | |
70 | vfree(runtime->dma_area); | |
71 | runtime->dma_area = NULL; | |
1da177e4 LT |
72 | return 0; |
73 | } | |
74 | ||
75 | /* | |
76 | * clear the SRAM contents | |
77 | */ | |
db131548 | 78 | static int pdacf_pcm_clear_sram(struct snd_pdacf *chip) |
1da177e4 LT |
79 | { |
80 | int max_loop = 64 * 1024; | |
81 | ||
82 | while (inw(chip->port + PDAUDIOCF_REG_RDP) != inw(chip->port + PDAUDIOCF_REG_WDP)) { | |
83 | if (max_loop-- < 0) | |
84 | return -EIO; | |
85 | inw(chip->port + PDAUDIOCF_REG_MD); | |
86 | } | |
87 | return 0; | |
88 | } | |
89 | ||
90 | /* | |
91 | * pdacf_pcm_trigger - trigger callback for capture | |
92 | */ | |
db131548 | 93 | static int pdacf_pcm_trigger(struct snd_pcm_substream *subs, int cmd) |
1da177e4 | 94 | { |
db131548 TI |
95 | struct snd_pdacf *chip = snd_pcm_substream_chip(subs); |
96 | struct snd_pcm_runtime *runtime = subs->runtime; | |
1da177e4 LT |
97 | int inc, ret = 0, rate; |
98 | unsigned short mask, val, tmp; | |
99 | ||
100 | if (chip->chip_status & PDAUDIOCF_STAT_IS_STALE) | |
101 | return -EBUSY; | |
102 | ||
103 | switch (cmd) { | |
104 | case SNDRV_PCM_TRIGGER_START: | |
105 | chip->pcm_hwptr = 0; | |
106 | chip->pcm_tdone = 0; | |
107 | /* fall thru */ | |
108 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: | |
109 | case SNDRV_PCM_TRIGGER_RESUME: | |
110 | mask = 0; | |
111 | val = PDAUDIOCF_RECORD; | |
112 | inc = 1; | |
113 | rate = snd_ak4117_check_rate_and_errors(chip->ak4117, AK4117_CHECK_NO_STAT|AK4117_CHECK_NO_RATE); | |
114 | break; | |
115 | case SNDRV_PCM_TRIGGER_STOP: | |
116 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: | |
117 | case SNDRV_PCM_TRIGGER_SUSPEND: | |
118 | mask = PDAUDIOCF_RECORD; | |
119 | val = 0; | |
120 | inc = -1; | |
121 | rate = 0; | |
122 | break; | |
123 | default: | |
124 | return -EINVAL; | |
125 | } | |
126 | spin_lock(&chip->reg_lock); | |
127 | chip->pcm_running += inc; | |
128 | tmp = pdacf_reg_read(chip, PDAUDIOCF_REG_SCR); | |
129 | if (chip->pcm_running) { | |
130 | if ((chip->ak4117->rcs0 & AK4117_UNLCK) || runtime->rate != rate) { | |
131 | chip->pcm_running -= inc; | |
132 | ret = -EIO; | |
133 | goto __end; | |
134 | } | |
135 | } | |
136 | tmp &= ~mask; | |
137 | tmp |= val; | |
138 | pdacf_reg_write(chip, PDAUDIOCF_REG_SCR, tmp); | |
139 | __end: | |
140 | spin_unlock(&chip->reg_lock); | |
141 | snd_ak4117_check_rate_and_errors(chip->ak4117, AK4117_CHECK_NO_RATE); | |
142 | return ret; | |
143 | } | |
144 | ||
145 | /* | |
146 | * pdacf_pcm_hw_params - hw_params callback for playback and capture | |
147 | */ | |
db131548 TI |
148 | static int pdacf_pcm_hw_params(struct snd_pcm_substream *subs, |
149 | struct snd_pcm_hw_params *hw_params) | |
1da177e4 LT |
150 | { |
151 | return snd_pcm_alloc_vmalloc_buffer(subs, params_buffer_bytes(hw_params)); | |
152 | } | |
153 | ||
154 | /* | |
155 | * pdacf_pcm_hw_free - hw_free callback for playback and capture | |
156 | */ | |
db131548 | 157 | static int pdacf_pcm_hw_free(struct snd_pcm_substream *subs) |
1da177e4 LT |
158 | { |
159 | return snd_pcm_free_vmalloc_buffer(subs); | |
160 | } | |
161 | ||
162 | /* | |
163 | * pdacf_pcm_prepare - prepare callback for playback and capture | |
164 | */ | |
db131548 | 165 | static int pdacf_pcm_prepare(struct snd_pcm_substream *subs) |
1da177e4 | 166 | { |
db131548 TI |
167 | struct snd_pdacf *chip = snd_pcm_substream_chip(subs); |
168 | struct snd_pcm_runtime *runtime = subs->runtime; | |
1da177e4 LT |
169 | u16 val, nval, aval; |
170 | ||
171 | if (chip->chip_status & PDAUDIOCF_STAT_IS_STALE) | |
172 | return -EBUSY; | |
173 | ||
174 | chip->pcm_channels = runtime->channels; | |
175 | ||
176 | chip->pcm_little = snd_pcm_format_little_endian(runtime->format) > 0; | |
177 | #ifdef SNDRV_LITTLE_ENDIAN | |
178 | chip->pcm_swab = snd_pcm_format_big_endian(runtime->format) > 0; | |
179 | #else | |
180 | chip->pcm_swab = chip->pcm_little; | |
181 | #endif | |
182 | ||
183 | if (snd_pcm_format_unsigned(runtime->format)) | |
184 | chip->pcm_xor = 0x80008000; | |
185 | ||
186 | if (pdacf_pcm_clear_sram(chip) < 0) | |
187 | return -EIO; | |
188 | ||
189 | val = nval = pdacf_reg_read(chip, PDAUDIOCF_REG_SCR); | |
190 | nval &= ~(PDAUDIOCF_DATAFMT0|PDAUDIOCF_DATAFMT1); | |
191 | switch (runtime->format) { | |
192 | case SNDRV_PCM_FORMAT_S16_LE: | |
193 | case SNDRV_PCM_FORMAT_S16_BE: | |
194 | break; | |
195 | default: /* 24-bit */ | |
196 | nval |= PDAUDIOCF_DATAFMT0 | PDAUDIOCF_DATAFMT1; | |
197 | break; | |
198 | } | |
199 | aval = 0; | |
200 | chip->pcm_sample = 4; | |
201 | switch (runtime->format) { | |
202 | case SNDRV_PCM_FORMAT_S16_LE: | |
203 | case SNDRV_PCM_FORMAT_S16_BE: | |
204 | aval = AK4117_DIF_16R; | |
205 | chip->pcm_frame = 2; | |
206 | chip->pcm_sample = 2; | |
207 | break; | |
208 | case SNDRV_PCM_FORMAT_S24_3LE: | |
209 | case SNDRV_PCM_FORMAT_S24_3BE: | |
210 | chip->pcm_sample = 3; | |
93b1fae4 | 211 | /* fall through */ |
1da177e4 LT |
212 | default: /* 24-bit */ |
213 | aval = AK4117_DIF_24R; | |
214 | chip->pcm_frame = 3; | |
215 | chip->pcm_xor &= 0xffff0000; | |
216 | break; | |
217 | } | |
218 | ||
219 | if (val != nval) { | |
220 | snd_ak4117_reg_write(chip->ak4117, AK4117_REG_IO, AK4117_DIF2|AK4117_DIF1|AK4117_DIF0, aval); | |
221 | pdacf_reg_write(chip, PDAUDIOCF_REG_SCR, nval); | |
222 | } | |
223 | ||
224 | val = pdacf_reg_read(chip, PDAUDIOCF_REG_IER); | |
225 | val &= ~(PDAUDIOCF_IRQLVLEN1); | |
226 | val |= PDAUDIOCF_IRQLVLEN0; | |
227 | pdacf_reg_write(chip, PDAUDIOCF_REG_IER, val); | |
228 | ||
229 | chip->pcm_size = runtime->buffer_size; | |
230 | chip->pcm_period = runtime->period_size; | |
231 | chip->pcm_area = runtime->dma_area; | |
232 | ||
233 | return 0; | |
234 | } | |
235 | ||
236 | ||
237 | /* | |
238 | * capture hw information | |
239 | */ | |
240 | ||
db131548 | 241 | static struct snd_pcm_hardware pdacf_pcm_capture_hw = { |
1da177e4 LT |
242 | .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | |
243 | SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME | | |
244 | SNDRV_PCM_INFO_MMAP_VALID), | |
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 | } |