[alsa-devel] mapping externally allocated Scatter Gather DMA buffers
Manu Abraham
abraham.manu at gmail.com
Fri Nov 12 10:29:50 CET 2010
On Fri, Nov 12, 2010 at 1:35 PM, Jaroslav Kysela <perex at perex.cz> wrote:
> On Fri, 12 Nov 2010, Manu Abraham wrote:
>
>> On Thu, Nov 11, 2010 at 6:04 PM, Manu Abraham <abraham.manu at gmail.com>
>> wrote:
>>>
>>> On Thu, Nov 11, 2010 at 4:00 PM, Jaroslav Kysela <perex at perex.cz> wrote:
>>>>
>>>> On Thu, 11 Nov 2010, Manu Abraham wrote:
>>>>
>>>>> On Wed, Nov 10, 2010 at 11:35 PM, Jaroslav Kysela <perex at perex.cz>
>>>>> wrote:
>>>
>>>>>
>>>>> I adapted the whole thing to make the buffersize divided amongst the
>>>>> XS2D_BUFFERS (8):
>>>>
>>>> Probably yes.
>>>>
>>>>> I hope the mapping of the buffers is okay ? It looks thus, now ..
>>>>
>>>> From information you sent me about hw privately, I think that the
>>>> period_bytes must be 4096 or multiple of this value with minumum count
>>>> of
>>>> periods 8 (or multiple of 8). Otherwise you get a non-continuous memory
>>>> area
>>>> (the hw uses only portion of system memory page, thus there'll be gaps).
>>>> The
>>>> problem is that we have MMAP_COMPLEX mode, but no application can handle
>>>> (does not implement) this mmap mode and I'm not sure, if we can even
>>>> describe the DMA buffer size layout for this case for your specific hw.
>>>>
>>>> I would use:
>>>>
>>>> snd_pcm_hw_constraint_step(runtime, 0,
>>>> SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
>>>> 4096);
>>>>
>>>>
>>>> snd_pcm_hw_constraint_step(runtime, 0,
>>>> SNDRV_PCM_HW_PARAM_PERIODS,
>>>> 8);
>>>>
>>>> And define periods_min = 8 (max to multiple 8 - choose your max limit)
>>>> and
>>>> period_bytes_min to 4096 (max to multiple 4096 - choose your max limit).
>>>>
>>>> Note that -EIO means that your driver does not called
>>>> snd_pcm_period_elapsed() and/or the pointer callback returns wrong
>>>> position
>>>> to the audio ring buffer.
>>>
>>>
>>>
>>> Ok, modified it, also added in code to acquire the stream, It's a bit
>>> more complete now.
>>> The crazy part that I do see now, is that I see an inconsistent lock
>>> state with the hda_intel
>>> driver which is the soundcard on my system. But I don't understand why
>>> the locking on it
>>> has to become inconsistent on loading this driver.
>>
>>
>> Ok, I found the reason: I was passing a single page for the
>> page_table, which I guess corrupted the whole stack !!
>>
>> Eventually, I fixed the same. but ran into another issue ? Should
>> trigger not sleep or something that way ?
>> Currently, I see the issue as follows in the log, but if the
>> ops->run() callback is commented out I don't get that
>> weird message/error about lock states.
>>
>> Now, ops->run(), ie xs2dtl_run() is called with other other streams,
>> such as an MPEG stream, where it doesn't
>> show any issues.
>>
>> Any idea, why uncommenting ops->run() produces that message ? Should
>> trigger not sleep ? Confused ...
>
> Trigger must be fast without any sleeping. Only fast spinlocks are allowed.
> If you need sleep, just activate a tasklet and do the start job in the
> tasklet or a thread.
Ok, I have now a tasklet to handle the commands. But still I do have
the same issue.
if i comment out ops->run() things do look sane. But with the tasklet,
also added in,
I can see the same error, but almost immediately, the machine freezes
completely.
I see quite a lot of junk in the syslog, looks like there has been
memory corruption.
Any idea, what could be going wrong ?
static int saa7231_hw_params(struct snd_pcm_substream *pcm, struct
snd_pcm_hw_params *params)
{
struct saa7231_audio *audio = snd_pcm_substream_chip(pcm);
struct saa7231_dev *saa7231 = audio->saa7231;
struct snd_pcm_runtime *rt = pcm->runtime;
struct saa7231_dmabuf *buffer;
struct saa7231_stream *stream;
struct saa7231_dmabuf *dmabuf;
struct page **ptable;
void *mem;
void *dma_area;
int i, j, pages, bytes, periods, bufsiz, idx;
#define MAX_ENTRIES_PER_PAGE (PAGE_SIZE / 8)
#define PAGES_PER_XS2D(__pages) (__pages / XS2D_BUFFERS)
#define BUFSIZE_PER_XS2D(__size) (__size / XS2D_BUFFERS)
dprintk(SAA7231_DEBUG, 1, "DEBUG: ()");
periods = params_periods(params);
bytes = params_period_bytes(params);
bufsiz = params_buffer_bytes(params);
pages = snd_sgbuf_aligned_pages(bufsiz);
audio->bufsize = bufsiz;
dprintk(SAA7231_DEBUG, 1, "bufsiz=%d periods=%d bytes=%d pages=%d",
bufsiz,
periods,
bytes,
pages);
/* enable stream */
/* initializing 8 buffer with "pages" pages each .. */
stream = saa7231_stream_init(saa7231, AUDIO_CAPTURE, ADAPTER_INT, 0,
(pages / XS2D_BUFFERS));
if (!stream) {
dprintk(SAA7231_ERROR, 1, "ERROR: Registering stream");
return -ENOMEM;
}
audio->stream = stream;
buffer = stream->dmabuf;
saa7231_add_irqevent(saa7231, 43, SAA7231_EDGE_RISING,
saa7231_audio_evhandler, "AS2D_AVIS");
dprintk(SAA7231_DEBUG, 1, "Mapping %d buffers with %d pages each",
XS2D_BUFFERS, PAGES_PER_XS2D(pages));
dprintk(SAA7231_DEBUG, 1, "Page Table array size=%d", pages);
ptable = kzalloc((sizeof (struct page) * pages), GFP_KERNEL);
if (!ptable) {
dprintk(SAA7231_ERROR, 1, "ERROR: No memory to allocate virtual map");
return -ENOMEM;
}
audio->ptable = ptable;
idx = 0;
for (i = 0; i < XS2D_BUFFERS; i ++) {
dmabuf = &buffer[i];
mem = dmabuf->vmalloc;
for (j = 0; j < PAGES_PER_XS2D(pages); j++) {
BUG_ON(idx > pages);
dprintk(SAA7231_DEBUG, 1, "Mapping Page:%d from XS2D_BUFFER:%d to
PTA Offset:%d", j, i, idx);
ptable[idx] = virt_to_page(mem);
mem += PAGE_SIZE;
idx += 1;
}
}
dma_area = vmap(ptable, pages, VM_MAP, PAGE_KERNEL);
rt->dma_area = dma_area;
rt->dma_bytes = pages * PAGE_SIZE;
rt->dma_addr = 0;
return 0;
}
static void saa7231_cmd_tasklet(unsigned long data)
{
struct saa7231_audio *audio = (struct saa7231_audio *) data;
struct saa7231_dev *saa7231 = audio->saa7231;
struct saa7231_stream *stream = audio->stream;
struct stream_ops *ops = &stream->ops;
int cmd = audio->cmd;
int err = 0;
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
dprintk(SAA7231_DEBUG, 1, "Trying to START stream, cmd=%d", cmd);
if (ops->run) {
err = ops->run(stream);
if (err) {
dprintk(SAA7231_ERROR, 1, "ERROR: starting stream, err=%d", err);
// return -EIO;
}
}
break;
case SNDRV_PCM_TRIGGER_STOP:
dprintk(SAA7231_DEBUG, 1, "Trying to STOP stream, cmd=%d", cmd);
if (ops->stop) {
err = ops->stop(stream);
if (err) {
dprintk(SAA7231_ERROR, 1, "ERROR: stopping stream, err=%d", err);
// return -EIO;
}
}
break;
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
case SNDRV_PCM_TRIGGER_SUSPEND:
dprintk(SAA7231_DEBUG, 1, "Trying to PAUSE stream, cmd=%d", cmd);
if (ops->pause) {
err = ops->pause(stream);
if (err) {
dprintk(SAA7231_ERROR, 1, "ERROR: pausing stream, err=%d", err);
// return -EIO;
}
}
break;
default:
dprintk(SAA7231_DEBUG, 1, "Unknown command, cmd=%d", cmd);
snd_BUG();
// return -EINVAL;
}
}
/*
* saa7231_capture_trigger()
*
* This callback is called when the PCM is started, stoppped or paused.
*/
static int saa7231_capture_trigger(struct snd_pcm_substream *pcm, int cmd)
{
struct saa7231_audio *audio = snd_pcm_substream_chip(pcm);
struct saa7231_dev *saa7231 = audio->saa7231;
dprintk(SAA7231_DEBUG, 1, "Scheduling cmd tasklet with cmd=%d", cmd);
audio->cmd = cmd;
tasklet_schedule(&audio->cmd_tasklet);
return 0;
}
static snd_pcm_uframes_t saa7231_capture_pointer(struct snd_pcm_substream *pcm)
{
struct saa7231_audio *audio = snd_pcm_substream_chip(pcm);
struct saa7231_dev *saa7231 = audio->saa7231;
dprintk(SAA7231_DEBUG, 1, "DEBUG:()");
return 0; //bytes_to_frames(rt, played);
}
int saa7231_alsa_init(struct saa7231_dev *saa7231)
{
int err;
struct snd_card *card;
struct saa7231_audio *audio;
struct saa7231_config *config = saa7231->config;
struct card_desc *desc = config->desc;
dprintk(SAA7231_DEBUG, 1, "Initializing Audio ..");
audio = kzalloc(sizeof (struct saa7231_audio), GFP_KERNEL);
if (!audio) {
dprintk(SAA7231_ERROR, 1, "ERROR: Out of memory, audio=NULL");
return -ENOMEM;
}
err = snd_card_create(index[saa7231->num], id[saa7231->num],
THIS_MODULE, 0, &card);
if (err < 0) {
dprintk(SAA7231_ERROR, 1, "snd_card_create() failed, err=%d", err);
return err;
}
saa7231->audio = audio;
audio->saa7231 = saa7231;
audio->card = card;
err = snd_saa7231_pcm_init(audio);
if (err) {
dprintk(SAA7231_ERROR, 1, "PCM Initialization failed, err=%d", err);
return err;
}
err = snd_saa7231_mixer_init(audio);
if (err) {
dprintk(SAA7231_ERROR, 1, "Mixer initialization failed, err=%d", err);
return err;
}
snd_card_set_dev(card, &saa7231->pdev->dev);
strcpy(card->driver, "SAA7231");
sprintf(card->shortname, "%s-%d", desc->product, saa7231->num);
sprintf(card->mixername, "%s", saa7231->name);
sprintf(card->longname, "%s %s (%s) @ 0x%p, irq %i",
desc->vendor,
desc->product,
card->shortname,
saa7231->mmio1,
saa7231->pdev->irq);
err = snd_card_register(card);
if (err) {
dprintk(SAA7231_ERROR, 1, "Sound Card registration failed, err=%d", err);
return err;
}
tasklet_init(&audio->cmd_tasklet, saa7231_cmd_tasklet, (unsigned long) audio);
return 0;
}
testbox ~ # cat /var/log/messages
ginInfo-AllowEmptySettings.X-KDE-PluginInfo-Author
2X-KDE-PluginInfo-Category
0X-KDE-PluginInfo-Depends
,X-KDE-PluginInfo-Email
BX-KDE-PluginInfo-EnabledByDefault4X-KDE-PluginInfo-Immutable0X-KDE-PluginInfo-License
*X-KDE-PluginInfo-Name
0X-KDE-PluginInfo-Version
0X-KDE-PluginInfo-Website
X-KDE-PriorityX-KDE-Protocol
X-KDE-Protocols
X-KDE-Read(X-KDE-ResourceFamily
$X-KDE-ResourceType
<X-KDE-SolidBackendInfo-Version
&X-KDE-StartupNotify&X-KDE-SubstituteUID<X-KDE-System-Settings-Category
JX-KDE-System-Settings-Parent-Category
X-KDE-Type
,X-KDE-UA-DYNAMIC-ENTRY▒X-KDE-UA-FULL
▒X-KDE-UA-NAME
X-KDE-UA-SYSNAME
&X-KDE-UA-SYSRELEASE
▒X-KDE-UA-TAG
X-KDE-UA-VERSION
X-KDE-Username
▒X-KDE-Version▒X-KDE-WMClass
▒X-KDE-WeightX-KDE-Write,X-KDE-okularAPIVersion>X-KDE-okularHasInternalSettingsX-KIPI-Id
"X-KIPI-Interfaces
X-KIPI-Mimetypes
$X-KIPI-ReqFeatures
(X-Kate-MajorProfiles
Thanks,
Manu
More information about the Alsa-devel
mailing list