2 * Driver for Sound Core PDAudioCF soundcards
6 * Copyright (c) 2003 by Jaroslav Kysela <perex@suse.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 <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"
33 * we use a vmalloc'ed (sg-)buffer
36 /* get the physical page pointer on the given offset */
37 static struct page
*snd_pcm_get_vmalloc_page(struct snd_pcm_substream
*subs
, unsigned long offset
)
39 void *pageptr
= subs
->runtime
->dma_area
+ offset
;
40 return vmalloc_to_page(pageptr
);
45 * NOTE: this may be called not only once per pcm open!
47 static int snd_pcm_alloc_vmalloc_buffer(struct snd_pcm_substream
*subs
, size_t size
)
49 struct snd_pcm_runtime
*runtime
= subs
->runtime
;
50 if (runtime
->dma_area
) {
51 if (runtime
->dma_bytes
>= size
)
52 return 0; /* already enough large */
53 vfree(runtime
->dma_area
);
55 runtime
->dma_area
= vmalloc_32(size
);
56 if (! runtime
->dma_area
)
58 runtime
->dma_bytes
= size
;
64 * NOTE: this may be called not only once per pcm open!
66 static int snd_pcm_free_vmalloc_buffer(struct snd_pcm_substream
*subs
)
68 struct snd_pcm_runtime
*runtime
= subs
->runtime
;
70 vfree(runtime
->dma_area
);
71 runtime
->dma_area
= NULL
;
76 * clear the SRAM contents
78 static int pdacf_pcm_clear_sram(struct snd_pdacf
*chip
)
80 int max_loop
= 64 * 1024;
82 while (inw(chip
->port
+ PDAUDIOCF_REG_RDP
) != inw(chip
->port
+ PDAUDIOCF_REG_WDP
)) {
85 inw(chip
->port
+ PDAUDIOCF_REG_MD
);
91 * pdacf_pcm_trigger - trigger callback for capture
93 static int pdacf_pcm_trigger(struct snd_pcm_substream
*subs
, int cmd
)
95 struct snd_pdacf
*chip
= snd_pcm_substream_chip(subs
);
96 struct snd_pcm_runtime
*runtime
= subs
->runtime
;
97 int inc
, ret
= 0, rate
;
98 unsigned short mask
, val
, tmp
;
100 if (chip
->chip_status
& PDAUDIOCF_STAT_IS_STALE
)
104 case SNDRV_PCM_TRIGGER_START
:
108 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE
:
109 case SNDRV_PCM_TRIGGER_RESUME
:
111 val
= PDAUDIOCF_RECORD
;
113 rate
= snd_ak4117_check_rate_and_errors(chip
->ak4117
, AK4117_CHECK_NO_STAT
|AK4117_CHECK_NO_RATE
);
115 case SNDRV_PCM_TRIGGER_STOP
:
116 case SNDRV_PCM_TRIGGER_PAUSE_PUSH
:
117 case SNDRV_PCM_TRIGGER_SUSPEND
:
118 mask
= PDAUDIOCF_RECORD
;
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
;
138 pdacf_reg_write(chip
, PDAUDIOCF_REG_SCR
, tmp
);
140 spin_unlock(&chip
->reg_lock
);
141 snd_ak4117_check_rate_and_errors(chip
->ak4117
, AK4117_CHECK_NO_RATE
);
146 * pdacf_pcm_hw_params - hw_params callback for playback and capture
148 static int pdacf_pcm_hw_params(struct snd_pcm_substream
*subs
,
149 struct snd_pcm_hw_params
*hw_params
)
151 return snd_pcm_alloc_vmalloc_buffer(subs
, params_buffer_bytes(hw_params
));
155 * pdacf_pcm_hw_free - hw_free callback for playback and capture
157 static int pdacf_pcm_hw_free(struct snd_pcm_substream
*subs
)
159 return snd_pcm_free_vmalloc_buffer(subs
);
163 * pdacf_pcm_prepare - prepare callback for playback and capture
165 static int pdacf_pcm_prepare(struct snd_pcm_substream
*subs
)
167 struct snd_pdacf
*chip
= snd_pcm_substream_chip(subs
);
168 struct snd_pcm_runtime
*runtime
= subs
->runtime
;
171 if (chip
->chip_status
& PDAUDIOCF_STAT_IS_STALE
)
174 chip
->pcm_channels
= runtime
->channels
;
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;
180 chip
->pcm_swab
= chip
->pcm_little
;
183 if (snd_pcm_format_unsigned(runtime
->format
))
184 chip
->pcm_xor
= 0x80008000;
186 if (pdacf_pcm_clear_sram(chip
) < 0)
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
:
195 default: /* 24-bit */
196 nval
|= PDAUDIOCF_DATAFMT0
| PDAUDIOCF_DATAFMT1
;
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
;
206 chip
->pcm_sample
= 2;
208 case SNDRV_PCM_FORMAT_S24_3LE
:
209 case SNDRV_PCM_FORMAT_S24_3BE
:
210 chip
->pcm_sample
= 3;
212 default: /* 24-bit */
213 aval
= AK4117_DIF_24R
;
215 chip
->pcm_xor
&= 0xffff0000;
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
);
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
);
229 chip
->pcm_size
= runtime
->buffer_size
;
230 chip
->pcm_period
= runtime
->period_size
;
231 chip
->pcm_area
= runtime
->dma_area
;
238 * capture hw information
241 static struct snd_pcm_hardware pdacf_pcm_capture_hw
= {
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
,
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
);