Unify the playback and capture timer function. A group of linked streams only uses one timer. Cope with linked streams on more than one card (untested) Signed-off-by: Eliot Blennerhassett <eblennerhassett@xxxxxxxxxxxxxxxx>
diff -r 74474638024b pci/asihpi/asihpi.c --- a/pci/asihpi/asihpi.c Mon Aug 06 13:50:08 2007 +0200 +++ b/pci/asihpi/asihpi.c Thu Aug 09 14:27:58 2007 +1200 @@ -95,11 +95,9 @@ static HPI_HSUBSYS *phSubSys; /* handle static HPI_HSUBSYS *phSubSys; /* handle to HPI audio subsystem */ /* defaults */ -#ifndef MAX_BUFFER_SIZE -#define MAX_BUFFER_SIZE (256*1024) -#endif - -#define PRELOAD_PERIODS 2 +#define PERIODS_MIN 2 +#define PERIOD_BYTES_MIN 2304 +#define BUFFER_BYTES_MAX (128*1024) /*#define TIMER_MILLISECONDS 20 #define FORCE_TIMER_JIFFIES ((TIMER_MILLISECONDS * HZ + 999)/1000) @@ -229,12 +227,12 @@ static inline u16 HPI_StreamGetInfoEx(HP { if (HPI_HandleObject(hStream) == HPI_OBJ_OSTREAM) return HPI_OutStreamGetInfoEx(hS, hStream, pwState, + pdwBufferSize, pdwDataInBuffer, + pdwSampleCount, pdwAuxiliaryData); + else + return HPI_InStreamGetInfoEx(hS, hStream, pwState, pdwBufferSize, pdwDataInBuffer, pdwSampleCount, pdwAuxiliaryData); - else - return HPI_InStreamGetInfoEx(hS, hStream, pwState, - pdwBufferSize, pdwDataInBuffer, - pdwSampleCount, pdwAuxiliaryData); } static inline u16 HPI_StreamGroupAdd(HPI_HSUBSYS * hS, HPI_HSTREAM hMaster, @@ -383,7 +381,7 @@ static int snd_card_asihpi_pcm_hw_params "StreamHostBufferAttach succeeded\n"); } else { snd_printd(KERN_INFO - "StreamHostBufferAttach error(%d)\n", err); + "StreamHostBufferAttach error %d\n", err); return -ENOMEM; } @@ -391,10 +389,9 @@ static int snd_card_asihpi_pcm_hw_params &dpcm->hpi_buffer_attached, NULL, NULL, NULL); - snd_printd(KERN_INFO "StreamHostBufferAttach status(%d)\n", + snd_printd(KERN_INFO "StreamHostBufferAttach status 0x%x\n", dpcm->hpi_buffer_attached); } - bytes_per_sec = params_rate(params) * params_channels(params); bytes_per_sec *= snd_pcm_format_width(params_format(params)); bytes_per_sec /= 8; @@ -404,7 +401,7 @@ static int snd_card_asihpi_pcm_hw_params dpcm->bytes_per_sec = bytes_per_sec; dpcm->pcm_size = params_buffer_bytes(params); dpcm->pcm_count = params_period_bytes(params); - snd_printd(KERN_INFO "pcm_size %d, pcm_count %d\n", + snd_printd(KERN_INFO "pcm_size x%x, pcm_count x%x\n", dpcm->pcm_size, dpcm->pcm_count); #ifdef FORCE_TIMER_JIFFIES @@ -455,26 +452,35 @@ static int snd_card_asihpi_trigger(struc switch (cmd) { case SNDRV_PCM_TRIGGER_START: snd_pcm_group_for_each_entry(s, substream) { - struct snd_card_asihpi_pcm *ds; + struct snd_card_asihpi_pcm *ds = + s->runtime->private_data; if (snd_pcm_substream_chip(s) != card) continue; - ds = s->runtime->private_data; - - snd_card_asihpi_pcm_timer_start(s); - if (s->stream == SNDRV_PCM_STREAM_PLAYBACK) { if (card->support_mmap) { - HPI_OutStreamWriteBuf(phSubSys, ds->hStream, &s->runtime->dma_area[0], ds->pcm_size, /* entire buffer preload?? */ - /*ds->pcm_count*PRELOAD_PERIODS, */ + /* How do I know how much valid data is present in buffer? + * Just guessing 2 periods, but if buffer is bigger it may + * contain even more data + */ + unsigned int preload = + ds->pcm_count * 2; + VPRINTK2("Preload x%x\n", preload); + HPI_OutStreamWriteBuf(phSubSys, + ds->hStream, + &s->runtime-> + dma_area[0], + preload, &ds->Format); + ds->pcm_irq_pos = + ds->pcm_irq_pos + preload; } } if (card->support_grouping) { - snd_printd("\tGroup %dstream %d\n", s->stream, - s->number); + VPRINTK1("\tGroup %dstream %d\n", s->stream, + s->number); e = HPI_HandleError(HPI_StreamGroupAdd (phSubSys, dpcm->hStream, ds->hStream)); @@ -487,20 +493,22 @@ static int snd_card_asihpi_trigger(struc } snd_printd("Start\n"); /* start the master stream */ + snd_card_asihpi_pcm_timer_start(substream); HPI_HandleError(HPI_StreamStart(phSubSys, dpcm->hStream)); break; case SNDRV_PCM_TRIGGER_STOP: + snd_card_asihpi_pcm_timer_stop(substream); snd_pcm_group_for_each_entry(s, substream) { if (snd_pcm_substream_chip(s) != card) continue; - snd_card_asihpi_pcm_timer_stop(s); + /*? workaround linked streams don't transition to SETUP 20070706 */ s->runtime->status->state = SNDRV_PCM_STATE_SETUP; if (card->support_grouping) { - snd_printd("\tGroup %dstream %d\n", s->stream, - s->number); + VPRINTK1("\tGroup %dstream %d\n", s->stream, + s->number); snd_pcm_trigger_done(s, substream); } else break; @@ -554,66 +562,154 @@ static void snd_card_asihpi_runtime_free } /***************************** PLAYBACK OPS ****************/ -static void snd_card_asihpi_playback_timer_function(unsigned long data) + +#if 0 +/*algorithm outline + Without linking degenerates to getting single stream pos etc + Without mmap 2nd loop degenerates to snd_pcm_period_elapsed +*/ +/* +buf_pos=get_buf_pos(s); +for_each_linked_stream(s) { + buf_pos=get_buf_pos(s); + min_buf_pos = modulo_min(min_buf_pos, buf_pos, pcm_size) + new_data = min(new_data, calc_new_data(buf_pos,irq_pos) +} +timer.expires = jiffies + predict_next_period_ready(min_buf_pos); +for_each_linked_stream(s) { + s->buf_pos = min_buf_pos; + if (new_data > pcm_count) { + if (mmap) { + irq_pos = (irq_pos + pcm_count) % pcm_size; + if (playback) { + write(pcm_count); + } else { + read(pcm_count); + } + } + snd_pcm_period_elapsed(s); + } +} +*/ +#endif + +/** Minimum of 2 modulo values. Works correctly when the difference between +* the values is less than half the modulus +*/ +static inline unsigned int modulo_min(unsigned int a, unsigned int b, + unsigned long int modulus) +{ + unsigned int result; + if (((a - b) % modulus) < (modulus / 2)) + result = b; + else + result = a; + + return result; +} + +/** Timer function, equivalent to interrupt service routine for cards +*/ +static void snd_card_asihpi_timer_function(unsigned long data) { struct snd_card_asihpi_pcm *dpcm = (struct snd_card_asihpi_pcm *)data; struct snd_card_asihpi *card = snd_pcm_substream_chip(dpcm->substream); - struct snd_pcm_runtime *runtime = dpcm->substream->runtime; - - unsigned int newdata; - unsigned int remdata; - unsigned int writedata; - u16 wState, err; - u32 dwBufferSize; - u32 dwDataToPlay; - u32 dwSamplesPlayed; - u32 dwAux; - unsigned int next_jiffies; - - err = HPI_OutStreamGetInfoEx(phSubSys, dpcm->hStream, &wState, - &dwBufferSize, &dwDataToPlay, - &dwSamplesPlayed, &dwAux); - HPI_HandleError(err); - - VPRINTK1(KERN_DEBUG - "%d state=%d, played=%d, left=%d, aux=%d\n", - dpcm->substream->number, wState, - (int)dwSamplesPlayed, (int)dwDataToPlay, (int)dwAux); - - VPRINTK1(KERN_INFO "PB timer hw_ptr %lu, appl_ptr %lu\n", - runtime->status->hw_ptr, runtime->control->appl_ptr); - - if ((wState == HPI_STATE_DRAINED)) { - snd_printd(KERN_WARNING "OStream %d drained\n", - dpcm->substream->number); - snd_pcm_stop(dpcm->substream, SNDRV_PCM_STATE_XRUN); - } - dpcm->pcm_buf_pos = frames_to_bytes(runtime, dwSamplesPlayed); - newdata = (dpcm->pcm_buf_pos - dpcm->pcm_irq_pos) % dpcm->pcm_size; + struct snd_pcm_runtime *runtime; + struct snd_pcm_substream *s; + unsigned int newdata = 0; + unsigned int buf_pos, min_buf_pos = 0; + unsigned int remdata, xfercount, next_jiffies; + int first = 1; + u16 wState; + u32 dwBufferSize, dwDataAvail, dwSamplesPlayed, dwAux; + + /* check all streams of the group, find minimum newdata and buffer pos */ + snd_pcm_group_for_each_entry(s, dpcm->substream) { + struct snd_card_asihpi_pcm *ds = s->runtime->private_data; + runtime = s->runtime; + + if (snd_pcm_substream_chip(s) != card) + continue; + + HPI_HandleError(HPI_StreamGetInfoEx(phSubSys, + ds->hStream, &wState, + &dwBufferSize, &dwDataAvail, + &dwSamplesPlayed, &dwAux)); + + if (wState == HPI_STATE_DRAINED) { + snd_printd(KERN_WARNING "OStream %d drained\n", + s->number); + snd_pcm_stop(s, SNDRV_PCM_STATE_XRUN); + } + + if (s->stream == SNDRV_PCM_STREAM_PLAYBACK) { + buf_pos = frames_to_bytes(runtime, dwSamplesPlayed); + } else { + buf_pos = dwDataAvail + ds->pcm_irq_pos; + } + + if (first) { /* can't statically init min when wraparound is involved */ + min_buf_pos = buf_pos; + newdata = (buf_pos - ds->pcm_irq_pos) % ds->pcm_size; + first = 0; + } else { + min_buf_pos = + modulo_min(min_buf_pos, buf_pos, UINT_MAX + 1L); + newdata = + min((buf_pos - ds->pcm_irq_pos) % ds->pcm_size, + newdata); + } + + VPRINTK1("PB timer hw_ptr x%04lX, appl_ptr x%04lX ", + (unsigned long)frames_to_bytes(runtime, + runtime->status-> + hw_ptr), + (unsigned long)frames_to_bytes(runtime, + runtime->control-> + appl_ptr)); + VPRINTK1 + ("%d S=%d, irq=%04X, pos=x%04X, left=x%04X, aux=x%04X space=x%04X\n", + s->number, wState, ds->pcm_irq_pos, buf_pos, + (int)dwDataAvail, (int)dwAux, dwBufferSize - dwDataAvail); + } + remdata = newdata % dpcm->pcm_count; - writedata = newdata - remdata; - + xfercount = newdata - remdata; /* a multiple of pcm_count */ next_jiffies = ((dpcm->pcm_count - remdata) * HZ / dpcm->bytes_per_sec) + 1; - next_jiffies = max(next_jiffies, 4U * HZ / 1000U); + next_jiffies = max(next_jiffies, 2U * HZ / 1000U); dpcm->timer.expires = jiffies + next_jiffies; - - if (dpcm->respawn_timer) + VPRINTK1("jif %d buf pos x%04X newdata x%04X\n", next_jiffies, + min_buf_pos, newdata); + + snd_pcm_group_for_each_entry(s, dpcm->substream) { + struct snd_card_asihpi_pcm *ds = s->runtime->private_data; + ds->pcm_buf_pos = min_buf_pos; + + if (xfercount) { + if (card->support_mmap) { + ds->pcm_irq_pos = ds->pcm_irq_pos + xfercount; + if (s->stream == SNDRV_PCM_STREAM_PLAYBACK) { + VPRINTK2("Write OS%d x%04x\n", + s->number, ds->pcm_count); + HPI_HandleError(HPI_OutStreamWriteBuf + (phSubSys, ds->hStream, + NULL, xfercount, + &ds->Format)); + } else { + VPRINTK2("Read IS%d x%04x\n", s->number, + dpcm->pcm_count); + HPI_HandleError(HPI_InStreamReadBuf + (phSubSys, ds->hStream, + NULL, xfercount)); + } + } /* else R/W will be handled by read/write callbacks */ + snd_pcm_period_elapsed(s); + } + } + + if (dpcm->respawn_timer) { add_timer(&dpcm->timer); - VPRINTK1(KERN_DEBUG "%d %d respawn %d\n", newdata, remdata, - next_jiffies); - - /*snd_printd(KERN_INFO "newdata=%d irq=%d buf=%d\n",newdata,dpcm->pcm_irq_pos,dpcm->pcm_buf_pos); */ - if (newdata >= dpcm->pcm_count) { - if (card->support_mmap) { - VPRINTK2("OSW %d\n", writedata); - HPI_HandleError(HPI_OutStreamWriteBuf - (phSubSys, dpcm->hStream, NULL, - writedata, &dpcm->Format)); - } - dpcm->pcm_irq_pos = dpcm->pcm_irq_pos + writedata; - /* snd_printd(KERN_INFO "Period_elapsed irq=%d buf=%d\n",dpcm->pcm_irq_pos,dpcm->pcm_buf_pos); */ - snd_pcm_period_elapsed(dpcm->substream); } } @@ -647,19 +743,22 @@ snd_card_asihpi_playback_pointer(struct u32 dwSamplesPlayed; u16 err; - /* NOTE, can use samples played for playback position here and in timer fn - because it LAGS the actual read pointer, and is a better representation - of actual playout position - */ - err = HPI_OutStreamGetInfoEx(phSubSys, dpcm->hStream, NULL, - NULL, NULL, &dwSamplesPlayed, NULL); - HPI_HandleError(err); - - dpcm->pcm_buf_pos = - (frames_to_bytes(runtime, dwSamplesPlayed)) % dpcm->pcm_size; - - ptr = bytes_to_frames(runtime, dpcm->pcm_buf_pos); - VPRINTK2("Playback ptr %d\n", ptr); + if (!snd_pcm_stream_linked(substream)) { + /* NOTE, can use samples played for playback position here and in timer fn + because it LAGS the actual read pointer, and is a better representation + of actual playout position + */ + err = HPI_OutStreamGetInfoEx(phSubSys, dpcm->hStream, NULL, + NULL, NULL, + &dwSamplesPlayed, NULL); + HPI_HandleError(err); + + dpcm->pcm_buf_pos = frames_to_bytes(runtime, dwSamplesPlayed); + } + /* else must return most conservative value found in timer func looping over all streams */ + + ptr = bytes_to_frames(runtime, dpcm->pcm_buf_pos % dpcm->pcm_size); + VPRINTK2("Playback ptr x%04lx\n", (unsigned long)ptr); return ptr; } @@ -697,11 +796,11 @@ static struct snd_pcm_hardware snd_card_ static struct snd_pcm_hardware snd_card_asihpi_playback = { .channels_min = 1, .channels_max = 2, - .buffer_bytes_max = MAX_BUFFER_SIZE, - .period_bytes_min = 768, - .period_bytes_max = MAX_BUFFER_SIZE / PRELOAD_PERIODS, - .periods_min = PRELOAD_PERIODS, - .periods_max = MAX_BUFFER_SIZE / 768, + .buffer_bytes_max = BUFFER_BYTES_MAX, + .period_bytes_min = PERIOD_BYTES_MIN, + .period_bytes_max = BUFFER_BYTES_MAX / PERIODS_MIN, + .periods_min = PERIODS_MIN, + .periods_max = BUFFER_BYTES_MAX / PERIOD_BYTES_MIN, .fifo_size = 0, }; @@ -734,7 +833,7 @@ static int snd_card_asihpi_playback_open init_timer(&dpcm->timer); dpcm->timer.data = (unsigned long)dpcm; - dpcm->timer.function = snd_card_asihpi_playback_timer_function; + dpcm->timer.function = snd_card_asihpi_timer_function; dpcm->substream = substream; runtime->private_data = dpcm; runtime->private_free = snd_card_asihpi_runtime_free; @@ -747,9 +846,14 @@ static int snd_card_asihpi_playback_open snd_card_asihpi_playback.rate_max = 192000; snd_card_asihpi_playback.info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_PAUSE; + if (card->support_mmap) snd_card_asihpi_playback.info |= SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID; + + if (card->support_grouping) + snd_card_asihpi_playback.info |= SNDRV_PCM_INFO_SYNC_START; + /* struct copy so can create initializer dynamically */ runtime->hw = snd_card_asihpi_playback; /* make buffer size a power of 2. Strictly only necessary for HPI6205 adapters */ @@ -842,111 +946,19 @@ static struct snd_pcm_ops snd_card_asihp }; /***************************** CAPTURE OPS ****************/ -static void snd_card_asihpi_capture_timer_function(unsigned long data) -{ - struct snd_card_asihpi_pcm *dpcm = (struct snd_card_asihpi_pcm *)data; - u16 wState, err; - u32 dwBufferSize, dwDataAvail, dwSamplesPlayed, dwAux; - u32 next_jiffies; - - err = - HPI_InStreamGetInfoEx(phSubSys, dpcm->hStream, &wState, - &dwBufferSize, &dwDataAvail, - &dwSamplesPlayed, &dwAux); - HPI_HandleError(err); - - /* Used by capture_pointer */ - dpcm->pcm_buf_pos = (dpcm->pcm_irq_pos + dwDataAvail) % dpcm->pcm_size; - - VPRINTK1("Capture timer%d %d samples, %d left, pos %d, aux %d\n", - dpcm->substream->number, (int)dwSamplesPlayed, - (int)dwDataAvail, dpcm->pcm_buf_pos, dwAux); - - /* schedule next timer when next complete period estimated to be available */ - next_jiffies = - ((dpcm->pcm_count - - (dwDataAvail % dpcm->pcm_count)) * HZ / dpcm->bytes_per_sec) + 1; - /* minimum 4ms interval */ - next_jiffies = max(next_jiffies, 4U * HZ / 1000U); - VPRINTK2("Cap next jiffies %d\n", next_jiffies); - dpcm->timer.expires = jiffies + next_jiffies; - - if (dpcm->respawn_timer) - add_timer(&dpcm->timer); - - if (dwDataAvail >= dpcm->pcm_count) { - snd_pcm_period_elapsed(dpcm->substream); - } -} - -static void snd_card_asihpi_capture_timer_mmap_function(unsigned long data) -{ - struct snd_card_asihpi_pcm *dpcm = (struct snd_card_asihpi_pcm *)data; - u16 wState; - u32 dwBufferSize, dwDataAvail, dwSamplesPlayed, dwAux; - - HPI_HandleError(HPI_InStreamGetInfoEx(phSubSys, dpcm->hStream, &wState, - &dwBufferSize, &dwDataAvail, - &dwSamplesPlayed, &dwAux)); - - /* If data is left over, make timer expire sooner */ - dpcm->timer.expires = - ((dpcm->pcm_count - - (dwDataAvail % dpcm->pcm_count)) * HZ / dpcm->bytes_per_sec) + 1 + - jiffies; - - if (dpcm->respawn_timer) - add_timer(&dpcm->timer); - - if (dwDataAvail >= dpcm->pcm_count) { - struct snd_pcm_runtime *runtime = dpcm->substream->runtime; - size_t bs, len1, len2; - - bs = frames_to_bytes(runtime, runtime->buffer_size); - len1 = bs - dpcm->pcm_buf_pos; - - if (len1 >= dpcm->pcm_count) { - len1 = dpcm->pcm_count; - len2 = 0; - } else { - len2 = dpcm->pcm_count - len1; - } - - VPRINTK2("ISR %d ", len1); - HPI_HandleError(HPI_InStreamReadBuf(phSubSys, dpcm->hStream, - &runtime->dma_area[dpcm-> - pcm_buf_pos], - len1)); - if (len2) { - - VPRINTK2("ISR %d \n", len2); - HPI_HandleError(HPI_InStreamReadBuf - (phSubSys, dpcm->hStream, - &runtime->dma_area[0], len2)); - } else { - VPRINTK2("\n"); - } - - /* Used by capture_pointer */ - dpcm->pcm_buf_pos = - (dpcm->pcm_buf_pos + dpcm->pcm_count) % dpcm->pcm_size; - snd_pcm_period_elapsed(dpcm->substream); - } -} - static snd_pcm_uframes_t snd_card_asihpi_capture_pointer(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct snd_card_asihpi_pcm *dpcm = runtime->private_data; - VPRINTK3("Capture pointer%d %d\n", substream->number, + VPRINTK3("Capture pointer%d x%04x\n", substream->number, dpcm->pcm_buf_pos); - /* NOTE (unlike playback) it is problematic to use actual 'dwSamplesPlayed + /* NOTE Unlike playback can't use actual dwSamplesPlayed for the capture position, because those samples aren't yet in the local buffer available for reading. */ - return bytes_to_frames(runtime, dpcm->pcm_buf_pos); + return bytes_to_frames(runtime, dpcm->pcm_buf_pos % dpcm->pcm_size); } static int snd_card_asihpi_capture_ioctl(struct snd_pcm_substream *substream, @@ -1000,11 +1012,11 @@ static struct snd_pcm_hardware snd_card_ static struct snd_pcm_hardware snd_card_asihpi_capture = { .channels_min = 1, .channels_max = 2, - .buffer_bytes_max = MAX_BUFFER_SIZE / 2, - .period_bytes_min = 768, - .period_bytes_max = MAX_BUFFER_SIZE / 4, - .periods_min = 2, - .periods_max = MAX_BUFFER_SIZE / (2 * 768), + .buffer_bytes_max = BUFFER_BYTES_MAX, + .period_bytes_min = PERIOD_BYTES_MIN, + .period_bytes_max = BUFFER_BYTES_MAX / PERIODS_MIN, + .periods_min = PERIODS_MIN, + .periods_max = BUFFER_BYTES_MAX / PERIOD_BYTES_MIN, .fifo_size = 0, }; @@ -1034,11 +1046,7 @@ static int snd_card_asihpi_capture_open( init_timer(&dpcm->timer); dpcm->timer.data = (unsigned long)dpcm; - if (card->support_mmap) - dpcm->timer.function = - snd_card_asihpi_capture_timer_mmap_function; - else - dpcm->timer.function = snd_card_asihpi_capture_timer_function; + dpcm->timer.function = snd_card_asihpi_timer_function; dpcm->substream = substream; runtime->private_data = dpcm; runtime->private_free = snd_card_asihpi_runtime_free; @@ -1089,7 +1097,7 @@ static int snd_card_asihpi_capture_copy( runtime->dma_area, dwDataSize)); /* Used by capture_pointer */ - dpcm->pcm_irq_pos = (dpcm->pcm_irq_pos + dwDataSize) % dpcm->pcm_size; + dpcm->pcm_irq_pos = dpcm->pcm_irq_pos + dwDataSize; if (copy_to_user(dst, runtime->dma_area, dwDataSize)) return -EFAULT; @@ -1152,7 +1160,7 @@ static int __devinit snd_card_asihpi_pcm MMAP emulation - WHY NOT? */ snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(asihpi->pci), - 64 * 1024, MAX_BUFFER_SIZE); + 64 * 1024, BUFFER_BYTES_MAX); return 0; }
_______________________________________________ Alsa-devel mailing list Alsa-devel@xxxxxxxxxxxxxxxx http://mailman.alsa-project.org/mailman/listinfo/alsa-devel