Re: [linuxsh-dev] Re: [Alsa-devel] [PATCH] Add Dreamcast AICA driver to alsa-driver

From: Takashi Iwai
Date: Mon Apr 24 2006 - 13:57:10 EST


At Mon, 24 Apr 2006 18:44:55 +0100,
Adrian McMenamin wrote:
>
> >
> > > +static int stereo_buffer_transfer(struct snd_pcm_substream
> > > + *substream, int buffer_size, int period)
> > > +{
> >
> > I feel this transfer-and-wait could be done more efficiently using
> > workq than doing it in timer callback. The trigger(start) and
> > aica_period_elapsed() calls queue_work() at each time.
> >
> > The most work of spu_begin_dma() should be put in the workq, too.
> >
>
> heh. If you remember, a couple of months ago I had this in a kernel
> thread - ie much the same - and you or Lee said I should absolutely not
> use that mechanism :)

Well, a source code tell you better than hundreds words :)


> > You can write a single function for both mono and two-channel
> > streams.
> >
>
> How can I do that given the dma has to be serialised?

The second dma_xfer() can be in if (channels > 1) block (in addition
to different transfer bytes). Something like below:

static int buffer_transfer(struct snd_pcm_substream*substream,
int buffer_size, int period)
{
int transferred;
int dma_countout;
struct snd_pcm_runtime *runtime;
int period_offset;
long dma_flags;

period_offset = period;
period_offset %= (AICA_PERIOD_NUMBER / runtime->channels);
runtime = substream->runtime;
/* transfer left and then right */
dma_flags = claim_dma_lock();
dma_countout = 0;
dma_xfer(0,
runtime->dma_area + (AICA_PERIOD_SIZE * period_offset),
AICA_CHANNEL0_OFFSET +
(AICA_PERIOD_SIZE * period_offset), buffer_size / 2, 5);
/* wait for completion */
do {
udelay(5);
transferred = get_dma_residue(0);
dma_countout++;
if (dma_countout > 0x10000)
break; /* Approx 1/3 sec timeout in case of hardware failure */
}
while (transferred < buffer_size / 2);
if (runtime->channels > 1) {
dma_xfer(0,
AICA_BUFFER_SIZE / 2 + runtime->dma_area +
(AICA_PERIOD_SIZE * period_offset),
AICA_CHANNEL1_OFFSET +
(AICA_PERIOD_SIZE * period_offset), buffer_size / 2, 5);
/* have to wait again */
dma_countout = 0;
do {
udelay(5);
transferred = get_dma_residue(0);
dma_countout++;
if (dma_countout > 0x10000)
break;
}
while (transferred < buffer_size / 2);
}
release_dma_lock(dma_flags);
return 0;
}


Takashi
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/