Merge branch 'core/iommu' into core/urgent
[linux-2.6] / sound / pcmcia / pdaudiocf / pdaudiocf_pcm.c
1 /*
2  * Driver for Sound Core PDAudioCF soundcards
3  *
4  * PCM part
5  *
6  * Copyright (c) 2003 by Jaroslav Kysela <perex@perex.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 <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 */
36 static struct page *snd_pcm_get_vmalloc_page(struct snd_pcm_substream *subs, unsigned long offset)
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  */
46 static int snd_pcm_alloc_vmalloc_buffer(struct snd_pcm_substream *subs, size_t size)
47 {
48         struct snd_pcm_runtime *runtime = subs->runtime;
49         if (runtime->dma_area) {
50                 if (runtime->dma_bytes >= size)
51                         return 0; /* already enough large */
52                 vfree(runtime->dma_area);
53         }
54         runtime->dma_area = vmalloc_32(size);
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  */
65 static int snd_pcm_free_vmalloc_buffer(struct snd_pcm_substream *subs)
66 {
67         struct snd_pcm_runtime *runtime = subs->runtime;
68
69         vfree(runtime->dma_area);
70         runtime->dma_area = NULL;
71         return 0;
72 }
73
74 /*
75  * clear the SRAM contents
76  */
77 static int pdacf_pcm_clear_sram(struct snd_pdacf *chip)
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  */
92 static int pdacf_pcm_trigger(struct snd_pcm_substream *subs, int cmd)
93 {
94         struct snd_pdacf *chip = snd_pcm_substream_chip(subs);
95         struct snd_pcm_runtime *runtime = subs->runtime;
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  */
147 static int pdacf_pcm_hw_params(struct snd_pcm_substream *subs,
148                                      struct snd_pcm_hw_params *hw_params)
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  */
156 static int pdacf_pcm_hw_free(struct snd_pcm_substream *subs)
157 {
158         return snd_pcm_free_vmalloc_buffer(subs);
159 }
160
161 /*
162  * pdacf_pcm_prepare - prepare callback for playback and capture
163  */
164 static int pdacf_pcm_prepare(struct snd_pcm_substream *subs)
165 {
166         struct snd_pdacf *chip = snd_pcm_substream_chip(subs);
167         struct snd_pcm_runtime *runtime = subs->runtime;
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;
210                 /* fall through */
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
240 static struct snd_pcm_hardware pdacf_pcm_capture_hw = {
241         .info =                 (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
242                                  SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME |
243                                  SNDRV_PCM_INFO_MMAP_VALID),
244         .formats =              SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE |
245                                 SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE |
246                                 SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE,
247         .rates =                SNDRV_PCM_RATE_32000 |
248                                 SNDRV_PCM_RATE_44100 |
249                                 SNDRV_PCM_RATE_48000 |
250                                 SNDRV_PCM_RATE_88200 |
251                                 SNDRV_PCM_RATE_96000 |
252                                 SNDRV_PCM_RATE_176400 |
253                                 SNDRV_PCM_RATE_192000,
254         .rate_min =             32000,
255         .rate_max =             192000,
256         .channels_min =         1,
257         .channels_max =         2,
258         .buffer_bytes_max =     (512*1024),
259         .period_bytes_min =     8*1024,
260         .period_bytes_max =     (64*1024),
261         .periods_min =          2,
262         .periods_max =          128,
263         .fifo_size =            0,
264 };
265
266
267 /*
268  * pdacf_pcm_capture_open - open callback for capture
269  */
270 static int pdacf_pcm_capture_open(struct snd_pcm_substream *subs)
271 {
272         struct snd_pcm_runtime *runtime = subs->runtime;
273         struct snd_pdacf *chip = snd_pcm_substream_chip(subs);
274
275         if (chip->chip_status & PDAUDIOCF_STAT_IS_STALE)
276                 return -EBUSY;
277
278         runtime->hw = pdacf_pcm_capture_hw;
279         runtime->private_data = chip;
280         chip->pcm_substream = subs;
281
282         return 0;
283 }
284
285 /*
286  * pdacf_pcm_capture_close - close callback for capture
287  */
288 static int pdacf_pcm_capture_close(struct snd_pcm_substream *subs)
289 {
290         struct snd_pdacf *chip = snd_pcm_substream_chip(subs);
291
292         if (!chip)
293                 return -EINVAL;
294         pdacf_reinit(chip, 0);
295         chip->pcm_substream = NULL;
296         return 0;
297 }
298
299
300 /*
301  * pdacf_pcm_capture_pointer - pointer callback for capture
302  */
303 static snd_pcm_uframes_t pdacf_pcm_capture_pointer(struct snd_pcm_substream *subs)
304 {
305         struct snd_pdacf *chip = snd_pcm_substream_chip(subs);
306         return chip->pcm_hwptr;
307 }
308
309 /*
310  * operators for PCM capture
311  */
312 static struct snd_pcm_ops pdacf_pcm_capture_ops = {
313         .open =         pdacf_pcm_capture_open,
314         .close =        pdacf_pcm_capture_close,
315         .ioctl =        snd_pcm_lib_ioctl,
316         .hw_params =    pdacf_pcm_hw_params,
317         .hw_free =      pdacf_pcm_hw_free,
318         .prepare =      pdacf_pcm_prepare,
319         .trigger =      pdacf_pcm_trigger,
320         .pointer =      pdacf_pcm_capture_pointer,
321         .page =         snd_pcm_get_vmalloc_page,
322 };
323
324
325 /*
326  * snd_pdacf_pcm_new - create and initialize a pcm
327  */
328 int snd_pdacf_pcm_new(struct snd_pdacf *chip)
329 {
330         struct snd_pcm *pcm;
331         int err;
332
333         err = snd_pcm_new(chip->card, "PDAudioCF", 0, 0, 1, &pcm);
334         if (err < 0)
335                 return err;
336                 
337         snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pdacf_pcm_capture_ops);
338
339         pcm->private_data = chip;
340         pcm->info_flags = 0;
341         strcpy(pcm->name, chip->card->shortname);
342         chip->pcm = pcm;
343         
344         err = snd_ak4117_build(chip->ak4117, pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream);
345         if (err < 0)
346                 return err;
347
348         return 0;
349 }