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