123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308 |
- /*
- * Driver for Sound Core PDAudioCF soundcards
- *
- * PCM part
- *
- * Copyright (c) 2003 by Jaroslav Kysela <perex@perex.cz>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
- #include <linux/delay.h>
- #include <sound/core.h>
- #include <sound/asoundef.h>
- #include "pdaudiocf.h"
- /*
- * clear the SRAM contents
- */
- static int pdacf_pcm_clear_sram(struct snd_pdacf *chip)
- {
- int max_loop = 64 * 1024;
- while (inw(chip->port + PDAUDIOCF_REG_RDP) != inw(chip->port + PDAUDIOCF_REG_WDP)) {
- if (max_loop-- < 0)
- return -EIO;
- inw(chip->port + PDAUDIOCF_REG_MD);
- }
- return 0;
- }
- /*
- * pdacf_pcm_trigger - trigger callback for capture
- */
- static int pdacf_pcm_trigger(struct snd_pcm_substream *subs, int cmd)
- {
- struct snd_pdacf *chip = snd_pcm_substream_chip(subs);
- struct snd_pcm_runtime *runtime = subs->runtime;
- int inc, ret = 0, rate;
- unsigned short mask, val, tmp;
- if (chip->chip_status & PDAUDIOCF_STAT_IS_STALE)
- return -EBUSY;
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_START:
- chip->pcm_hwptr = 0;
- chip->pcm_tdone = 0;
- /* fall thru */
- case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- case SNDRV_PCM_TRIGGER_RESUME:
- mask = 0;
- val = PDAUDIOCF_RECORD;
- inc = 1;
- rate = snd_ak4117_check_rate_and_errors(chip->ak4117, AK4117_CHECK_NO_STAT|AK4117_CHECK_NO_RATE);
- break;
- case SNDRV_PCM_TRIGGER_STOP:
- case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- case SNDRV_PCM_TRIGGER_SUSPEND:
- mask = PDAUDIOCF_RECORD;
- val = 0;
- inc = -1;
- rate = 0;
- break;
- default:
- return -EINVAL;
- }
- spin_lock(&chip->reg_lock);
- chip->pcm_running += inc;
- tmp = pdacf_reg_read(chip, PDAUDIOCF_REG_SCR);
- if (chip->pcm_running) {
- if ((chip->ak4117->rcs0 & AK4117_UNLCK) || runtime->rate != rate) {
- chip->pcm_running -= inc;
- ret = -EIO;
- goto __end;
- }
- }
- tmp &= ~mask;
- tmp |= val;
- pdacf_reg_write(chip, PDAUDIOCF_REG_SCR, tmp);
- __end:
- spin_unlock(&chip->reg_lock);
- snd_ak4117_check_rate_and_errors(chip->ak4117, AK4117_CHECK_NO_RATE);
- return ret;
- }
- /*
- * pdacf_pcm_hw_params - hw_params callback for playback and capture
- */
- static int pdacf_pcm_hw_params(struct snd_pcm_substream *subs,
- struct snd_pcm_hw_params *hw_params)
- {
- return snd_pcm_lib_alloc_vmalloc_32_buffer
- (subs, params_buffer_bytes(hw_params));
- }
- /*
- * pdacf_pcm_hw_free - hw_free callback for playback and capture
- */
- static int pdacf_pcm_hw_free(struct snd_pcm_substream *subs)
- {
- return snd_pcm_lib_free_vmalloc_buffer(subs);
- }
- /*
- * pdacf_pcm_prepare - prepare callback for playback and capture
- */
- static int pdacf_pcm_prepare(struct snd_pcm_substream *subs)
- {
- struct snd_pdacf *chip = snd_pcm_substream_chip(subs);
- struct snd_pcm_runtime *runtime = subs->runtime;
- u16 val, nval, aval;
- if (chip->chip_status & PDAUDIOCF_STAT_IS_STALE)
- return -EBUSY;
- chip->pcm_channels = runtime->channels;
- chip->pcm_little = snd_pcm_format_little_endian(runtime->format) > 0;
- #ifdef SNDRV_LITTLE_ENDIAN
- chip->pcm_swab = snd_pcm_format_big_endian(runtime->format) > 0;
- #else
- chip->pcm_swab = chip->pcm_little;
- #endif
- if (snd_pcm_format_unsigned(runtime->format))
- chip->pcm_xor = 0x80008000;
- if (pdacf_pcm_clear_sram(chip) < 0)
- return -EIO;
-
- val = nval = pdacf_reg_read(chip, PDAUDIOCF_REG_SCR);
- nval &= ~(PDAUDIOCF_DATAFMT0|PDAUDIOCF_DATAFMT1);
- switch (runtime->format) {
- case SNDRV_PCM_FORMAT_S16_LE:
- case SNDRV_PCM_FORMAT_S16_BE:
- break;
- default: /* 24-bit */
- nval |= PDAUDIOCF_DATAFMT0 | PDAUDIOCF_DATAFMT1;
- break;
- }
- aval = 0;
- chip->pcm_sample = 4;
- switch (runtime->format) {
- case SNDRV_PCM_FORMAT_S16_LE:
- case SNDRV_PCM_FORMAT_S16_BE:
- aval = AK4117_DIF_16R;
- chip->pcm_frame = 2;
- chip->pcm_sample = 2;
- break;
- case SNDRV_PCM_FORMAT_S24_3LE:
- case SNDRV_PCM_FORMAT_S24_3BE:
- chip->pcm_sample = 3;
- /* fall through */
- default: /* 24-bit */
- aval = AK4117_DIF_24R;
- chip->pcm_frame = 3;
- chip->pcm_xor &= 0xffff0000;
- break;
- }
- if (val != nval) {
- snd_ak4117_reg_write(chip->ak4117, AK4117_REG_IO, AK4117_DIF2|AK4117_DIF1|AK4117_DIF0, aval);
- pdacf_reg_write(chip, PDAUDIOCF_REG_SCR, nval);
- }
- val = pdacf_reg_read(chip, PDAUDIOCF_REG_IER);
- val &= ~(PDAUDIOCF_IRQLVLEN1);
- val |= PDAUDIOCF_IRQLVLEN0;
- pdacf_reg_write(chip, PDAUDIOCF_REG_IER, val);
- chip->pcm_size = runtime->buffer_size;
- chip->pcm_period = runtime->period_size;
- chip->pcm_area = runtime->dma_area;
- return 0;
- }
- /*
- * capture hw information
- */
- static struct snd_pcm_hardware pdacf_pcm_capture_hw = {
- .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
- SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME |
- SNDRV_PCM_INFO_MMAP_VALID |
- SNDRV_PCM_INFO_BATCH),
- .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE |
- SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE |
- SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE,
- .rates = SNDRV_PCM_RATE_32000 |
- SNDRV_PCM_RATE_44100 |
- SNDRV_PCM_RATE_48000 |
- SNDRV_PCM_RATE_88200 |
- SNDRV_PCM_RATE_96000 |
- SNDRV_PCM_RATE_176400 |
- SNDRV_PCM_RATE_192000,
- .rate_min = 32000,
- .rate_max = 192000,
- .channels_min = 1,
- .channels_max = 2,
- .buffer_bytes_max = (512*1024),
- .period_bytes_min = 8*1024,
- .period_bytes_max = (64*1024),
- .periods_min = 2,
- .periods_max = 128,
- .fifo_size = 0,
- };
- /*
- * pdacf_pcm_capture_open - open callback for capture
- */
- static int pdacf_pcm_capture_open(struct snd_pcm_substream *subs)
- {
- struct snd_pcm_runtime *runtime = subs->runtime;
- struct snd_pdacf *chip = snd_pcm_substream_chip(subs);
- if (chip->chip_status & PDAUDIOCF_STAT_IS_STALE)
- return -EBUSY;
- runtime->hw = pdacf_pcm_capture_hw;
- runtime->private_data = chip;
- chip->pcm_substream = subs;
- return 0;
- }
- /*
- * pdacf_pcm_capture_close - close callback for capture
- */
- static int pdacf_pcm_capture_close(struct snd_pcm_substream *subs)
- {
- struct snd_pdacf *chip = snd_pcm_substream_chip(subs);
- if (!chip)
- return -EINVAL;
- pdacf_reinit(chip, 0);
- chip->pcm_substream = NULL;
- return 0;
- }
- /*
- * pdacf_pcm_capture_pointer - pointer callback for capture
- */
- static snd_pcm_uframes_t pdacf_pcm_capture_pointer(struct snd_pcm_substream *subs)
- {
- struct snd_pdacf *chip = snd_pcm_substream_chip(subs);
- return chip->pcm_hwptr;
- }
- /*
- * operators for PCM capture
- */
- static struct snd_pcm_ops pdacf_pcm_capture_ops = {
- .open = pdacf_pcm_capture_open,
- .close = pdacf_pcm_capture_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = pdacf_pcm_hw_params,
- .hw_free = pdacf_pcm_hw_free,
- .prepare = pdacf_pcm_prepare,
- .trigger = pdacf_pcm_trigger,
- .pointer = pdacf_pcm_capture_pointer,
- .page = snd_pcm_lib_get_vmalloc_page,
- .mmap = snd_pcm_lib_mmap_vmalloc,
- };
- /*
- * snd_pdacf_pcm_new - create and initialize a pcm
- */
- int snd_pdacf_pcm_new(struct snd_pdacf *chip)
- {
- struct snd_pcm *pcm;
- int err;
- err = snd_pcm_new(chip->card, "PDAudioCF", 0, 0, 1, &pcm);
- if (err < 0)
- return err;
-
- snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pdacf_pcm_capture_ops);
- pcm->private_data = chip;
- pcm->info_flags = 0;
- strcpy(pcm->name, chip->card->shortname);
- chip->pcm = pcm;
-
- err = snd_ak4117_build(chip->ak4117, pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream);
- if (err < 0)
- return err;
- return 0;
- }
|