2 * Driver for Sound Core PDAudioCF soundcards
6 * Copyright (c) 2003 by Jaroslav Kysela <perex@perex.cz>
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.
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.
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
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"
32 * we use a vmalloc'ed (sg-)buffer
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
)
38 void *pageptr
= subs
->runtime
->dma_area
+ offset
;
39 return vmalloc_to_page(pageptr
);
44 * NOTE: this may be called not only once per pcm open!
46 static int snd_pcm_alloc_vmalloc_buffer(struct snd_pcm_substream
*subs
, size_t size
)
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
);
54 runtime
->dma_area
= vmalloc_32(size
);
55 if (! runtime
->dma_area
)
57 runtime
->dma_bytes
= size
;
63 * NOTE: this may be called not only once per pcm open!
65 static int snd_pcm_free_vmalloc_buffer(struct snd_pcm_substream
*subs
)
67 struct snd_pcm_runtime
*runtime
= subs
->runtime
;
69 vfree(runtime
->dma_area
);
70 runtime
->dma_area
= NULL
;
75 * clear the SRAM contents
77 static int pdacf_pcm_clear_sram(struct snd_pdacf
*chip
)
79 int max_loop
= 64 * 1024;
81 while (inw(chip
->port
+ PDAUDIOCF_REG_RDP
) != inw(chip
->port
+ PDAUDIOCF_REG_WDP
)) {
84 inw(chip
->port
+ PDAUDIOCF_REG_MD
);
90 * pdacf_pcm_trigger - trigger callback for capture
92 static int pdacf_pcm_trigger(struct snd_pcm_substream
*subs
, int cmd
)
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
;
99 if (chip
->chip_status
& PDAUDIOCF_STAT_IS_STALE
)
103 case SNDRV_PCM_TRIGGER_START
:
107 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE
:
108 case SNDRV_PCM_TRIGGER_RESUME
:
110 val
= PDAUDIOCF_RECORD
;
112 rate
= snd_ak4117_check_rate_and_errors(chip
->ak4117
, AK4117_CHECK_NO_STAT
|AK4117_CHECK_NO_RATE
);
114 case SNDRV_PCM_TRIGGER_STOP
:
115 case SNDRV_PCM_TRIGGER_PAUSE_PUSH
:
116 case SNDRV_PCM_TRIGGER_SUSPEND
:
117 mask
= PDAUDIOCF_RECORD
;
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
;
137 pdacf_reg_write(chip
, PDAUDIOCF_REG_SCR
, tmp
);
139 spin_unlock(&chip
->reg_lock
);
140 snd_ak4117_check_rate_and_errors(chip
->ak4117
, AK4117_CHECK_NO_RATE
);
145 * pdacf_pcm_hw_params - hw_params callback for playback and capture
147 static int pdacf_pcm_hw_params(struct snd_pcm_substream
*subs
,
148 struct snd_pcm_hw_params
*hw_params
)
150 return snd_pcm_alloc_vmalloc_buffer(subs
, params_buffer_bytes(hw_params
));
154 * pdacf_pcm_hw_free - hw_free callback for playback and capture
156 static int pdacf_pcm_hw_free(struct snd_pcm_substream
*subs
)
158 return snd_pcm_free_vmalloc_buffer(subs
);
162 * pdacf_pcm_prepare - prepare callback for playback and capture
164 static int pdacf_pcm_prepare(struct snd_pcm_substream
*subs
)
166 struct snd_pdacf
*chip
= snd_pcm_substream_chip(subs
);
167 struct snd_pcm_runtime
*runtime
= subs
->runtime
;
170 if (chip
->chip_status
& PDAUDIOCF_STAT_IS_STALE
)
173 chip
->pcm_channels
= runtime
->channels
;
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;
179 chip
->pcm_swab
= chip
->pcm_little
;
182 if (snd_pcm_format_unsigned(runtime
->format
))
183 chip
->pcm_xor
= 0x80008000;
185 if (pdacf_pcm_clear_sram(chip
) < 0)
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
:
194 default: /* 24-bit */
195 nval
|= PDAUDIOCF_DATAFMT0
| PDAUDIOCF_DATAFMT1
;
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
;
205 chip
->pcm_sample
= 2;
207 case SNDRV_PCM_FORMAT_S24_3LE
:
208 case SNDRV_PCM_FORMAT_S24_3BE
:
209 chip
->pcm_sample
= 3;
211 default: /* 24-bit */
212 aval
= AK4117_DIF_24R
;
214 chip
->pcm_xor
&= 0xffff0000;
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
);
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
);
228 chip
->pcm_size
= runtime
->buffer_size
;
229 chip
->pcm_period
= runtime
->period_size
;
230 chip
->pcm_area
= runtime
->dma_area
;
237 * capture hw information
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 SNDRV_PCM_INFO_BATCH
),
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
,
259 .buffer_bytes_max
= (512*1024),
260 .period_bytes_min
= 8*1024,
261 .period_bytes_max
= (64*1024),
269 * pdacf_pcm_capture_open - open callback for capture
271 static int pdacf_pcm_capture_open(struct snd_pcm_substream
*subs
)
273 struct snd_pcm_runtime
*runtime
= subs
->runtime
;
274 struct snd_pdacf
*chip
= snd_pcm_substream_chip(subs
);
276 if (chip
->chip_status
& PDAUDIOCF_STAT_IS_STALE
)
279 runtime
->hw
= pdacf_pcm_capture_hw
;
280 runtime
->private_data
= chip
;
281 chip
->pcm_substream
= subs
;
287 * pdacf_pcm_capture_close - close callback for capture
289 static int pdacf_pcm_capture_close(struct snd_pcm_substream
*subs
)
291 struct snd_pdacf
*chip
= snd_pcm_substream_chip(subs
);
295 pdacf_reinit(chip
, 0);
296 chip
->pcm_substream
= NULL
;
302 * pdacf_pcm_capture_pointer - pointer callback for capture
304 static snd_pcm_uframes_t
pdacf_pcm_capture_pointer(struct snd_pcm_substream
*subs
)
306 struct snd_pdacf
*chip
= snd_pcm_substream_chip(subs
);
307 return chip
->pcm_hwptr
;
311 * operators for PCM capture
313 static struct snd_pcm_ops pdacf_pcm_capture_ops
= {
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
,
327 * snd_pdacf_pcm_new - create and initialize a pcm
329 int snd_pdacf_pcm_new(struct snd_pdacf
*chip
)
334 err
= snd_pcm_new(chip
->card
, "PDAudioCF", 0, 0, 1, &pcm
);
338 snd_pcm_set_ops(pcm
, SNDRV_PCM_STREAM_CAPTURE
, &pdacf_pcm_capture_ops
);
340 pcm
->private_data
= chip
;
342 strcpy(pcm
->name
, chip
->card
->shortname
);
345 err
= snd_ak4117_build(chip
->ak4117
, pcm
->streams
[SNDRV_PCM_STREAM_CAPTURE
].substream
);