From mboxrd@z Thu Jan 1 00:00:00 1970 From: Takashi Iwai Subject: Tring to support RME32 full-duplex Date: Fri, 18 Jun 2004 17:17:05 +0200 Sender: alsa-devel-admin@lists.sourceforge.net Message-ID: Mime-Version: 1.0 (generated by SEMI 1.14.5 - "Awara-Onsen") Content-Type: multipart/mixed; boundary="Multipart_Fri_Jun_18_17:17:04_2004-1" Return-path: Errors-To: alsa-devel-admin@lists.sourceforge.net List-Unsubscribe: , List-Post: List-Help: List-Subscribe: , List-Archive: To: alsa-devel@lists.sourceforge.net List-Id: alsa-devel@alsa-project.org --Multipart_Fri_Jun_18_17:17:04_2004-1 Content-Type: text/plain; charset=US-ASCII Hi, (I believe this interests mainly Martin.) The patch below adds (hopefully) the support the full-duplex on RME32. You'll need to applay the mmap-iomem patch before this one. If you have an RME32, please test and debug it ;) Takashi --Multipart_Fri_Jun_18_17:17:04_2004-1 Content-Type: text/plain; charset=US-ASCII Index: alsa-kernel/pci/rme32.c =================================================================== RCS file: /suse/tiwai/cvs/alsa/alsa-kernel/pci/rme32.c,v retrieving revision 1.36 diff -u -r1.36 rme32.c --- alsa-kernel/pci/rme32.c 18 Jun 2004 13:57:32 -0000 1.36 +++ alsa-kernel/pci/rme32.c 18 Jun 2004 15:10:48 -0000 @@ -52,6 +52,19 @@ * patch would be welcome! * * **************************************************************************** + * + * "The story after the long seeking" -- tiwai + * + * Ok, the situation regarding the full duplex is now improved a bit. + * In the fullduplex mode (given by the module parameter), the hardware buffer + * is split to halves for read and write directions at the DMA pointer. + * That is, the half above the current DMA pointer is used for write, and + * the half below is used for read. To mangle this strange behavior, an + * software intermediate buffer is introduced. This is, of course, not good + * from the viewpoint of the data transfer efficiency. However, this allows + * you to use arbitrary buffer sizes, instead of the fixed I/O buffer size. + * + * **************************************************************************** */ @@ -68,6 +81,7 @@ #include #include #include +#include #include #include @@ -76,6 +90,7 @@ static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */ +static int fullduplex[SNDRV_CARDS]; // = {[0 ... (SNDRV_CARDS - 1)] = 1}; static int boot_devs; module_param_array(index, int, boot_devs, 0444); @@ -87,6 +102,8 @@ module_param_array(enable, bool, boot_devs, 0444); MODULE_PARM_DESC(enable, "Enable RME Digi32 soundcard."); MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); +module_param_array(fullduplex, bool, boot_devs, 0444); +MODULE_PARM_DESC(fullduplex, "Support full-duplex mode."); MODULE_AUTHOR("Martin Langer "); MODULE_DESCRIPTION("RME Digi32, Digi32/8, Digi32 PRO"); MODULE_LICENSE("GPL"); @@ -168,6 +185,9 @@ /* Block sizes in bytes */ #define RME32_BLOCK_SIZE 8192 +/* Software intermediate buffer (max) size */ +#define RME32_MID_BUFFER_SIZE (1024*1024) + /* Hardware revisions */ #define RME32_32_REVISION 192 #define RME32_328_REVISION_OLD 100 @@ -213,6 +233,12 @@ size_t playback_periodsize; /* in bytes, zero if not used */ size_t capture_periodsize; /* in bytes, zero if not used */ + unsigned int fullduplex_mode; + int running; + + snd_pcm_indirect_t playback_pcm; + snd_pcm_indirect_t capture_pcm; + snd_card_t *card; snd_pcm_t *spdif_pcm; snd_pcm_t *adat_pcm; @@ -239,33 +265,16 @@ static int snd_rme32_capture_prepare(snd_pcm_substream_t * substream); -static int -snd_rme32_playback_trigger(snd_pcm_substream_t * substream, int cmd); - -static int -snd_rme32_capture_trigger(snd_pcm_substream_t * substream, int cmd); - -static snd_pcm_uframes_t -snd_rme32_playback_pointer(snd_pcm_substream_t * substream); - -static snd_pcm_uframes_t -snd_rme32_capture_pointer(snd_pcm_substream_t * substream); +static int snd_rme32_pcm_trigger(snd_pcm_substream_t * substream, int cmd); static void snd_rme32_proc_init(rme32_t * rme32); static int snd_rme32_create_switches(snd_card_t * card, rme32_t * rme32); -static inline unsigned int snd_rme32_playback_ptr(rme32_t * rme32) -{ - - return (readl(rme32->iobase + RME32_IO_GET_POS) - & RME32_RCR_AUDIO_ADDR_MASK) >> rme32->playback_frlog; -} - -static inline unsigned int snd_rme32_capture_ptr(rme32_t * rme32) +static inline unsigned int snd_rme32_pcm_byteptr(rme32_t * rme32) { return (readl(rme32->iobase + RME32_IO_GET_POS) - & RME32_RCR_AUDIO_ADDR_MASK) >> rme32->capture_frlog; + & RME32_RCR_AUDIO_ADDR_MASK); } static int snd_rme32_ratecode(int rate) @@ -281,6 +290,7 @@ return 0; } +/* silence callback for halfduplex mode */ static int snd_rme32_playback_silence(snd_pcm_substream_t * substream, int channel, /* not used (interleaved data) */ snd_pcm_uframes_t pos, snd_pcm_uframes_t count) @@ -292,6 +302,7 @@ return 0; } +/* copy callback for halfduplex mode */ static int snd_rme32_playback_copy(snd_pcm_substream_t * substream, int channel, /* not used (interleaved data) */ snd_pcm_uframes_t pos, void *src, snd_pcm_uframes_t count) @@ -305,6 +316,7 @@ return 0; } +/* copy callback for halfduplex mode */ static int snd_rme32_capture_copy(snd_pcm_substream_t * substream, int channel, /* not used (interleaved data) */ snd_pcm_uframes_t pos, void *dst, snd_pcm_uframes_t count) @@ -320,14 +332,15 @@ } /* - * Digital output capabilites (S/PDIF) + * SPDIF I/O capabilites (half-duplex mode) */ -static snd_pcm_hardware_t snd_rme32_playback_spdif_info = { +static snd_pcm_hardware_t snd_rme32_spdif_info = { .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_MMAP_IOMEM | SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_PAUSE), + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_SYNC_START), .formats = (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE), .rates = (SNDRV_PCM_RATE_32000 | @@ -346,41 +359,16 @@ }; /* - * Digital input capabilites (S/PDIF) + * ADAT I/O capabilites (half-duplex mode) */ -static snd_pcm_hardware_t snd_rme32_capture_spdif_info = { - .info = (SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_MMAP_VALID | - SNDRV_PCM_INFO_MMAP_IOMEM | - SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_PAUSE), - .formats = (SNDRV_PCM_FMTBIT_S16_LE | - SNDRV_PCM_FMTBIT_S32_LE), - .rates = (SNDRV_PCM_RATE_32000 | - SNDRV_PCM_RATE_44100 | - SNDRV_PCM_RATE_48000), - .rate_min = 32000, - .rate_max = 48000, - .channels_min = 2, - .channels_max = 2, - .buffer_bytes_max = RME32_BUFFER_SIZE, - .period_bytes_min = RME32_BLOCK_SIZE, - .period_bytes_max = RME32_BLOCK_SIZE, - .periods_min = RME32_BUFFER_SIZE / RME32_BLOCK_SIZE, - .periods_max = RME32_BUFFER_SIZE / RME32_BLOCK_SIZE, - .fifo_size = 0, -}; - -/* - * Digital output capabilites (ADAT) - */ -static snd_pcm_hardware_t snd_rme32_playback_adat_info = +static snd_pcm_hardware_t snd_rme32_adat_info = { .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_MMAP_IOMEM | SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_PAUSE), + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_SYNC_START), .formats= SNDRV_PCM_FMTBIT_S16_LE, .rates = (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000), @@ -397,28 +385,54 @@ }; /* - * Digital input capabilites (ADAT) + * SPDIF I/O capabilites (full-duplex mode) + */ +static snd_pcm_hardware_t snd_rme32_spdif_fd_info = { + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_SYNC_START), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S32_LE), + .rates = (SNDRV_PCM_RATE_32000 | + SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000), + .rate_min = 32000, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = RME32_MID_BUFFER_SIZE, + .period_bytes_min = RME32_BLOCK_SIZE, + .period_bytes_max = RME32_BLOCK_SIZE, + .periods_min = 2, + .periods_max = RME32_MID_BUFFER_SIZE / RME32_BLOCK_SIZE, + .fifo_size = 0, +}; + +/* + * ADAT I/O capabilites (full-duplex mode) */ -static snd_pcm_hardware_t snd_rme32_capture_adat_info = +static snd_pcm_hardware_t snd_rme32_adat_fd_info = { .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | - SNDRV_PCM_INFO_MMAP_IOMEM | SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_PAUSE), - .formats = SNDRV_PCM_FMTBIT_S16_LE, + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_SYNC_START), + .formats= SNDRV_PCM_FMTBIT_S16_LE, .rates = (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000), .rate_min = 44100, .rate_max = 48000, - .channels_min = 8, + .channels_min = 8, .channels_max = 8, - .buffer_bytes_max = RME32_BUFFER_SIZE, + .buffer_bytes_max = RME32_MID_BUFFER_SIZE, .period_bytes_min = RME32_BLOCK_SIZE, .period_bytes_max = RME32_BLOCK_SIZE, - .periods_min = RME32_BUFFER_SIZE / RME32_BLOCK_SIZE, - .periods_max = RME32_BUFFER_SIZE / RME32_BLOCK_SIZE, - .fifo_size = 0, + .periods_min = 2, + .periods_max = RME32_MID_BUFFER_SIZE / RME32_BLOCK_SIZE, + .fifo_size = 0, }; static void snd_rme32_reset_dac(rme32_t *rme32) @@ -680,9 +694,15 @@ rme32_t *rme32 = _snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; - runtime->dma_area = (void *)(rme32->iobase + RME32_IO_DATA_BUFFER); - runtime->dma_addr = rme32->port + RME32_IO_DATA_BUFFER; - runtime->dma_bytes = RME32_BUFFER_SIZE; + if (rme32->fullduplex_mode) { + err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params)); + if (err < 0) + return err; + } else { + runtime->dma_area = (void *)(rme32->iobase + RME32_IO_DATA_BUFFER); + runtime->dma_addr = rme32->port + RME32_IO_DATA_BUFFER; + runtime->dma_bytes = RME32_BUFFER_SIZE; + } spin_lock_irq(&rme32->lock); if ((rme32->rcreg & RME32_RCR_KMODE) && @@ -730,9 +750,15 @@ rme32_t *rme32 = _snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; - runtime->dma_area = (void *)(rme32->iobase + RME32_IO_DATA_BUFFER); - runtime->dma_addr = rme32->port + RME32_IO_DATA_BUFFER; - runtime->dma_bytes = RME32_BUFFER_SIZE; + if (rme32->fullduplex_mode) { + err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params)); + if (err < 0) + return err; + } else { + runtime->dma_area = (void *)rme32->iobase + RME32_IO_DATA_BUFFER; + runtime->dma_addr = rme32->port + RME32_IO_DATA_BUFFER; + runtime->dma_bytes = RME32_BUFFER_SIZE; + } spin_lock_irqsave(&rme32->lock, flags); /* enable AutoSync for record-preparing */ @@ -777,17 +803,15 @@ return 0; } -static void snd_rme32_playback_start(rme32_t * rme32, int from_pause) +static int snd_rme32_pcm_hw_free(snd_pcm_substream_t * substream) { - if (!from_pause) { - writel(0, rme32->iobase + RME32_IO_RESET_POS); - } - - rme32->wcreg |= RME32_WCR_START; - writel(rme32->wcreg, rme32->iobase + RME32_IO_CONTROL_REGISTER); + rme32_t *rme32 = _snd_pcm_substream_chip(substream); + if (! rme32->fullduplex_mode) + return 0; + return snd_pcm_lib_free_pages(substream); } -static void snd_rme32_capture_start(rme32_t * rme32, int from_pause) +static void snd_rme32_pcm_start(rme32_t * rme32, int from_pause) { if (!from_pause) { writel(0, rme32->iobase + RME32_IO_RESET_POS); @@ -797,7 +821,7 @@ writel(rme32->wcreg, rme32->iobase + RME32_IO_CONTROL_REGISTER); } -static void snd_rme32_playback_stop(rme32_t * rme32) +static void snd_rme32_pcm_stop(rme32_t * rme32) { /* * Check if there is an unconfirmed IRQ, if so confirm it, or else @@ -811,16 +835,7 @@ if (rme32->wcreg & RME32_WCR_SEL) rme32->wcreg |= RME32_WCR_MUTE; writel(rme32->wcreg, rme32->iobase + RME32_IO_CONTROL_REGISTER); -} - -static void snd_rme32_capture_stop(rme32_t * rme32) -{ - rme32->rcreg = readl(rme32->iobase + RME32_IO_CONTROL_REGISTER); - if (rme32->rcreg & RME32_RCR_IRQ) { - writel(0, rme32->iobase + RME32_IO_CONFIRM_ACTION_IRQ); - } - rme32->wcreg &= ~RME32_WCR_START; - writel(rme32->wcreg, rme32->iobase + RME32_IO_CONTROL_REGISTER); + writel(0, rme32->iobase + RME32_IO_RESET_POS); } static irqreturn_t @@ -854,6 +869,18 @@ .mask = 0 }; +static void snd_rme32_set_buffer_constraint(rme32_t *rme32, snd_pcm_runtime_t *runtime) +{ + if (! rme32->fullduplex_mode) { + snd_pcm_hw_constraint_minmax(runtime, + SNDRV_PCM_HW_PARAM_BUFFER_BYTES, + RME32_BUFFER_SIZE, RME32_BUFFER_SIZE); + snd_pcm_hw_constraint_list(runtime, 0, + SNDRV_PCM_HW_PARAM_PERIOD_BYTES, + &hw_constraints_period_bytes); + } +} + static int snd_rme32_playback_spdif_open(snd_pcm_substream_t * substream) { unsigned long flags; @@ -873,7 +900,10 @@ rme32->playback_substream = substream; spin_unlock_irqrestore(&rme32->lock, flags); - runtime->hw = snd_rme32_playback_spdif_info; + if (rme32->fullduplex_mode) + runtime->hw = snd_rme32_spdif_fd_info; + else + runtime->hw = snd_rme32_spdif_info; if (rme32->pci->device == PCI_DEVICE_ID_DIGI32_PRO) { runtime->hw.rates |= SNDRV_PCM_RATE_64000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000; runtime->hw.rate_max = 96000; @@ -885,12 +915,8 @@ runtime->hw.rate_min = rate; runtime->hw.rate_max = rate; } - snd_pcm_hw_constraint_minmax(runtime, - SNDRV_PCM_HW_PARAM_BUFFER_BYTES, - RME32_BUFFER_SIZE, RME32_BUFFER_SIZE); - snd_pcm_hw_constraint_list(runtime, 0, - SNDRV_PCM_HW_PARAM_PERIOD_BYTES, - &hw_constraints_period_bytes); + + snd_rme32_set_buffer_constraint(rme32, runtime); rme32->wcreg_spdif_stream = rme32->wcreg_spdif; rme32->spdif_ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE; @@ -916,7 +942,10 @@ rme32->capture_substream = substream; spin_unlock_irqrestore(&rme32->lock, flags); - runtime->hw = snd_rme32_capture_spdif_info; + if (rme32->fullduplex_mode) + runtime->hw = snd_rme32_spdif_fd_info; + else + runtime->hw = snd_rme32_spdif_info; if (RME32_PRO_WITH_8414(rme32)) { runtime->hw.rates |= SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000; runtime->hw.rate_max = 96000; @@ -930,12 +959,7 @@ runtime->hw.rate_max = rate; } - snd_pcm_hw_constraint_minmax(runtime, - SNDRV_PCM_HW_PARAM_BUFFER_BYTES, - RME32_BUFFER_SIZE, RME32_BUFFER_SIZE); - snd_pcm_hw_constraint_list(runtime, 0, - SNDRV_PCM_HW_PARAM_PERIOD_BYTES, - &hw_constraints_period_bytes); + snd_rme32_set_buffer_constraint(rme32, runtime); return 0; } @@ -960,7 +984,10 @@ rme32->playback_substream = substream; spin_unlock_irqrestore(&rme32->lock, flags); - runtime->hw = snd_rme32_playback_adat_info; + if (rme32->fullduplex_mode) + runtime->hw = snd_rme32_adat_fd_info; + else + runtime->hw = snd_rme32_adat_info; if ((rme32->rcreg & RME32_RCR_KMODE) && (rate = snd_rme32_capture_getrate(rme32, &dummy)) > 0) { /* AutoSync */ @@ -968,10 +995,8 @@ runtime->hw.rate_min = rate; runtime->hw.rate_max = rate; } - snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, - RME32_BUFFER_SIZE, RME32_BUFFER_SIZE); - snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, - &hw_constraints_period_bytes); + + snd_rme32_set_buffer_constraint(rme32, runtime); return 0; } @@ -983,7 +1008,10 @@ rme32_t *rme32 = _snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; - runtime->hw = snd_rme32_capture_adat_info; + if (rme32->fullduplex_mode) + runtime->hw = snd_rme32_adat_fd_info; + else + runtime->hw = snd_rme32_adat_info; if ((rate = snd_rme32_capture_getrate(rme32, &isadat)) > 0) { if (!isadat) { return -EIO; @@ -1003,10 +1031,7 @@ rme32->capture_substream = substream; spin_unlock_irqrestore(&rme32->lock, flags); - snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, - RME32_BUFFER_SIZE, RME32_BUFFER_SIZE); - snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, - &hw_constraints_period_bytes); + snd_rme32_set_buffer_constraint(rme32, runtime); return 0; } @@ -1045,139 +1070,174 @@ static int snd_rme32_playback_prepare(snd_pcm_substream_t * substream) { rme32_t *rme32 = _snd_pcm_substream_chip(substream); - unsigned long flags; - spin_lock_irqsave(&rme32->lock, flags); - if (RME32_ISWORKING(rme32)) { - snd_rme32_playback_stop(rme32); + spin_lock(&rme32->lock); + if (rme32->fullduplex_mode) { + memset(&rme32->playback_pcm, 0, sizeof(rme32->playback_pcm)); + rme32->playback_pcm.hw_buffer_size = RME32_BUFFER_SIZE; + rme32->playback_pcm.hw_queue_size = RME32_BUFFER_SIZE / 2; + rme32->playback_pcm.sw_buffer_size = snd_pcm_lib_buffer_bytes(substream); + } else { + writel(0, rme32->iobase + RME32_IO_RESET_POS); } - writel(0, rme32->iobase + RME32_IO_RESET_POS); if (rme32->wcreg & RME32_WCR_SEL) rme32->wcreg &= ~RME32_WCR_MUTE; writel(rme32->wcreg, rme32->iobase + RME32_IO_CONTROL_REGISTER); - spin_unlock_irqrestore(&rme32->lock, flags); + spin_unlock(&rme32->lock); return 0; } static int snd_rme32_capture_prepare(snd_pcm_substream_t * substream) { rme32_t *rme32 = _snd_pcm_substream_chip(substream); - unsigned long flags; - spin_lock_irqsave(&rme32->lock, flags); - if (RME32_ISWORKING(rme32)) { - snd_rme32_capture_stop(rme32); + spin_lock(&rme32->lock); + if (rme32->fullduplex_mode) { + memset(&rme32->capture_pcm, 0, sizeof(rme32->capture_pcm)); + rme32->capture_pcm.hw_buffer_size = RME32_BUFFER_SIZE; + rme32->capture_pcm.hw_queue_size = RME32_BUFFER_SIZE / 2; + rme32->capture_pcm.sw_buffer_size = snd_pcm_lib_buffer_bytes(substream); + } else { + writel(0, rme32->iobase + RME32_IO_RESET_POS); } - writel(0, rme32->iobase + RME32_IO_RESET_POS); - spin_unlock_irqrestore(&rme32->lock, flags); + spin_unlock(&rme32->lock); return 0; } static int -snd_rme32_playback_trigger(snd_pcm_substream_t * substream, int cmd) +snd_rme32_pcm_trigger(snd_pcm_substream_t * substream, int cmd) { rme32_t *rme32 = _snd_pcm_substream_chip(substream); - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - if (!RME32_ISWORKING(rme32)) { - if (substream != rme32->playback_substream) { - return -EBUSY; + struct list_head *pos; + snd_pcm_substream_t *s; + + spin_lock(&rme32->lock); + snd_pcm_group_for_each(pos, substream) { + s = snd_pcm_group_substream_entry(pos); + if (s != rme32->playback_substream && + s != rme32->capture_substream) + continue; + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + rme32->running++; + if (rme32->fullduplex_mode) { + /* remember the current DMA position */ + if (s == rme32->playback_substream) { + rme32->playback_pcm.hw_data = snd_rme32_pcm_byteptr(rme32); + s->ops->ack(s); /* prefill buffer */ + } else { + rme32->capture_pcm.hw_data = snd_rme32_pcm_byteptr(rme32); + } } - snd_rme32_playback_start(rme32, 0); + break; + case SNDRV_PCM_TRIGGER_STOP: + rme32->running--; + break; } + snd_pcm_trigger_done(s, substream); + } + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + if (rme32->running && ! RME32_ISWORKING(rme32)) + snd_rme32_pcm_start(rme32, 0); break; - case SNDRV_PCM_TRIGGER_STOP: - if (RME32_ISWORKING(rme32)) { - if (substream != rme32->playback_substream) { - return -EBUSY; - } - snd_rme32_playback_stop(rme32); - } + if (! rme32->running && RME32_ISWORKING(rme32)) + snd_rme32_pcm_stop(rme32); break; - case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - if (RME32_ISWORKING(rme32)) { - snd_rme32_playback_stop(rme32); - } + if (rme32->running && RME32_ISWORKING(rme32)) + snd_rme32_pcm_stop(rme32); break; - case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - if (!RME32_ISWORKING(rme32)) { - snd_rme32_playback_start(rme32, 1); - } + if (rme32->running && ! RME32_ISWORKING(rme32)) + snd_rme32_pcm_start(rme32, 1); break; - - default: - return -EINVAL; } + spin_unlock(&rme32->lock); return 0; } -static int -snd_rme32_capture_trigger(snd_pcm_substream_t * substream, int cmd) +/* pointer callback for halfduplex mode */ +static snd_pcm_uframes_t +snd_rme32_playback_pointer(snd_pcm_substream_t * substream) { rme32_t *rme32 = _snd_pcm_substream_chip(substream); + return snd_rme32_pcm_byteptr(rme32) >> rme32->playback_frlog; +} - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - if (!RME32_ISWORKING(rme32)) { - if (substream != rme32->capture_substream) { - return -EBUSY; - } - snd_rme32_capture_start(rme32, 0); - } - break; +static snd_pcm_uframes_t +snd_rme32_capture_pointer(snd_pcm_substream_t * substream) +{ + rme32_t *rme32 = _snd_pcm_substream_chip(substream); + return snd_rme32_pcm_byteptr(rme32) >> rme32->capture_frlog; +} - case SNDRV_PCM_TRIGGER_STOP: - if (RME32_ISWORKING(rme32)) { - if (substream != rme32->capture_substream) { - return -EBUSY; - } - snd_rme32_capture_stop(rme32); - } - break; - case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - if (RME32_ISWORKING(rme32)) { - snd_rme32_capture_stop(rme32); - } - break; +/* ack and pointer callbacks for fullduplex mode */ +static void snd_rme32_pb_trans_copy(snd_pcm_substream_t *substream, + snd_pcm_indirect_t *rec, size_t bytes) +{ + rme32_t *rme32 = _snd_pcm_substream_chip(substream); + memcpy_toio(rme32->iobase + RME32_IO_DATA_BUFFER + rec->hw_data, + substream->runtime->dma_area + rec->sw_data, bytes); +} - case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - if (!RME32_ISWORKING(rme32)) { - snd_rme32_capture_start(rme32, 1); - } - break; +static int snd_rme32_playback_fd_ack(snd_pcm_substream_t *substream) +{ + rme32_t *rme32 = _snd_pcm_substream_chip(substream); + snd_pcm_indirect_playback_transfer(substream, &rme32->playback_pcm, + snd_rme32_pb_trans_copy); + return 0; +} - default: - return -EINVAL; - } +static void snd_rme32_cp_trans_copy(snd_pcm_substream_t *substream, + snd_pcm_indirect_t *rec, size_t bytes) +{ + rme32_t *rme32 = _snd_pcm_substream_chip(substream); + memcpy_fromio(substream->runtime->dma_area + rec->sw_data, + rme32->iobase + RME32_IO_DATA_BUFFER + rec->hw_data, + bytes); +} +static int snd_rme32_capture_fd_ack(snd_pcm_substream_t *substream) +{ + rme32_t *rme32 = _snd_pcm_substream_chip(substream); + snd_pcm_indirect_capture_transfer(substream, &rme32->capture_pcm, + snd_rme32_cp_trans_copy); return 0; } static snd_pcm_uframes_t -snd_rme32_playback_pointer(snd_pcm_substream_t * substream) +snd_rme32_playback_fd_pointer(snd_pcm_substream_t * substream) { rme32_t *rme32 = _snd_pcm_substream_chip(substream); - return snd_rme32_playback_ptr(rme32); + size_t ptr; + + ptr = readl(rme32->iobase + RME32_IO_GET_POS) & RME32_RCR_AUDIO_ADDR_MASK; + return snd_pcm_indirect_playback_pointer(substream, &rme32->playback_pcm, ptr); } static snd_pcm_uframes_t -snd_rme32_capture_pointer(snd_pcm_substream_t * substream) +snd_rme32_capture_fd_pointer(snd_pcm_substream_t * substream) { rme32_t *rme32 = _snd_pcm_substream_chip(substream); - return snd_rme32_capture_ptr(rme32); + size_t ptr; + + ptr = readl(rme32->iobase + RME32_IO_GET_POS) & RME32_RCR_AUDIO_ADDR_MASK; + return snd_pcm_indirect_capture_pointer(substream, &rme32->playback_pcm, ptr); } +/* for halfduplex mode */ static snd_pcm_ops_t snd_rme32_playback_spdif_ops = { .open = snd_rme32_playback_spdif_open, .close = snd_rme32_playback_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = snd_rme32_playback_hw_params, + .hw_free = snd_rme32_pcm_hw_free, .prepare = snd_rme32_playback_prepare, - .trigger = snd_rme32_playback_trigger, + .trigger = snd_rme32_pcm_trigger, .pointer = snd_rme32_playback_pointer, .copy = snd_rme32_playback_copy, .silence = snd_rme32_playback_silence, @@ -1188,8 +1248,9 @@ .close = snd_rme32_capture_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = snd_rme32_capture_hw_params, + .hw_free = snd_rme32_pcm_hw_free, .prepare = snd_rme32_capture_prepare, - .trigger = snd_rme32_capture_trigger, + .trigger = snd_rme32_pcm_trigger, .pointer = snd_rme32_capture_pointer, .copy = snd_rme32_capture_copy, }; @@ -1200,7 +1261,7 @@ .ioctl = snd_pcm_lib_ioctl, .hw_params = snd_rme32_playback_hw_params, .prepare = snd_rme32_playback_prepare, - .trigger = snd_rme32_playback_trigger, + .trigger = snd_rme32_pcm_trigger, .pointer = snd_rme32_playback_pointer, .copy = snd_rme32_playback_copy, .silence = snd_rme32_playback_silence, @@ -1212,11 +1273,58 @@ .ioctl = snd_pcm_lib_ioctl, .hw_params = snd_rme32_capture_hw_params, .prepare = snd_rme32_capture_prepare, - .trigger = snd_rme32_capture_trigger, + .trigger = snd_rme32_pcm_trigger, .pointer = snd_rme32_capture_pointer, .copy = snd_rme32_capture_copy, }; +/* for fullduplex mode */ +static snd_pcm_ops_t snd_rme32_playback_spdif_fd_ops = { + .open = snd_rme32_playback_spdif_open, + .close = snd_rme32_playback_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_rme32_playback_hw_params, + .hw_free = snd_rme32_pcm_hw_free, + .prepare = snd_rme32_playback_prepare, + .trigger = snd_rme32_pcm_trigger, + .pointer = snd_rme32_playback_fd_pointer, + .ack = snd_rme32_playback_fd_ack, +}; + +static snd_pcm_ops_t snd_rme32_capture_spdif_fd_ops = { + .open = snd_rme32_capture_spdif_open, + .close = snd_rme32_capture_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_rme32_capture_hw_params, + .hw_free = snd_rme32_pcm_hw_free, + .prepare = snd_rme32_capture_prepare, + .trigger = snd_rme32_pcm_trigger, + .pointer = snd_rme32_capture_fd_pointer, + .ack = snd_rme32_capture_fd_ack, +}; + +static snd_pcm_ops_t snd_rme32_playback_adat_fd_ops = { + .open = snd_rme32_playback_adat_open, + .close = snd_rme32_playback_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_rme32_playback_hw_params, + .prepare = snd_rme32_playback_prepare, + .trigger = snd_rme32_pcm_trigger, + .pointer = snd_rme32_playback_fd_pointer, + .ack = snd_rme32_capture_fd_ack, +}; + +static snd_pcm_ops_t snd_rme32_capture_adat_fd_ops = { + .open = snd_rme32_capture_adat_open, + .close = snd_rme32_capture_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_rme32_capture_hw_params, + .prepare = snd_rme32_capture_prepare, + .trigger = snd_rme32_pcm_trigger, + .pointer = snd_rme32_capture_fd_pointer, + .ack = snd_rme32_capture_fd_ack, +}; + static void snd_rme32_free(void *private_data) { rme32_t *rme32 = (rme32_t *) private_data; @@ -1225,8 +1333,7 @@ return; } if (rme32->irq >= 0) { - snd_rme32_playback_stop(rme32); - snd_rme32_capture_stop(rme32); + snd_rme32_pcm_stop(rme32); free_irq(rme32->irq, (void *) rme32); rme32->irq = -1; } @@ -1294,12 +1401,22 @@ rme32->spdif_pcm->private_data = rme32; rme32->spdif_pcm->private_free = snd_rme32_free_spdif_pcm; strcpy(rme32->spdif_pcm->name, "Digi32 IEC958"); - snd_pcm_set_ops(rme32->spdif_pcm, SNDRV_PCM_STREAM_PLAYBACK, - &snd_rme32_playback_spdif_ops); - snd_pcm_set_ops(rme32->spdif_pcm, SNDRV_PCM_STREAM_CAPTURE, - &snd_rme32_capture_spdif_ops); - - rme32->spdif_pcm->info_flags = SNDRV_PCM_INFO_HALF_DUPLEX; + if (rme32->fullduplex_mode) { + snd_pcm_set_ops(rme32->spdif_pcm, SNDRV_PCM_STREAM_PLAYBACK, + &snd_rme32_playback_spdif_fd_ops); + snd_pcm_set_ops(rme32->spdif_pcm, SNDRV_PCM_STREAM_CAPTURE, + &snd_rme32_capture_spdif_fd_ops); + snd_pcm_lib_preallocate_pages_for_all(rme32->spdif_pcm, SNDRV_DMA_TYPE_CONTINUOUS, + snd_dma_continuous_data(GFP_KERNEL), + 0, RME32_MID_BUFFER_SIZE); + rme32->spdif_pcm->info_flags = SNDRV_PCM_INFO_JOINT_DUPLEX; + } else { + snd_pcm_set_ops(rme32->spdif_pcm, SNDRV_PCM_STREAM_PLAYBACK, + &snd_rme32_playback_spdif_ops); + snd_pcm_set_ops(rme32->spdif_pcm, SNDRV_PCM_STREAM_CAPTURE, + &snd_rme32_capture_spdif_ops); + rme32->spdif_pcm->info_flags = SNDRV_PCM_INFO_HALF_DUPLEX; + } /* set up ALSA pcm device for ADAT */ if ((pci->device == PCI_DEVICE_ID_DIGI32) || @@ -1316,12 +1433,22 @@ rme32->adat_pcm->private_data = rme32; rme32->adat_pcm->private_free = snd_rme32_free_adat_pcm; strcpy(rme32->adat_pcm->name, "Digi32 ADAT"); - snd_pcm_set_ops(rme32->adat_pcm, SNDRV_PCM_STREAM_PLAYBACK, - &snd_rme32_playback_adat_ops); - snd_pcm_set_ops(rme32->adat_pcm, SNDRV_PCM_STREAM_CAPTURE, - &snd_rme32_capture_adat_ops); - - rme32->adat_pcm->info_flags = SNDRV_PCM_INFO_HALF_DUPLEX; + if (rme32->fullduplex_mode) { + snd_pcm_set_ops(rme32->adat_pcm, SNDRV_PCM_STREAM_PLAYBACK, + &snd_rme32_playback_adat_fd_ops); + snd_pcm_set_ops(rme32->adat_pcm, SNDRV_PCM_STREAM_CAPTURE, + &snd_rme32_capture_adat_fd_ops); + snd_pcm_lib_preallocate_pages_for_all(rme32->adat_pcm, SNDRV_DMA_TYPE_CONTINUOUS, + snd_dma_continuous_data(GFP_KERNEL), + 0, RME32_MID_BUFFER_SIZE); + rme32->adat_pcm->info_flags = SNDRV_PCM_INFO_JOINT_DUPLEX; + } else { + snd_pcm_set_ops(rme32->adat_pcm, SNDRV_PCM_STREAM_PLAYBACK, + &snd_rme32_playback_adat_ops); + snd_pcm_set_ops(rme32->adat_pcm, SNDRV_PCM_STREAM_CAPTURE, + &snd_rme32_capture_adat_ops); + rme32->adat_pcm->info_flags = SNDRV_PCM_INFO_HALF_DUPLEX; + } } @@ -1329,8 +1456,7 @@ rme32->capture_periodsize = 0; /* make sure playback/capture is stopped, if by some reason active */ - snd_rme32_playback_stop(rme32); - snd_rme32_capture_stop(rme32); + snd_rme32_pcm_stop(rme32); /* reset DAC */ snd_rme32_reset_dac(rme32); @@ -1375,6 +1501,10 @@ snd_iprintf(buffer, " (index #%d)\n", rme32->card->number + 1); snd_iprintf(buffer, "\nGeneral settings\n"); + if (rme32->fullduplex_mode) + snd_iprintf(buffer, " Full-duplex mode\n"); + else + snd_iprintf(buffer, " Half-duplex mode\n"); if (RME32_PRO_WITH_8414(rme32)) { snd_iprintf(buffer, " receiver: CS8414\n"); } else { @@ -1818,7 +1948,7 @@ int idx, err; snd_kcontrol_t *kctl; - for (idx = 0; idx < 7; idx++) { + for (idx = 0; idx < (int)ARRAY_SIZE(snd_rme32_controls); idx++) { if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_rme32_controls[idx], rme32))) < 0) return err; if (idx == 1) /* IEC958 (S/PDIF) Stream */ Index: alsa-kernel/include/pcm-indirect.h =================================================================== RCS file: alsa-kernel/include/pcm-indirect.h diff -N alsa-kernel/include/pcm-indirect.h --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ alsa-kernel/include/pcm-indirect.h 18 Jun 2004 15:11:39 -0000 @@ -0,0 +1,159 @@ +/* + * Helper functions for indirect PCM data transfer + * + * Copyright (c) by Takashi Iwai + * Jaroslav Kysela + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __SOUND_PCM_INDIRECT_H +#define __SOUND_PCM_INDIRECT_H + +#include + +typedef struct sndrv_pcm_indirect { + unsigned int hw_buffer_size; /* Byte size of hardware buffer */ + unsigned int hw_queue_size; /* Max queue size of hw buffer (0 = buffer size) */ + unsigned int hw_data; /* Offset to next dst (or src) in hw ring buffer */ + unsigned int hw_io; /* Ring buffer hw pointer */ + int hw_ready; /* Bytes ready for play (or captured) in hw ring buffer */ + unsigned int sw_buffer_size; /* Byte size of software buffer */ + unsigned int sw_data; /* Offset to next dst (or src) in sw ring buffer */ + unsigned int sw_io; /* Current software pointer in bytes */ + int sw_ready; /* Bytes ready to be transferred to/from hw */ + size_t appl_ptr; /* Last seen appl_ptr */ +} snd_pcm_indirect_t; + +typedef void (*snd_pcm_indirect_copy_t)(snd_pcm_substream_t *substream, + snd_pcm_indirect_t *rec, size_t bytes); + +static inline void +snd_pcm_indirect_playback_transfer(snd_pcm_substream_t *substream, + snd_pcm_indirect_t *rec, + snd_pcm_indirect_copy_t copy) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + snd_pcm_uframes_t appl_ptr = runtime->control->appl_ptr; + snd_pcm_sframes_t diff = appl_ptr - rec->appl_ptr; + snd_pcm_uframes_t qsize; + + if (diff) { + if (diff < -(snd_pcm_sframes_t) (runtime->boundary / 2)) + diff += runtime->boundary; + rec->sw_ready += frames_to_bytes(runtime, diff); + rec->appl_ptr = appl_ptr; + } + qsize = rec->hw_queue_size ? rec->hw_queue_size : rec->hw_buffer_size; + while (rec->hw_ready < qsize && rec->sw_ready > 0) { + size_t hw_to_end = rec->hw_buffer_size - rec->hw_data; + size_t sw_to_end = rec->sw_buffer_size - rec->sw_data; + size_t bytes = rec->hw_buffer_size - rec->hw_ready; + if (rec->sw_ready < (int)bytes) + bytes = rec->sw_ready; + if (hw_to_end < bytes) + bytes = hw_to_end; + if (sw_to_end < bytes) + bytes = sw_to_end; + if (! bytes) + break; + copy(substream, rec, bytes); + rec->hw_data += bytes; + if ((int)rec->hw_data == rec->hw_buffer_size) + rec->hw_data = 0; + rec->sw_data += bytes; + if (rec->sw_data == rec->sw_buffer_size) + rec->sw_data = 0; + rec->hw_ready += bytes; + rec->sw_ready -= bytes; + } +} + +static inline snd_pcm_uframes_t +snd_pcm_indirect_playback_pointer(snd_pcm_substream_t *substream, + snd_pcm_indirect_t *rec, size_t ptr) +{ + ssize_t bytes = ptr - rec->hw_io; + if (bytes < 0) + bytes += rec->hw_buffer_size; + rec->hw_io = ptr; + rec->hw_ready -= bytes; + rec->sw_io += bytes; + if (rec->sw_io >= rec->sw_buffer_size) + rec->sw_io -= rec->sw_buffer_size; + if (substream->ops->ack) + substream->ops->ack(substream); + return bytes_to_frames(substream->runtime, rec->sw_io); +} + + +static inline void +snd_pcm_indirect_capture_transfer(snd_pcm_substream_t *substream, + snd_pcm_indirect_t *rec, + snd_pcm_indirect_copy_t copy) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + snd_pcm_uframes_t appl_ptr = runtime->control->appl_ptr; + snd_pcm_sframes_t diff = appl_ptr - rec->appl_ptr; + + if (diff) { + if (diff < -(snd_pcm_sframes_t) (runtime->boundary / 2)) + diff += runtime->boundary; + rec->sw_ready -= frames_to_bytes(runtime, diff); + rec->appl_ptr = appl_ptr; + } + while (rec->hw_ready > 0 && + rec->sw_ready < (int)rec->sw_buffer_size) { + size_t hw_to_end = rec->hw_buffer_size - rec->hw_data; + size_t sw_to_end = rec->sw_buffer_size - rec->sw_data; + size_t bytes = rec->sw_buffer_size - rec->sw_ready; + if (rec->hw_ready < (int)bytes) + bytes = rec->hw_ready; + if (hw_to_end < bytes) + bytes = hw_to_end; + if (sw_to_end < bytes) + bytes = sw_to_end; + if (! bytes) + break; + copy(substream, rec, bytes); + rec->hw_data += bytes; + if ((int)rec->hw_data == rec->hw_buffer_size) + rec->hw_data = 0; + rec->sw_data += bytes; + if (rec->sw_data == rec->sw_buffer_size) + rec->sw_data = 0; + rec->hw_ready -= bytes; + rec->sw_ready += bytes; + } +} + +static inline snd_pcm_uframes_t +snd_pcm_indirect_capture_pointer(snd_pcm_substream_t *substream, + snd_pcm_indirect_t *rec, size_t ptr) +{ + ssize_t bytes = ptr - rec->hw_io; + if (bytes < 0) + bytes += rec->hw_buffer_size; + rec->hw_io = ptr; + rec->hw_ready += bytes; + rec->sw_io += bytes; + if (rec->sw_io >= rec->sw_buffer_size) + rec->sw_io -= rec->sw_buffer_size; + if (substream->ops->ack) + substream->ops->ack(substream); + return bytes_to_frames(substream->runtime, rec->sw_io); +} + +#endif /* __SOUND_PCM_INDIRECT_H */ --Multipart_Fri_Jun_18_17:17:04_2004-1-- ------------------------------------------------------- This SF.Net email is sponsored by The 2004 JavaOne(SM) Conference Learn from the experts at JavaOne(SM), Sun's Worldwide Java Developer Conference, June 28 - July 1 at the Moscone Center in San Francisco, CA REGISTER AND SAVE! http://java.sun.com/javaone/sf Priority Code NWMGYKND