Re: Driver design question

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



At Fri, 15 Sep 2006 10:47:59 -0400,
Lee Revell wrote:
> 
> On Thu, 2006-09-14 at 12:29 +0100, Takashi Iwai wrote:
> > >>> Lee Revell <rlrevell@xxxxxxxxxxx> 09/12/06 10:27 PM >>>
> > > I have a device here that can play PCM, but it's a weird implementation.
> > > Only 2 periods of 0x2000 words per buffer are supported, and there is no
> > > DMA - the driver must poll a status register, and when the chip is
> > > ready, deliver all 0x2000 words using outw() then send an end xfer
> > > command.
> > > 
> > > I think I need to use an intermediate buffer, and implement copy/silence
> > > callbacks that write to this.  Then I plan to use a tasklet or workqueue
> > > to do the actual xfer of PCM data to the hardware.  A timer callback
> > > will periodically check whether at least 0x2000 words are in the buffer
> > > and if so, schedule the tasklet to drain it.
> > 
> > You can implement it in two ways, at least.
> > 
> > One is to use copy and silent callbacks.  This is pretty straightforward,
> > doesn't need extra buffer, but it cannot use the native mmap.  (alsa-lib
> > provides the mmap emulation mode, but it seems not so stable.)
> > Also, the buffer and period sizes are very restricitve (in your case, 0x2000
> > x 2 words), so many apps might not work well.
> > An exampleof this type is isa/sb/emu8000_pcm.c (and some of gus pcm,
> > IIRC).
> > 
> > Another is to use an intermediate buffer as you suggsted.
> > In this case, you do _not_ need copy and silent callbacks.  These callbacks
> > are the operations to copy and silent the hardware buffers.  WIth an
> > intermediate buffer, the copy and silent are done on this buffer, i.e. on
> > normal RAM.  What you need instead is the background-running copy
> > operation from this intermediate buffer to the hardware via outw.
> > 
> > This copy task could be done in the interrupt handler (or timer in your case)
> > if it's minimum.  If not, it'd be better to take a workq since it can sleep and
> > much preemptive.
> 
> Thanks very much for the information.
> 
> So if I use an intermediate buffer but no copy and silence callbacks, I
> must copy the data to the hardware in a workqueue (I have no interrupt
> ability and the copy to hardware must be able to sleep).  Where do I
> copy the data from, and how do I know when 0x2000 words are available?

The timing must be triggered via a ceratin irq.  In your case, you'd
need to take a timer as the irq source, I guess.

> By directly accessing runtime->dma_area and the software pointer?  IOW
> how do I get the information that would be passed to the copy callback?

You can allocate runtime->dma_area via normal memory by calling
preallocator with SNDRV_DMA_TYPE_CONTINUOUS, and the rest is just like
other drivers.  In this case, runtime->dma_area points the
intermediate buffer.  Alternatively, you can use vmalloc() for
allocating buffers, as used in usbaudio.c.

The workq task is a pseudo-DMA engine that runs in background.
When it's woken up, it feeds data from intermediate buffer to hardware
as much as possible.  The available data can be found in

If all data are fed, it sleeps again (or exit
the task).  The timer wakes up or schedule the new workq task.


Maybe you can utilize pcm-indirect.h for a framework.
It contains the basic stuff for handling the intermediate and hardware
buffers.  You'll need to define the following:

1. ack pcm op, which invokes the transfer function
2. set up struct snd_pcm_indirect fields in prepare callback
3. call transfer in the trigger-start

The implementation for 1 depends on how long it takes to copy the data
chunk.  If writel can be done relatively fast, it's OK to do it in the
irq context.  Then, call snd_pcm_indirect_playback_transfer() in ack
callback with own copy function.  The copy function does the data
transfer for a given amount of data.

If the transfer would take time, you'll need a workq, as mentioned.
In such a case, ack callback would simply do wakeup() or
schedule_work() for the workq task that calls
snd_pcm_indirect_playback_transfer().

For 2, setup of snd_pcm_direct, you'll need at least to define
hw_buffer_size and sw_buffer_size fields (in bytes unit).  Other
fields can be zero.  

Then, call the same transfer function as ack calls in the
trigger-start.  This will fill the h/w buffer before actually starting
the stream.

The pointer callback requires the calculation of the current h/w
position.  This is translated to the intermediate buffer position by
calling snd_pcm_indirect_playback_pointer().  This helper also calls
data-transfer (ack op) appropriately to fill the empty h/w buffer
space.

The example is found in emu10k1/emupcm.c.


Takashi

-------------------------------------------------------------------------
Take Surveys. Earn Cash. Influence the Future of IT
Join SourceForge.net's Techsay panel and you'll get the chance to share your
opinions on IT & business topics through brief surveys -- and earn cash
http://www.techsay.com/default.php?page=join.php&p=sourceforge&CID=DEVDEV
_______________________________________________
Alsa-devel mailing list
Alsa-devel@xxxxxxxxxxxxxxxxxxxxx
https://lists.sourceforge.net/lists/listinfo/alsa-devel

[Index of Archives]     [ALSA User]     [Linux Audio Users]     [Kernel Archive]     [Asterisk PBX]     [Photo Sharing]     [Linux Sound]     [Video 4 Linux]     [Gimp]     [Yosemite News]

  Powered by Linux