[alsa-devel] mapping externally allocated Scatter Gather DMA buffers
Hi,
I have been wondering how to interface a Scatter Gather region as a DMA buffer for an Audio Grabbing device. The device is a NOC type device, similar to a SOC, the audio grabber is a part of it, for realizing the audio capabilities on the device.
The hardware DMA is mostly a Scatter Gather based engine, and it can be described thus, briefly.
struct dmabuf { void *vmalloc; dma_addr_t phys; void *virt; int pages; int offset; struct scatterlist *sg_list; struct list_head list; };
where:
dmabuf->virt = __get_free_page(GFP_KERNEL); dmabuf->phys = dma_map_single(dev, PAGE_SIZE, DMA_FROM_DEVICE); dmabuf->sg_list = kzalloc(sizeof (struct scatterlist) * pages, GFP_KERNEL); sg_init_table(sg_list, pages); dmabuf->vmalloc = vmalloc((pages + 1) * PAGE_SIZE);
In a loop with the pages involved, I do populate the pages into the list;
Now I have a stream engine with the hardware, where 8 such dmabufs are allocated. On an interrupt, the device can know from status registers, which dmabuf the device driver needs to read the data from.
I wonder what would be the best possible way to interface the stream engine to the Alsa DMA capture engine. I have been eyeballing the alsa sources and whatever documents that are available, but found very little details on the alsa core, but nevertheless found that, it might be possible to pass the vmalloc'd region to substream->runtime->dma_area. I wonder whether this is possible practically and would like to hear your comments on it, whatever feedback, or ideas you have.
Any helpful comments appreciated very much.
Thanks, Manu
On Tue, 26 Oct 2010, Manu Abraham wrote:
Hi,
I have been wondering how to interface a Scatter Gather region as a DMA buffer for an Audio Grabbing device. The device is a NOC type device, similar to a SOC, the audio grabber is a part of it, for realizing the audio capabilities on the device.
The hardware DMA is mostly a Scatter Gather based engine, and it can be described thus, briefly.
Use snd_pcm_lib_preallocate_pages_for_all() - type SNDRV_DMA_TYPE_DEV_SG and snd_pcm_sgbuf_* routines. Check hda_intel.c source for more details.
Jaroslav
----- Jaroslav Kysela perex@perex.cz Linux Kernel Sound Maintainer ALSA Project, Red Hat, Inc.
On Tue, Oct 26, 2010 at 12:54 PM, Jaroslav Kysela perex@perex.cz wrote:
On Tue, 26 Oct 2010, Manu Abraham wrote:
Hi,
I have been wondering how to interface a Scatter Gather region as a DMA buffer for an Audio Grabbing device. The device is a NOC type device, similar to a SOC, the audio grabber is a part of it, for realizing the audio capabilities on the device.
The hardware DMA is mostly a Scatter Gather based engine, and it can be described thus, briefly.
Use snd_pcm_lib_preallocate_pages_for_all() - type SNDRV_DMA_TYPE_DEV_SG and snd_pcm_sgbuf_* routines. Check hda_intel.c source for more details
Thanks. I did take a deeper look at it, from a few angles. However still I am not very clear on some aspects, especially due to the multiple SG buffers involved. Let me try to make it a bit more clear ..
1. I have exactly 1 stream to be handled/grabbed
2. This single stream consists of 8 SG page tables (buffers), each buffer can contain multiple frames, but initially to keep code complexity down, I will have only a single frame per buffer.
3. The Pages (PTE's) in each SG buffer are virtually contiguous, ie, say buf_pages are vmalloc'd. But one buffer is discontiguous from the other.
4. On the 1st interrupt, likely I can read from the buffer1, on the next interrupt from buffer2 and so on in a round robin fashion.
5. The SG buffers are allocated much earlier along with hardware initialization, So these SG buffers do exist, and hence cannot be allocated again.
In such a circumstance, I wonder how I can populate runtime->dma_area. Initially I thought that I would simply map the dmabuffer as it is, but since they are 8 in number and discontiguous between the buffers, just mapping the buffers alone wouldn't help. After a bit of thoughts, I came up with a following thought.
A virtual map is created with 8 of these buffers and the base address of this map is provided to runtime->dma_area
I wonder, whether such a concept will work ?
Best Regards, Manu
On Tue, Oct 26, 2010 at 12:54 PM, Jaroslav Kysela perex@perex.cz wrote:
On Tue, 26 Oct 2010, Manu Abraham wrote:
Hi,
I have been wondering how to interface a Scatter Gather region as a DMA buffer for an Audio Grabbing device. The device is a NOC type device, similar to a SOC, the audio grabber is a part of it, for realizing the audio capabilities on the device.
The hardware DMA is mostly a Scatter Gather based engine, and it can be described thus, briefly.
Use snd_pcm_lib_preallocate_pages_for_all() - type SNDRV_DMA_TYPE_DEV_SG and snd_pcm_sgbuf_* routines. Check hda_intel.c source for more details.
I have something like this: http://202.88.242.108:8000/saa7231_audops.c
testbox ~ # arecord -L default:CARD=SB HDA ATI SB, ALC889A Analog Default Audio Device front:CARD=SB,DEV=0 HDA ATI SB, ALC889A Analog Front speakers surround40:CARD=SB,DEV=0 HDA ATI SB, ALC889A Analog 4.0 Surround output to Front and Rear speakers surround41:CARD=SB,DEV=0 HDA ATI SB, ALC889A Analog 4.1 Surround output to Front, Rear and Subwoofer speakers surround50:CARD=SB,DEV=0 HDA ATI SB, ALC889A Analog 5.0 Surround output to Front, Center and Rear speakers surround51:CARD=SB,DEV=0 HDA ATI SB, ALC889A Analog 5.1 Surround output to Front, Center, Rear and Subwoofer speakers surround71:CARD=SB,DEV=0 HDA ATI SB, ALC889A Analog 7.1 Surround output to Front, Center, Side, Rear and Woofer speakers iec958:CARD=SB,DEV=0 HDA ATI SB, ALC889A Digital IEC958 (S/PDIF) Digital Audio Output null Discard all samples (playback) or generate zero samples (capture) default:CARD=BGT35950 BGT3595-0, SAA7231 PCM Default Audio Device
testbox ~ # arecord -Dhw:2,0 Recording WAVE 'stdin' : Unsigned 8 bit, Rate 8000 Hz, Mono arecord: set_params:957: Broken configuration for this PCM: no configurations available
[ 719.292194] saa7231_capture_open (0): () [ 719.292390] saa7231_hw_free (0): DEBUG: Removing IRQ event .. [ 719.292393] saa7231_remove_irqevent (0): Removing IRQ Event:43 ...... [ 719.292395] saa7231_hw_free (0): DEBUG: Stream exiting .. [ 719.292397] saa7231_hw_free (0): DEBUG: Freeing ptable ... [ 719.292398] saa7231_capture_close (0): DEBUG: Closing stream
Any idea, why saa7231_hw_params is not getting invoked ?
Thanks, Manu
On Tue, 9 Nov 2010, Manu Abraham wrote:
testbox ~ # arecord -Dhw:2,0 Recording WAVE 'stdin' : Unsigned 8 bit, Rate 8000 Hz, Mono
Any idea, why saa7231_hw_params is not getting invoked ?
Your hw probably does not support directly 8-bit sample at 8000Hz, one channel. Use 'plughw:2,0' device instead 'hw:2,0' to let alsa-lib do all stream format conversions.
Jaroslav
----- Jaroslav Kysela perex@perex.cz Linux Kernel Sound Maintainer ALSA Project, Red Hat, Inc.
On Tue, Nov 9, 2010 at 5:31 PM, Jaroslav Kysela perex@perex.cz wrote:
On Tue, 9 Nov 2010, Manu Abraham wrote:
testbox ~ # arecord -Dhw:2,0 Recording WAVE 'stdin' : Unsigned 8 bit, Rate 8000 Hz, Mono
Any idea, why saa7231_hw_params is not getting invoked ?
Your hw probably does not support directly 8-bit sample at 8000Hz, one channel. Use 'plughw:2,0' device instead 'hw:2,0' to let alsa-lib do all stream format conversions.
That didn't make any difference at all.
I just tried that, I get something like this :
testbox ~ # arecord -Dplughw:2,0 Recording WAVE 'stdin' : Unsigned 8 bit, Rate 8000 Hz, Mono ALSA lib pcm_params.c:2150:(snd1_pcm_hw_refine_slave) Slave PCM not usable arecord: set_params:957: Broken configuration for this PCM: no configurations available
[ 719.292194] saa7231_capture_open (0): () [ 719.292390] saa7231_hw_free (0): DEBUG: Removing IRQ event .. [ 719.292393] saa7231_remove_irqevent (0): Removing IRQ Event:43 ...... [ 719.292395] saa7231_hw_free (0): DEBUG: Stream exiting .. [ 719.292397] saa7231_hw_free (0): DEBUG: Freeing ptable ... [ 719.292398] saa7231_capture_close (0): DEBUG: Closing stream [ 777.191250] saa7231_capture_open (0): () [ 777.191441] saa7231_hw_free (0): DEBUG: Removing IRQ event .. [ 777.191444] saa7231_remove_irqevent (0): Removing IRQ Event:43 ...... [ 777.191446] saa7231_hw_free (0): DEBUG: Stream exiting .. [ 777.191448] saa7231_hw_free (0): DEBUG: Freeing ptable ... [ 777.191450] saa7231_capture_close (0): DEBUG: Closing stream [ 6928.076343] saa7231_capture_open (0): () [ 6928.076559] saa7231_hw_free (0): DEBUG: Removing IRQ event .. [ 6928.076562] saa7231_remove_irqevent (0): Removing IRQ Event:43 ...... [ 6928.076565] saa7231_hw_free (0): DEBUG: Stream exiting .. [ 6928.076567] saa7231_hw_free (0): DEBUG: Freeing ptable ... [ 6928.076568] saa7231_capture_close (0): DEBUG: Closing stream
Best Regards, Manu
On Tue, 9 Nov 2010, Manu Abraham wrote:
On Tue, Nov 9, 2010 at 5:31 PM, Jaroslav Kysela perex@perex.cz wrote:
On Tue, 9 Nov 2010, Manu Abraham wrote:
testbox ~ # arecord -Dhw:2,0 Recording WAVE 'stdin' : Unsigned 8 bit, Rate 8000 Hz, Mono
Any idea, why saa7231_hw_params is not getting invoked ?
Your hw probably does not support directly 8-bit sample at 8000Hz, one channel. Use 'plughw:2,0' device instead 'hw:2,0' to let alsa-lib do all stream format conversions.
That didn't make any difference at all.
Check your code. You probably created wrong hw_ops, looking to your code:
.buffer_bytes_max = 512 * 4096, .period_bytes_min = 8192, .period_bytes_max = 8192, .periods_min = 8192, .periods_max = 8192,
It's definitely an empty configuration space.
You have buffer_bytes_max 2097152, but restricted number of periods to 8192 and period_bytes to 8192.
8192 * 8192 = 67108864 which is not less than 2097152 (and I'm not counting the bytes for channels and formats). Bingo, the ALSA PCM code does not know what to configure.
Decrease periods_min and probably also periods_max.
Jaroslav
----- Jaroslav Kysela perex@perex.cz
On Tue, Nov 9, 2010 at 6:21 PM, Jaroslav Kysela perex@perex.cz wrote:
On Tue, 9 Nov 2010, Manu Abraham wrote:
On Tue, Nov 9, 2010 at 5:31 PM, Jaroslav Kysela perex@perex.cz wrote:
On Tue, 9 Nov 2010, Manu Abraham wrote:
testbox ~ # arecord -Dhw:2,0 Recording WAVE 'stdin' : Unsigned 8 bit, Rate 8000 Hz, Mono
Any idea, why saa7231_hw_params is not getting invoked ?
Your hw probably does not support directly 8-bit sample at 8000Hz, one channel. Use 'plughw:2,0' device instead 'hw:2,0' to let alsa-lib do all stream format conversions.
That didn't make any difference at all.
Check your code. You probably created wrong hw_ops, looking to your code:
.buffer_bytes_max = 512 * 4096, .period_bytes_min = 8192, .period_bytes_max = 8192, .periods_min = 8192, .periods_max = 8192,
It's definitely an empty configuration space.
You have buffer_bytes_max 2097152, but restricted number of periods to 8192 and period_bytes to 8192.
8192 * 8192 = 67108864 which is not less than 2097152 (and I'm not counting the bytes for channels and formats). Bingo, the ALSA PCM code does not know what to configure.
Decrease periods_min and probably also periods_max.
oh ! The error message was really a weird one. I can say that the hardware can do 192 audio frames in an interrupt, whatever the frame size is. So, what periods and period_bytes should I describe in the configuration ?
Best Regards, Manu
On Tue, 9 Nov 2010, Manu Abraham wrote:
On Tue, Nov 9, 2010 at 6:21 PM, Jaroslav Kysela perex@perex.cz wrote:
On Tue, 9 Nov 2010, Manu Abraham wrote:
On Tue, Nov 9, 2010 at 5:31 PM, Jaroslav Kysela perex@perex.cz wrote:
On Tue, 9 Nov 2010, Manu Abraham wrote:
testbox ~ # arecord -Dhw:2,0 Recording WAVE 'stdin' : Unsigned 8 bit, Rate 8000 Hz, Mono
Any idea, why saa7231_hw_params is not getting invoked ?
Your hw probably does not support directly 8-bit sample at 8000Hz, one channel. Use 'plughw:2,0' device instead 'hw:2,0' to let alsa-lib do all stream format conversions.
That didn't make any difference at all.
Check your code. You probably created wrong hw_ops, looking to your code:
.buffer_bytes_max = 512 * 4096, .period_bytes_min = 8192, .period_bytes_max = 8192, .periods_min = 8192, .periods_max = 8192,
It's definitely an empty configuration space.
You have buffer_bytes_max 2097152, but restricted number of periods to 8192 and period_bytes to 8192.
8192 * 8192 = 67108864 which is not less than 2097152 (and I'm not counting the bytes for channels and formats). Bingo, the ALSA PCM code does not know what to configure.
Decrease periods_min and probably also periods_max.
oh ! The error message was really a weird one. I can say that the hardware can do 192 audio frames in an interrupt, whatever the frame size is. So, what periods and period_bytes should I describe in the configuration ?
You cannot describe this using hw_ops. Leave period bytes in good range (192 - 1536?), periods (2-1024?) and add something like this to the open callback:
err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 192, 192);
Note the difference between SNDRV_PCM_HW_PARAM_PERIOD_SIZE and SNDRV_PCM_HW_PARAM_PERIOD_BYTES.
Jaroslav
----- Jaroslav Kysela perex@perex.cz Linux Kernel Sound Maintainer ALSA Project, Red Hat, Inc.
On Tue, Nov 9, 2010 at 8:04 PM, Jaroslav Kysela perex@perex.cz wrote:
On Tue, 9 Nov 2010, Manu Abraham wrote:
On Tue, Nov 9, 2010 at 6:21 PM, Jaroslav Kysela perex@perex.cz wrote:
On Tue, 9 Nov 2010, Manu Abraham wrote:
On Tue, Nov 9, 2010 at 5:31 PM, Jaroslav Kysela perex@perex.cz wrote:
On Tue, 9 Nov 2010, Manu Abraham wrote:
testbox ~ # arecord -Dhw:2,0 Recording WAVE 'stdin' : Unsigned 8 bit, Rate 8000 Hz, Mono
Any idea, why saa7231_hw_params is not getting invoked ?
Your hw probably does not support directly 8-bit sample at 8000Hz, one channel. Use 'plughw:2,0' device instead 'hw:2,0' to let alsa-lib do all stream format conversions.
That didn't make any difference at all.
Check your code. You probably created wrong hw_ops, looking to your code:
.buffer_bytes_max = 512 * 4096, .period_bytes_min = 8192, .period_bytes_max = 8192, .periods_min = 8192, .periods_max = 8192,
It's definitely an empty configuration space.
You have buffer_bytes_max 2097152, but restricted number of periods to 8192 and period_bytes to 8192.
8192 * 8192 = 67108864 which is not less than 2097152 (and I'm not counting the bytes for channels and formats). Bingo, the ALSA PCM code does not know what to configure.
Decrease periods_min and probably also periods_max.
oh ! The error message was really a weird one. I can say that the hardware can do 192 audio frames in an interrupt, whatever the frame size is. So, what periods and period_bytes should I describe in the configuration ?
You cannot describe this using hw_ops. Leave period bytes in good range (192
- 1536?), periods (2-1024?) and add something like this to the open
callback:
err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 192, 192);
Note the difference between SNDRV_PCM_HW_PARAM_PERIOD_SIZE and SNDRV_PCM_HW_PARAM_PERIOD_BYTES.
Ok, thanks.
I modified it to look thus ...
static struct snd_pcm_hardware saa7231_capture_info = { .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP_VALID),
.formats = (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE | SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_U16_BE),
.rates = SNDRV_PCM_RATE_32000,
.rate_min = 32000, .rate_max = 48000,
.channels_min = 2, .channels_max = 2,
.buffer_bytes_max = 512 * 4096, .period_bytes_min = 192, .period_bytes_max = 1536, .periods_min = 2, .periods_max = 1024, };
static int saa7231_capture_open(struct snd_pcm_substream *pcm) { struct saa7231_audio *audio = snd_pcm_substream_chip(pcm); struct snd_pcm_runtime *rt = pcm->runtime; struct saa7231_dev *saa7231 = audio->saa7231; int err;
dprintk(SAA7231_DEBUG, 1, "()"); rt->hw = saa7231_capture_info; snd_pcm_hw_constraint_list(rt, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraint_rates); err = snd_pcm_hw_constraint_integer(rt, SNDRV_PCM_HW_PARAM_PERIODS); if (err < 0) { dprintk(SAA7231_ERROR, 1, "snd_pcm_hw_constraint_integer() failed. ret=%d", err); return err; } err = snd_pcm_hw_constraint_minmax(rt, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 192, 192); if (err < 0) { dprintk(SAA7231_ERROR, 1, "snd_pcm_hw_constraint_minmax() failed. ret=%d", err); return err; }
return 0; }
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 saa7231_dmabuf *buffer; struct saa7231_stream *stream; struct saa7231_dmabuf *dmabuf;
struct page **ptable;
void *vma; void *dma_area;
int i, j, pages, bytes, periods, bufsiz;
dprintk(SAA7231_DEBUG, 1, "DEBUG: ()"); periods = params_periods(params); bytes = params_period_bytes(params);
bufsiz = periods * bytes; dprintk(SAA7231_DEBUG, 1, "bufsiz=%d periods=%d bytes=%d", bufsiz, periods, bytes);
pages = bufsiz / PAGE_SIZE;
/* enable stream */ stream = saa7231_stream_init(saa7231, AUDIO_CAPTURE, ADAPTER_INT, 0, 512); 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); return 0; }
testbox ~ # arecord -Dplughw:2,0 Recording WAVE 'stdin' : Unsigned 8 bit, Rate 8000 Hz, Mono ALSA lib pcm_mmap.c:369:(snd_pcm_mmap) mmap failed: Invalid argument arecord: set_params:1041: Unable to install hw params: ACCESS: RW_INTERLEAVED FORMAT: U8 SUBFORMAT: STD SAMPLE_BITS: 8 FRAME_BITS: 8 CHANNELS: 1 RATE: 8000 PERIOD_TIME: 6000 PERIOD_SIZE: 48 PERIOD_BYTES: 48 PERIODS: 83 BUFFER_TIME: 498000 BUFFER_SIZE: 3984 BUFFER_BYTES: 3984 TICK_TIME: 0
[ 819.928438] saa7231_capture_open (0): () [ 819.945466] saa7231_hw_params (0): DEBUG: () [ 819.945470] saa7231_hw_params (0): bufsiz=31872 periods=83 bytes=384 [ 819.945472] saa7231_stream_init (0): DEBUG: Initializing Stream with MODE=0x01 [ 819.945475] saa7231_xs2dtl_init (0): XS2DTL engine Initializing ..... [ 819.945477] saa7231_xs2dtl_init (0): Allocated 8 XS2DTL Buffer @0xf47b6000 [ 819.945479] saa7231_xs2dtl_init (0): Allocating DMA Buffers ... [ 819.945481] saa7231_xs2dtl_init (0): Allocating DMA buffer:0 @0xf47b6000.. [ 819.945492] saa7231_allocate_ptable (0): Virtual 0xf3bb7000 to Phys 0x33bb7000 mapped page [ 819.945598] saa7231_dmabuf_sgalloc (0): Virtual contiguous 2097152 byte region with 512 4k pages ... ...
bufsiz=31872 periods=83 bytes=384
Now, I wonder what a period really is. I guess I lack the basics here .. A basic explanation to the context, would be quite helpful.
Best Regards, Manu
On Wed, 10 Nov 2010, Manu Abraham wrote:
testbox ~ # arecord -Dplughw:2,0 Recording WAVE 'stdin' : Unsigned 8 bit, Rate 8000 Hz, Mono ALSA lib pcm_mmap.c:369:(snd_pcm_mmap) mmap failed: Invalid argument arecord: set_params:1041: Unable to install hw params:
set_params fails, because snd_pcm_mmap (mmap() syscall) failed for your driver. The configuration space for hw_params is now OK.
You have to check sound/core/pcm_native.c, if snd_pcm_default_mmap() and snd_pcm_mmap_data() implementation matches the mmap policy for your DMA buffer. You may create your own mmap callback - substream->ops->mmap.
bufsiz=31872 periods=83 bytes=384
This looks correct. Frame (in ALSA meaning) is 2 8-bit unsigned samples (you have forced to use 2 channels in your hw->ops). Thus 192 frames is 384 bytes for this stream format. Perhaps, your frame is not ALSA frame.
ALSA frame: channels * sizeof(sample)
Now, I wonder what a period really is. I guess I lack the basics here .. A basic explanation to the context, would be quite helpful.
Period is just a time after which should the driver acknowledge the transferred samples. Note that it's preffered to also allow large periods (if you can set hw to not acknowledge each DMA block transfer).
Jaroslav
----- Jaroslav Kysela perex@perex.cz Linux Kernel Sound Maintainer ALSA Project, Red Hat, Inc.
On Wed, Nov 10, 2010 at 12:35 PM, Jaroslav Kysela perex@perex.cz wrote:
On Wed, 10 Nov 2010, Manu Abraham wrote:
testbox ~ # arecord -Dplughw:2,0 Recording WAVE 'stdin' : Unsigned 8 bit, Rate 8000 Hz, Mono ALSA lib pcm_mmap.c:369:(snd_pcm_mmap) mmap failed: Invalid argument arecord: set_params:1041: Unable to install hw params:
set_params fails, because snd_pcm_mmap (mmap() syscall) failed for your driver. The configuration space for hw_params is now OK.
You have to check sound/core/pcm_native.c, if snd_pcm_default_mmap() and snd_pcm_mmap_data() implementation matches the mmap policy for your DMA buffer. You may create your own mmap callback - substream->ops->mmap.
I didn't yet map the buffers, will check it out.
bufsiz=31872 periods=83 bytes=384
This looks correct. Frame (in ALSA meaning) is 2 8-bit unsigned samples (you have forced to use 2 channels in your hw->ops). Thus 192 frames is 384 bytes for this stream format. Perhaps, your frame is not ALSA frame.
192 came from a mS long capture at 48kHz.
ALSA frame: channels * sizeof(sample)
Now, I wonder what a period really is. I guess I lack the basics here .. A basic explanation to the context, would be quite helpful.
Period is just a time after which should the driver acknowledge the transferred samples. Note that it's preffered to also allow large periods (if you can set hw to not acknowledge each DMA block transfer).
Ok, so period should be the interval at which an IRQ is expected. ie, if i set a buffer size for 40mS, the period should be 40, I guess.
That said, what should be a recommended value for period ? If hardware needs to generate an IRQ at the end of each period ?
Thanks, Manu
On Wed, 10 Nov 2010, Manu Abraham wrote:
Ok, so period should be the interval at which an IRQ is expected. ie, if i set a buffer size for 40mS, the period should be 40, I guess.
That said, what should be a recommended value for period ? If hardware needs to generate an IRQ at the end of each period ?
It's good to allow the lowledevel driver to pass all possible period sizes to the user space. The application does decision how many wakeups expects.
Jaroslav
----- Jaroslav Kysela perex@perex.cz Linux Kernel Sound Maintainer ALSA Project, Red Hat, Inc.
On Wed, Nov 10, 2010 at 5:00 PM, Jaroslav Kysela perex@perex.cz wrote:
On Wed, 10 Nov 2010, Manu Abraham wrote:
Ok, so period should be the interval at which an IRQ is expected. ie, if i set a buffer size for 40mS, the period should be 40, I guess.
That said, what should be a recommended value for period ? If hardware needs to generate an IRQ at the end of each period ?
It's good to allow the lowledevel driver to pass all possible period sizes to the user space. The application does decision how many wakeups expects.
Ok, I adapted it to look thus,
static struct snd_pcm_hardware saa7231_capture_info = { .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP_VALID),
.formats = (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE | SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_U16_BE),
.rates = SNDRV_PCM_RATE_32000,
.rate_min = 32000, .rate_max = 48000,
.channels_min = 2, .channels_max = 2,
.buffer_bytes_max = 512 * 4096, #if 0 .period_bytes_min = 192, .period_bytes_max = 1536, .periods_min = 2, .periods_max = 1024, #endif .period_bytes_min = 1920, /* 10mS @ 48khz */ .period_bytes_max = 768000, /* 4S @ 48kHz */
.periods_min = 10, /* 10mS */ .periods_max = 4000000, /* 4S */ };
/* * 10mS Buffer lengths * @48kHz 1920 bytes * @44.1kHz 1764 bytes * @32kHz 1280 bytes * * * period min = 10mS @48k: 1920 bytes @44.1k: 1764 bytes @32k: 1280 bytes * period max = 1S @48k:192000 bytes @44.1k:176400 bytes @32k:128000 bytes * period = 4S @48k:768000 bytes @44.1k:705600 bytes @32k:512000 bytes */ static int saa7231_capture_open(struct snd_pcm_substream *pcm) { struct saa7231_audio *audio = snd_pcm_substream_chip(pcm); struct snd_pcm_runtime *rt = pcm->runtime; struct saa7231_dev *saa7231 = audio->saa7231; int err;
dprintk(SAA7231_DEBUG, 1, "()"); rt->hw = saa7231_capture_info; snd_pcm_hw_constraint_list(rt, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraint_rates); err = snd_pcm_hw_constraint_integer(rt, SNDRV_PCM_HW_PARAM_PERIODS); if (err < 0) { dprintk(SAA7231_ERROR, 1, "snd_pcm_hw_constraint_integer() failed. ret=%d", err); return err; } err = snd_pcm_hw_constraint_minmax(rt, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 10, 4000000); if (err < 0) { dprintk(SAA7231_ERROR, 1, "snd_pcm_hw_constraint_minmax() failed. ret=%d", err); return err; } err = snd_pcm_hw_constraint_minmax(rt, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 1920, 768000); if (err < 0) { dprintk(SAA7231_ERROR, 1, "snd_pcm_hw_constraint_minmax() failed. ret=%d", err); return err; } return 0; }
/* * saa7231_hw_params() * * This callback is called when the hardware parameter (hw_params) is setup * by the application, ie., once when the buffer size, the period size, * the format etc are defined for the PCM substream. */ 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, pt_size;
dprintk(SAA7231_DEBUG, 1, "DEBUG: ()"); periods = params_periods(params); bytes = params_period_bytes(params);
bufsiz = periods * bytes; dprintk(SAA7231_DEBUG, 1, "bufsiz=%d periods=%d bytes=%d", bufsiz, periods, bytes);
pages = bufsiz / PAGE_SIZE;
/* enable stream */ /* initializing 8 buffer with "pages" pages each .. */ stream = saa7231_stream_init(saa7231, AUDIO_CAPTURE, ADAPTER_INT, 0, pages); 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);
/* * For a single DMA buffer: * each page takes a u64 size in a page table * number of entries a single page can hold = PAGE_SIZE / entry size * ie entries.max = PAGE_SIZE / 8 => 4096/8 = 512 * Now, we have entries.req = 7 ("pages") per buffer * * For all in XS2D_BUFFERS: * page table should be large enough to hold all the pages in each DMA buffer * total number of pages = pages * XS2D_BUFFERS * max buffers that we need to consider = 512 * 8, this needs 8 pages for a page table * * On a general note, we can calculate pages for page table as * page_table_size = total_pages / 512, with a minimum of a single page */
#define MAX_ENTRIES_PER_PAGE (PAGE_SIZE / 8)
pt_size = (pages * XS2D_BUFFERS) / MAX_ENTRIES_PER_PAGE; /* minimum 1 page required for the table */ if (pt_size < 1) pt_size = 1;
dprintk(SAA7231_DEBUG, 1, "Page Table array size=%d", pt_size); ptable = kzalloc((sizeof (struct page) * pt_size), GFP_KERNEL); if (!ptable) { dprintk(SAA7231_ERROR, 1, "ERROR: No memory to allocate virtual map"); return -ENOMEM; } audio->ptable = ptable;
for (i = 0; i < XS2D_BUFFERS; i ++) { dmabuf = &buffer[i]; mem = dmabuf->vmalloc; for (j = 0; j < pages; j++) ptable[j] = virt_to_page(mem); } #if 0 dma_area = vmap(ptable, (pages * XS2D_BUFFERS), VM_MAP, PAGE_KERNEL); rt->dma_area = dma_area; rt->dma_bytes = XS2D_BUFFERS * pages * SAA7231_PAGE_SIZE; rt->dma_addr = 0; #endif return 0; }
also after vmap(), crazy thing was that uncommenting the above #if 0'd block, ie with vmap() it would just crash the whole system.
Best Regards, Manu
On Wed, 10 Nov 2010, Manu Abraham wrote:
On Wed, Nov 10, 2010 at 5:00 PM, Jaroslav Kysela perex@perex.cz wrote:
On Wed, 10 Nov 2010, Manu Abraham wrote:
Ok, so period should be the interval at which an IRQ is expected. ie, if i set a buffer size for 40mS, the period should be 40, I guess.
That said, what should be a recommended value for period ? If hardware needs to generate an IRQ at the end of each period ?
It's good to allow the lowledevel driver to pass all possible period sizes to the user space. The application does decision how many wakeups expects.
Ok, I adapted it to look thus,
static struct snd_pcm_hardware saa7231_capture_info = { .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP_VALID),
.formats = (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE | SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_U16_BE),
.rates = SNDRV_PCM_RATE_32000,
.rate_min = 32000, .rate_max = 48000,
.channels_min = 2, .channels_max = 2,
.buffer_bytes_max = 512 * 4096, #if 0 .period_bytes_min = 192, .period_bytes_max = 1536, .periods_min = 2, .periods_max = 1024, #endif .period_bytes_min = 1920, /* 10mS @ 48khz */ .period_bytes_max = 768000, /* 4S @ 48kHz */
.periods_min = 10, /* 10mS */ .periods_max = 4000000, /* 4S */
??? 10 (period_bytes_min) * 10 (periods_min) = 100mS . Note that period_bytes_* limits periods size, but periods_* count of periods per the whole audio DMA ring buffer. Define smallest number of periods (it's usually value one or two for periods_min).
};
/*
- 10mS Buffer lengths
- @48kHz 1920 bytes
- @44.1kHz 1764 bytes
- @32kHz 1280 bytes
- period min = 10mS @48k: 1920 bytes @44.1k: 1764 bytes @32k: 1280 bytes
- period max = 1S @48k:192000 bytes @44.1k:176400 bytes @32k:128000 bytes
- period = 4S @48k:768000 bytes @44.1k:705600 bytes @32k:512000 bytes
*/ static int saa7231_capture_open(struct snd_pcm_substream *pcm) { struct saa7231_audio *audio = snd_pcm_substream_chip(pcm); struct snd_pcm_runtime *rt = pcm->runtime; struct saa7231_dev *saa7231 = audio->saa7231; int err;
dprintk(SAA7231_DEBUG, 1, "()"); rt->hw = saa7231_capture_info; snd_pcm_hw_constraint_list(rt, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraint_rates); err = snd_pcm_hw_constraint_integer(rt, SNDRV_PCM_HW_PARAM_PERIODS); if (err < 0) { dprintk(SAA7231_ERROR, 1, "snd_pcm_hw_constraint_integer() failed. ret=%d", err); return err; }
err = snd_pcm_hw_constraint_minmax(rt, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 10, 4000000); if (err < 0) { dprintk(SAA7231_ERROR, 1, "snd_pcm_hw_constraint_minmax() failed. ret=%d", err); return err; } err = snd_pcm_hw_constraint_minmax(rt, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 1920, 768000); if (err < 0) { dprintk(SAA7231_ERROR, 1, "snd_pcm_hw_constraint_minmax() failed. ret=%d", err); return err; }
Remove these minmax contrains. You already do limiting in hw->ops.
return 0;
}
/*
- saa7231_hw_params()
- This callback is called when the hardware parameter (hw_params) is setup
- by the application, ie., once when the buffer size, the period size,
- the format etc are defined for the PCM substream.
*/ 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, pt_size;
dprintk(SAA7231_DEBUG, 1, "DEBUG: ()"); periods = params_periods(params); bytes = params_period_bytes(params);
bufsiz = periods * bytes; dprintk(SAA7231_DEBUG, 1, "bufsiz=%d periods=%d bytes=%d", bufsiz, periods, bytes);
pages = bufsiz / PAGE_SIZE;
This is wrong. It should be 'pages = (bufsize + PAGE_SIZE - 1) / PAGE_SIZE'.
/* enable stream */ /* initializing 8 buffer with "pages" pages each .. */ stream = saa7231_stream_init(saa7231, AUDIO_CAPTURE, ADAPTER_INT, 0, pages); 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);
Unfortunately, I don't understand the role of XS2D_BUFFERS. The ALSA bufsize is the whole DMA area (you should use params_buffer_bytes() to get this value instead of calculating this using periods * period_size).
It means: Just allocate number of pages required for buffer_bytes. Don't play with periods (except the interrupts).
/* * For a single DMA buffer: * each page takes a u64 size in a page table * number of entries a single page can hold = PAGE_SIZE / entry size * ie entries.max = PAGE_SIZE / 8 => 4096/8 = 512 * Now, we have entries.req = 7 ("pages") per buffer * * For all in XS2D_BUFFERS: * page table should be large enough to hold all the pages in each DMA buffer * total number of pages = pages * XS2D_BUFFERS * max buffers that we need to consider = 512 * 8, this needs 8 pages for a page table * * On a general note, we can calculate pages for page table as * page_table_size = total_pages / 512, with a minimum of a single page */
#define MAX_ENTRIES_PER_PAGE (PAGE_SIZE / 8)
pt_size = (pages * XS2D_BUFFERS) / MAX_ENTRIES_PER_PAGE; /* minimum 1 page required for the table */ if (pt_size < 1) pt_size = 1;
dprintk(SAA7231_DEBUG, 1, "Page Table array size=%d", pt_size); ptable = kzalloc((sizeof (struct page) * pt_size), GFP_KERNEL); if (!ptable) { dprintk(SAA7231_ERROR, 1, "ERROR: No memory to allocate virtual map"); return -ENOMEM; } audio->ptable = ptable;
for (i = 0; i < XS2D_BUFFERS; i ++) { dmabuf = &buffer[i]; mem = dmabuf->vmalloc; for (j = 0; j < pages; j++) ptable[j] = virt_to_page(mem); }
This looks broken. ptable is overwritten for each XS2D_BUFFERS iteration.
#if 0 dma_area = vmap(ptable, (pages * XS2D_BUFFERS), VM_MAP, PAGE_KERNEL); rt->dma_area = dma_area; rt->dma_bytes = XS2D_BUFFERS * pages * SAA7231_PAGE_SIZE; rt->dma_addr = 0; #endif return 0; }
I think that the second argument is number of linux mem pages. Your value seems strange.
Jaroslav
----- Jaroslav Kysela perex@perex.cz Linux Kernel Sound Maintainer ALSA Project, Red Hat, Inc.
On Wed, Nov 10, 2010 at 8:35 PM, Jaroslav Kysela perex@perex.cz wrote:
On Wed, 10 Nov 2010, Manu Abraham wrote:
On Wed, Nov 10, 2010 at 5:00 PM, Jaroslav Kysela perex@perex.cz wrote:
On Wed, 10 Nov 2010, Manu Abraham wrote:
Ok, so period should be the interval at which an IRQ is expected. ie, if i set a buffer size for 40mS, the period should be 40, I guess.
That said, what should be a recommended value for period ? If hardware needs to generate an IRQ at the end of each period ?
It's good to allow the lowledevel driver to pass all possible period sizes to the user space. The application does decision how many wakeups expects.
Ok, I adapted it to look thus,
static struct snd_pcm_hardware saa7231_capture_info = { .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP_VALID),
.formats = (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE | SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_U16_BE),
.rates = SNDRV_PCM_RATE_32000,
.rate_min = 32000, .rate_max = 48000,
.channels_min = 2, .channels_max = 2,
.buffer_bytes_max = 512 * 4096, #if 0 .period_bytes_min = 192, .period_bytes_max = 1536, .periods_min = 2, .periods_max = 1024, #endif .period_bytes_min = 1920, /* 10mS @ 48khz */ .period_bytes_max = 768000, /* 4S @ 48kHz */
.periods_min = 10, /* 10mS */ .periods_max = 4000000, /* 4S */
??? 10 (period_bytes_min) * 10 (periods_min) = 100mS . Note that period_bytes_* limits periods size, but periods_* count of periods per the whole audio DMA ring buffer. Define smallest number of periods (it's usually value one or two for periods_min).
Ok.
};
/*
- 10mS Buffer lengths
- @48kHz 1920 bytes
- @44.1kHz 1764 bytes
- @32kHz 1280 bytes
- period min = 10mS @48k: 1920 bytes @44.1k: 1764 bytes @32k: 1280
bytes
- period max = 1S @48k:192000 bytes @44.1k:176400 bytes @32k:128000
bytes
- period = 4S @48k:768000 bytes @44.1k:705600 bytes @32k:512000
bytes */ static int saa7231_capture_open(struct snd_pcm_substream *pcm) { struct saa7231_audio *audio = snd_pcm_substream_chip(pcm); struct snd_pcm_runtime *rt = pcm->runtime; struct saa7231_dev *saa7231 = audio->saa7231; int err;
dprintk(SAA7231_DEBUG, 1, "()"); rt->hw = saa7231_capture_info; snd_pcm_hw_constraint_list(rt, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraint_rates); err = snd_pcm_hw_constraint_integer(rt, SNDRV_PCM_HW_PARAM_PERIODS); if (err < 0) { dprintk(SAA7231_ERROR, 1, "snd_pcm_hw_constraint_integer() failed. ret=%d", err); return err; }
err = snd_pcm_hw_constraint_minmax(rt, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 10, 4000000); if (err < 0) { dprintk(SAA7231_ERROR, 1, "snd_pcm_hw_constraint_minmax() failed. ret=%d", err); return err; } err = snd_pcm_hw_constraint_minmax(rt, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 1920, 768000); if (err < 0) { dprintk(SAA7231_ERROR, 1, "snd_pcm_hw_constraint_minmax() failed. ret=%d", err); return err; }
Remove these minmax contrains. You already do limiting in hw->ops.
Ok.
return 0; }
/*
- saa7231_hw_params()
- This callback is called when the hardware parameter (hw_params) is setup
- by the application, ie., once when the buffer size, the period size,
- the format etc are defined for the PCM substream.
*/ 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, pt_size;
dprintk(SAA7231_DEBUG, 1, "DEBUG: ()"); periods = params_periods(params); bytes = params_period_bytes(params);
bufsiz = periods * bytes; dprintk(SAA7231_DEBUG, 1, "bufsiz=%d periods=%d bytes=%d", bufsiz, periods, bytes);
pages = bufsiz / PAGE_SIZE;
This is wrong. It should be 'pages = (bufsize + PAGE_SIZE - 1) / PAGE_SIZE'.
Ok.
/* enable stream */ /* initializing 8 buffer with "pages" pages each .. */ stream = saa7231_stream_init(saa7231, AUDIO_CAPTURE, ADAPTER_INT, 0, pages); 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);
Unfortunately, I don't understand the role of XS2D_BUFFERS. The ALSA bufsize is the whole DMA area (you should use params_buffer_bytes() to get this value instead of calculating this using periods * period_size).
Ok. The hardware has 8 SG buffers each. each of the buffer heads are setup on to the MMU. each of these buffers XS2D_BUFFERS can handle a maximum of 512 pages, ie page table for the SG buffer can be a maximum of 4096 bytes long.
It means: Just allocate number of pages required for buffer_bytes. Don't play with periods (except the interrupts).
/* * For a single DMA buffer: * each page takes a u64 size in a page table * number of entries a single page can hold = PAGE_SIZE / entry size * ie entries.max = PAGE_SIZE / 8 => 4096/8 = 512 * Now, we have entries.req = 7 ("pages") per buffer * * For all in XS2D_BUFFERS: * page table should be large enough to hold all the pages in each DMA buffer * total number of pages = pages * XS2D_BUFFERS * max buffers that we need to consider = 512 * 8, this needs 8 pages for a page table * * On a general note, we can calculate pages for page table as * page_table_size = total_pages / 512, with a minimum of a single page */
#define MAX_ENTRIES_PER_PAGE (PAGE_SIZE / 8)
pt_size = (pages * XS2D_BUFFERS) / MAX_ENTRIES_PER_PAGE; /* minimum 1 page required for the table */ if (pt_size < 1) pt_size = 1;
dprintk(SAA7231_DEBUG, 1, "Page Table array size=%d", pt_size); ptable = kzalloc((sizeof (struct page) * pt_size), GFP_KERNEL); if (!ptable) { dprintk(SAA7231_ERROR, 1, "ERROR: No memory to allocate virtual map"); return -ENOMEM; } audio->ptable = ptable;
for (i = 0; i < XS2D_BUFFERS; i ++) { dmabuf = &buffer[i]; mem = dmabuf->vmalloc; for (j = 0; j < pages; j++) ptable[j] = virt_to_page(mem); }
This looks broken. ptable is overwritten for each XS2D_BUFFERS iteration.
True.
#if 0 dma_area = vmap(ptable, (pages * XS2D_BUFFERS), VM_MAP, PAGE_KERNEL); rt->dma_area = dma_area; rt->dma_bytes = XS2D_BUFFERS * pages * SAA7231_PAGE_SIZE; rt->dma_addr = 0; #endif return 0; }
I think that the second argument is number of linux mem pages. Your value seems strange.
Uh, oh. I had assumed that each of the 8 XS2D_Buffers would contain "pages" each and hence..
Regards, Manu
On Wed, Nov 10, 2010 at 8:35 PM, Jaroslav Kysela perex@perex.cz wrote:
On Wed, 10 Nov 2010, Manu Abraham wrote:
/* enable stream */ /* initializing 8 buffer with "pages" pages each .. */ stream = saa7231_stream_init(saa7231, AUDIO_CAPTURE, ADAPTER_INT, 0, pages); 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);
Unfortunately, I don't understand the role of XS2D_BUFFERS. The ALSA bufsize is the whole DMA area (you should use params_buffer_bytes() to get this value instead of calculating this using periods * period_size).
It means: Just allocate number of pages required for buffer_bytes. Don't play with periods (except the interrupts).
There are 8 SG buffers for the hardware; the maximum size of each buffer can be 512 pages, the minimum can be a single page.
Since each of the 8 buffers, the minimum buffer size of each buffer is 4096 bytes, the total minimum ALSA buffer size would be 8 * 4096 bytes = 32768 bytes, and a maximum of 8 * 512 * 4096 = 16777216 bytes
So, I should calculate the periods and period bytes based on the buffer size involved ? ie: it could turn out to be hard to get exact values for each of the sampling rates involved ?
Regards, Manu
On Wed, 10 Nov 2010, Manu Abraham wrote:
On Wed, Nov 10, 2010 at 8:35 PM, Jaroslav Kysela perex@perex.cz wrote:
On Wed, 10 Nov 2010, Manu Abraham wrote:
/* enable stream */ /* initializing 8 buffer with "pages" pages each .. */ stream = saa7231_stream_init(saa7231, AUDIO_CAPTURE, ADAPTER_INT, 0, pages); 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);
Unfortunately, I don't understand the role of XS2D_BUFFERS. The ALSA bufsize is the whole DMA area (you should use params_buffer_bytes() to get this value instead of calculating this using periods * period_size).
It means: Just allocate number of pages required for buffer_bytes. Don't play with periods (except the interrupts).
There are 8 SG buffers for the hardware; the maximum size of each buffer can be 512 pages, the minimum can be a single page.
Could you describe more the whole DMA layout, including IRQ acks?
It seems to me:
SG PAGE 1 points to 1 - 512 data pages (4096 bytes long) SG PAGE 2 ..... .... SG PAGE 8 .....
How you can program interrupts? Only when the whole page is finished or the interrupt does not depend on the DMA buffer position, but a hw clock timer?
Could you limit how much data (samples) are fetched from the one data page or the hw operates with whole pages only?
Note that the stream processing rate is usually independent from the DMA layout in most hw.
Jaroslav
----- Jaroslav Kysela perex@perex.cz Linux Kernel Sound Maintainer ALSA Project, Red Hat, Inc.
On Wed, Nov 10, 2010 at 11:35 PM, Jaroslav Kysela perex@perex.cz wrote:
On Wed, 10 Nov 2010, Manu Abraham wrote:
On Wed, Nov 10, 2010 at 8:35 PM, Jaroslav Kysela perex@perex.cz wrote:
On Wed, 10 Nov 2010, Manu Abraham wrote:
/* enable stream */ /* initializing 8 buffer with "pages" pages each .. */ stream = saa7231_stream_init(saa7231, AUDIO_CAPTURE, ADAPTER_INT, 0, pages); 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);
Unfortunately, I don't understand the role of XS2D_BUFFERS. The ALSA bufsize is the whole DMA area (you should use params_buffer_bytes() to get this value instead of calculating this using periods * period_size).
It means: Just allocate number of pages required for buffer_bytes. Don't play with periods (except the interrupts).
There are 8 SG buffers for the hardware; the maximum size of each buffer can be 512 pages, the minimum can be a single page.
Could you describe more the whole DMA layout, including IRQ acks?
It seems to me:
SG PAGE 1 points to 1 - 512 data pages (4096 bytes long) SG PAGE 2 ..... .... SG PAGE 8 .....
Yes, correct.
My initial thoughts and concepts were thus: - A particular buffer size is requested by ALSA - this buffer size is set for all the 8 SG buffers - A virtual area is created from these 8 SG buffers - This virtual area is mmap()'d - SG Buffer 0 gets filled - Interrupt occurs - Driver reads from offset 0 of virtual area - SG Buffer 1 gets filled - Interrupt occurs - Driver reads from offset "buffer size" of virtual area
and so on, the cycle repeats.
Little did I realize at that point of time that buffer size for ALSA meant the total length of the buffers.
How you can program interrupts? Only when the whole page is finished or the interrupt does not depend on the DMA buffer position, but a hw clock timer?
All 8 SG heads are placed on the MMU. The MMU can be programmed to generate an IRQ when "size" buffer is full. Suppose that an SG buffer which is 8 pages long, but I did program the MMU to generate an IRQ when eg: 192 bytes of data is filled into SG buffer. Now, as data is being digitized and grabbed, I get an interrupt when 192 bytes is filled in the SG buffer.
When the interrupt occurs, the driver needs to check from hardware registers, which of the 8 SG buffers has been filled upto say 192 bytes. Now, since I know that SG buffer 'x' is filled, I can ask the application to read from the buffer area. This length is called "stride" by the chip.
Normally, I could say the interrupts would be following a certain order, SGBUF1, SGBUF2, but It is indeed necessary to read the buffer index first and then deduce which SGBUF to read.
Putting it short, even though a whole page is setup for DMA, an interrupt can be invoked, depending on setting up the "stride" length, the maximum of which is the buffer length setup on the MMU.
There is no hardware clock timer involved in this process. but there is a hardware feature where the buffers can be time stamped for reason such as synchronization with a video stream for example.
Could you limit how much data (samples) are fetched from the one data page or the hw operates with whole pages only?
You need to setup a whole PAGE on to the MMU, even if you need to transfer only a byte.
Yes, you can set any number of bytes (stride) to be read from the whole SG buffer. ie, even if the SG buffer is 512 pages long, it could contain just contain 4 bytes of data alone (depending on stride), If I set up the hardware to generate an interrupt when 4 bytes is filled up.
Note that the stream processing rate is usually independent from the DMA layout in most hw.
Yes. I need to set the number of bytes that will be filled up for a buffer that causes an interrupt.
Regards, Manu
On Wed, Nov 10, 2010 at 11:35 PM, Jaroslav Kysela perex@perex.cz wrote:
On Wed, 10 Nov 2010, Manu Abraham wrote:
On Wed, Nov 10, 2010 at 8:35 PM, Jaroslav Kysela perex@perex.cz wrote:
On Wed, 10 Nov 2010, Manu Abraham wrote:
/* enable stream */ /* initializing 8 buffer with "pages" pages each .. */ stream = saa7231_stream_init(saa7231, AUDIO_CAPTURE, ADAPTER_INT, 0, pages); 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);
Unfortunately, I don't understand the role of XS2D_BUFFERS. The ALSA bufsize is the whole DMA area (you should use params_buffer_bytes() to get this value instead of calculating this using periods * period_size).
It means: Just allocate number of pages required for buffer_bytes. Don't play with periods (except the interrupts).
There are 8 SG buffers for the hardware; the maximum size of each buffer can be 512 pages, the minimum can be a single page.
Could you describe more the whole DMA layout, including IRQ acks?
It seems to me:
SG PAGE 1 points to 1 - 512 data pages (4096 bytes long) SG PAGE 2 ..... .... SG PAGE 8 .....
I adapted the whole thing to make the buffersize divided amongst the XS2D_BUFFERS (8): I hope the mapping of the buffers is okay ? It looks thus, now ..
static struct snd_pcm_hardware saa7231_capture_info = { .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP_VALID),
.formats = (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE | SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_U16_BE),
.rates = SNDRV_PCM_RATE_32000,
.rate_min = 32000, .rate_max = 32000,
.channels_min = 1, .channels_max = 2,
// .buffer_bytes_max = 512 * 4096, .buffer_bytes_max = 256 * 1024, #if 0 .period_bytes_min = 192, .period_bytes_max = 1536, .periods_min = 2, .periods_max = 1024, #endif #if 0 .period_bytes_min = 1920, /* 10mS @ 48khz */ .period_bytes_max = 768000, /* 4S @ 48kHz */
.periods_min = 10, /* 10mS */ .periods_max = 4000000, /* 4S */ #endif
.period_bytes_min = 64, .period_bytes_max = 256 * 1024,
.periods_min = 4, .periods_max = 1024, };
/* * 10mS Buffer lengths * @48kHz 1920 bytes * @44.1kHz 1764 bytes * @32kHz 1280 bytes * * * period min = 10mS @48k: 1920 bytes @44.1k: 1764 bytes @32k: 1280 bytes * period max = 1S @48k:192000 bytes @44.1k:176400 bytes @32k:128000 bytes * period = 4S @48k:768000 bytes @44.1k:705600 bytes @32k:512000 bytes */ static int saa7231_capture_open(struct snd_pcm_substream *pcm) { struct saa7231_audio *audio = snd_pcm_substream_chip(pcm); struct snd_pcm_runtime *rt = pcm->runtime; struct saa7231_dev *saa7231 = audio->saa7231; int err;
dprintk(SAA7231_DEBUG, 1, "()"); rt->hw = saa7231_capture_info; snd_pcm_hw_constraint_list(rt, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraint_rates); err = snd_pcm_hw_constraint_integer(rt, SNDRV_PCM_HW_PARAM_PERIODS); if (err < 0) { dprintk(SAA7231_ERROR, 1, "snd_pcm_hw_constraint_integer() failed. ret=%d", err); return err; } #if 0 err = snd_pcm_hw_constraint_minmax(rt, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 10, 4000000); if (err < 0) { dprintk(SAA7231_ERROR, 1, "snd_pcm_hw_constraint_minmax() failed. ret=%d", err); return err; } err = snd_pcm_hw_constraint_minmax(rt, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 1920, 768000); if (err < 0) { dprintk(SAA7231_ERROR, 1, "snd_pcm_hw_constraint_minmax() failed. ret=%d", err); return err; } #endif return 0; }
/* * saa7231_hw_params() * * This callback is called when the hardware parameter (hw_params) is setup * by the application, ie., once when the buffer size, the period size, * the format etc are defined for the PCM substream. */ 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, pt_size, idx;
#define MAX_ENTRIES_PER_PAGE (PAGE_SIZE / 8) #define PAGES_PER_XS2D(__pages) (__pages / XS2D_BUFFERS)
dprintk(SAA7231_DEBUG, 1, "DEBUG: ()");
periods = params_periods(params); bytes = params_period_bytes(params); bufsiz = params_buffer_bytes(params); pages = (bufsiz + PAGE_SIZE - 1) / PAGE_SIZE;
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));
/* * For a single DMA buffer: * each page takes a u64 size in a page table * number of entries a single page can hold = PAGE_SIZE / entry size * ie entries.max = PAGE_SIZE / 8 => 4096/8 = 512 * Now, we have entries.req = 7 ("pages") per buffer * * For all in XS2D_BUFFERS: * page table should be large enough to hold all the pages in each DMA buffer * total number of pages = pages * XS2D_BUFFERS * max buffers that we need to consider = 512 * 8, this needs 8 pages for a page table * * On a general note, we can calculate pages for page table as * page_table_size = total_pages / 512, with a minimum of a single page */
pt_size = (pages * XS2D_BUFFERS) / MAX_ENTRIES_PER_PAGE; /* minimum 1 page required for the table */ if (pt_size < 1) pt_size = 1;
dprintk(SAA7231_DEBUG, 1, "Page Table array size=%d", pt_size); ptable = kzalloc((sizeof (struct page) * pt_size), 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; }
/* * saa7231_hw_free() * * This callback is called to release the resources allocated via hw_params. * For eg: releasing the buffer via snd_pcm_lib_malloc_pages() is done by * snd_pcm_lib_free_pages(substream) * This function is always called before the close callback is called. Also, * the callback maybe called multiple times. */ static int saa7231_hw_free(struct snd_pcm_substream *pcm) { struct saa7231_audio *audio = snd_pcm_substream_chip(pcm); struct saa7231_dev *saa7231 = audio->saa7231; struct saa7231_stream *stream = audio->stream; struct page **ptable = audio->ptable; struct snd_pcm_runtime *rt = pcm->runtime; void *dma_area = rt->dma_area;
dprintk(SAA7231_DEBUG, 1, "DEBUG: Removing IRQ event .."); saa7231_remove_irqevent(saa7231, 43);
if (dma_area) { dprintk(SAA7231_DEBUG, 1, "DEBUG: Unmap Virtual memory region .."); vunmap(dma_area); } dprintk(SAA7231_DEBUG, 1, "DEBUG: Stream exiting .."); saa7231_stream_exit(stream);
dprintk(SAA7231_DEBUG, 1, "DEBUG: Freeing ptable ..."); kfree(ptable); return 0; }
testbox ~ # arecord -D plughw:2,0 --format S16_LE --rate=32000 -c 2 > ~/test_capture.wav Recording WAVE 'stdin' : Signed 16 bit Little Endian, Rate 32000 Hz, Stereo arecord: pcm_read:1617: read error: Input/output error
[ 114.326533] SAA7231 0000:05:00.0: PCI INT A -> GSI 16 (level, low) -> IRQ 16 [ 114.326541] SAA7231 0000:05:00.0: setting latency timer to 64 [ 114.326564] saa7231_pci_init (0): BAR 0 Offset: 04 BAR 2 Offset: 04 [ 114.326567] saa7231_pci_init (0): BAR0 Start=fc800000 length=4M [ 114.326569] saa7231_pci_init (0): BAR2 Start=fc400000 length=4M [ 114.328217] SAA7231GE [14c7:3595], irq: 16, [ 114.328219] mmio(0): 0xf8800000 mmio(2): 0xf9e00000 [ 114.328232] SAA7231 64Bit, MSI Disabled, MSI-X=1 msgs [ 114.328239] saa7231_get_version (0): SAA7231 PCI Express V1A found [ 114.328241] saa7231_cgu_init (0): Initializing CGU [ 114.328243] saa7231_cgu_init (0): Using 54MHz RootClock [ 114.328264] saa7231_cgu_init (0): PLL Status CDEC160: 00 REF550: 00 ADPLL: 00 DSP: 00 [ 114.328266] saa7231_set_clk (0): clk_output=0 clk_frequency=2 [ 114.328268] saa7231_set_clk (0): clk_output=1 clk_frequency=2 [ 114.328270] saa7231_set_clk (0): clk_output=2 clk_frequency=2 [ 114.328272] saa7231_set_clk (0): clk_output=3 clk_frequency=2 [ 114.328273] saa7231_set_clk (0): clk_output=4 clk_frequency=2 [ 114.328275] saa7231_set_clk (0): clk_output=5 clk_frequency=2 [ 114.328277] saa7231_set_clk (0): clk_output=6 clk_frequency=2 [ 114.328278] saa7231_set_clk (0): clk_output=7 clk_frequency=2 [ 114.328280] saa7231_set_clk (0): clk_output=8 clk_frequency=2 [ 114.328282] saa7231_set_clk (0): clk_output=9 clk_frequency=2 [ 114.328283] saa7231_set_clk (0): clk_output=10 clk_frequency=2 [ 114.328285] saa7231_set_clk (0): clk_output=11 clk_frequency=2 [ 114.328287] saa7231_set_clk (0): clk_output=12 clk_frequency=2 [ 114.328288] saa7231_set_clk (0): clk_output=13 clk_frequency=2 [ 114.328290] saa7231_set_clk (0): clk_output=14 clk_frequency=2 [ 114.328292] saa7231_set_clk (0): clk_output=15 clk_frequency=2 [ 114.328294] saa7231_set_clk (0): clk_output=16 clk_frequency=2 [ 114.328295] saa7231_set_clk (0): clk_output=17 clk_frequency=2 [ 114.328297] saa7231_set_clk (0): clk_output=18 clk_frequency=2 [ 114.379027] saa7231_cgu_init (0): DEBUG: RGU_RESET_ACTIVE0 7c02001f [ 114.481027] saa7231_cgu_init (0): DEBUG: RGU_RESET_ACTIVE0 ffffffff [ 114.481032] saa7231_cgu_init (0): DEBUG: RGU_RESET_ACTIVE1 2e1f [ 114.481034] saa7231_set_clk (0): clk_output=17 clk_frequency=0 [ 114.487053] saa7231_i2c_init (0): Initializing SAA7231 I2C Core [ 114.487057] saa7231_i2c_init (0): Initializing adapter (0) SAA7231 I2C:0 [ 114.487328] saa7231_i2c_hwinit (0): Adapter (109000) SAA7231 I2C:0 RESET [ 114.487330] saa7231_i2c_hwinit (0): Initializing Adapter SAA7231 I2C:0 @ 100k [ 114.487338] saa7231_i2c_init (0): Initializing adapter (1) SAA7231 12C:1 [ 114.487556] saa7231_i2c_hwinit (0): Adapter (10a000) SAA7231 12C:1 RESET [ 114.487559] saa7231_i2c_hwinit (0): Initializing Adapter SAA7231 12C:1 @ 100k [ 114.487568] saa7231_i2c_init (0): Initializing adapter (2) SAA7231 I2C:2 [ 114.487643] saa7231_i2c_hwinit (0): Adapter (10b000) SAA7231 I2C:2 RESET [ 114.487645] saa7231_i2c_hwinit (0): Initializing Adapter SAA7231 I2C:2 @ 100k [ 114.487653] saa7231_i2c_init (0): Initializing adapter (3) SAA7231 I2C:3 [ 114.488079] saa7231_i2c_hwinit (0): Adapter (10c000) SAA7231 I2C:3 RESET [ 114.488082] saa7231_i2c_hwinit (0): Initializing Adapter SAA7231 I2C:3 @ 100k [ 114.488089] saa7231_i2c_init (0): SAA7231 I2C Core succesfully initialized [ 114.488091] saa7231_alsa_init (0): Initializing Audio .. [ 114.495634] saa7231_pci_probe (0): SAA7231 device:0 initialized [ 124.166016] saa7231_capture_open (0): () [ 124.166546] saa7231_hw_params (0): DEBUG: () [ 124.166548] saa7231_hw_params (0): bufsiz=64000 periods=4 bytes=16000 pages=16 [ 124.166551] saa7231_stream_init (0): DEBUG: Initializing Stream with MODE=0x01 [ 124.166554] saa7231_xs2dtl_init (0): XS2DTL engine Initializing ..... [ 124.166557] saa7231_xs2dtl_init (0): Allocated 8 XS2DTL Buffer @0xf52a1200 [ 124.166559] saa7231_xs2dtl_init (0): Allocating DMA Buffers ... [ 124.166561] saa7231_xs2dtl_init (0): Allocating DMA buffer:0 @0xf52a1200.. [ 124.166571] saa7231_allocate_ptable (0): Virtual 0xf460f000 to Phys 0x3460f000 mapped page [ 124.166588] saa7231_dmabuf_sgalloc (0): Virtual contiguous 8192 byte region with 2 4k pages [ 124.166603] 6c48c000 6c487000 [ 124.166608] saa7231_xs2dtl_init (0): Allocating DMA buffer:1 @0xf52a1228.. [ 124.166616] saa7231_allocate_ptable (0): Virtual 0xf4611000 to Phys 0x34611000 mapped page [ 124.166622] saa7231_dmabuf_sgalloc (0): Virtual contiguous 8192 byte region with 2 4k pages [ 124.166633] 6f95e000 6c291000 [ 124.166638] saa7231_xs2dtl_init (0): Allocating DMA buffer:2 @0xf52a1250.. [ 124.166645] saa7231_allocate_ptable (0): Virtual 0xf64ab000 to Phys 0x364ab000 mapped page [ 124.166651] saa7231_dmabuf_sgalloc (0): Virtual contiguous 8192 byte region with 2 4k pages [ 124.166662] 6c2a9000 6c468000 [ 124.166666] saa7231_xs2dtl_init (0): Allocating DMA buffer:3 @0xf52a1278.. [ 124.166674] saa7231_allocate_ptable (0): Virtual 0xf5dad000 to Phys 0x35dad000 mapped page [ 124.166679] saa7231_dmabuf_sgalloc (0): Virtual contiguous 8192 byte region with 2 4k pages [ 124.166690] 6c463000 6c464000 [ 124.166694] saa7231_xs2dtl_init (0): Allocating DMA buffer:4 @0xf52a12a0.. [ 124.166702] saa7231_allocate_ptable (0): Virtual 0xf5e01000 to Phys 0x35e01000 mapped page [ 124.166707] saa7231_dmabuf_sgalloc (0): Virtual contiguous 8192 byte region with 2 4k pages [ 124.166719] 6c466000 6c467000 [ 124.166723] saa7231_xs2dtl_init (0): Allocating DMA buffer:5 @0xf52a12c8.. [ 124.166731] saa7231_allocate_ptable (0): Virtual 0xf53e9000 to Phys 0x353e9000 mapped page [ 124.166736] saa7231_dmabuf_sgalloc (0): Virtual contiguous 8192 byte region with 2 4k pages [ 124.166748] 6c221000 6c222000 [ 124.166752] saa7231_xs2dtl_init (0): Allocating DMA buffer:6 @0xf52a12f0.. [ 124.166760] saa7231_allocate_ptable (0): Virtual 0xf45ea000 to Phys 0x345ea000 mapped page [ 124.166765] saa7231_dmabuf_sgalloc (0): Virtual contiguous 8192 byte region with 2 4k pages [ 124.166776] 6c374000 6c375000 [ 124.166780] saa7231_xs2dtl_init (0): Allocating DMA buffer:7 @0xf52a1318.. [ 124.166788] saa7231_allocate_ptable (0): Virtual 0xf46e7000 to Phys 0x346e7000 mapped page [ 124.166793] saa7231_dmabuf_sgalloc (0): Virtual contiguous 8192 byte region with 2 4k pages [ 124.166804] 6c377000 6c358000 [ 124.166808] saa7231_xs2dtl_init (0): Initializing PTA ... [ 124.166810] saa7231_init_ptables (0): DEBUG: Initializing PORT:6 DMA_CH:7 with 8 Buffers [ 124.166812] saa7231_xs2dtl_init (0): Setting up PORT Gates ... [ 124.166815] saa7231_stream_init (0): INFO: Initialized MODE:0x01 for PORT:6 [ 124.166817] saa7231_add_irqevent (0): Adding AS2D_AVIS IRQ Event:43 ... [ 124.166819] saa7231_setup_vector (0): Adding Vector:43 [ 124.166823] saa7231_setup_vector (0): Enabling Vector:43 [ 124.166827] saa7231_add_irqevent (0): Succesfully added AS2D_AVIS as Event handler:43 [ 124.166829] saa7231_hw_params (0): Mapping 8 buffers with 2 pages each [ 124.166830] saa7231_hw_params (0): Page Table array size=1 [ 124.166832] saa7231_hw_params (0): Mapping Page:0 from XS2D_BUFFER:0 to PTA Offset:0 [ 124.166834] saa7231_hw_params (0): Mapping Page:1 from XS2D_BUFFER:0 to PTA Offset:1 [ 124.166836] saa7231_hw_params (0): Mapping Page:0 from XS2D_BUFFER:1 to PTA Offset:2 [ 124.166838] saa7231_hw_params (0): Mapping Page:1 from XS2D_BUFFER:1 to PTA Offset:3 [ 124.166840] saa7231_hw_params (0): Mapping Page:0 from XS2D_BUFFER:2 to PTA Offset:4 [ 124.166842] saa7231_hw_params (0): Mapping Page:1 from XS2D_BUFFER:2 to PTA Offset:5 [ 124.166844] saa7231_hw_params (0): Mapping Page:0 from XS2D_BUFFER:3 to PTA Offset:6 [ 124.166846] saa7231_hw_params (0): Mapping Page:1 from XS2D_BUFFER:3 to PTA Offset:7 [ 124.166848] saa7231_hw_params (0): Mapping Page:0 from XS2D_BUFFER:4 to PTA Offset:8 [ 124.166850] saa7231_hw_params (0): Mapping Page:1 from XS2D_BUFFER:4 to PTA Offset:9 [ 124.166852] saa7231_hw_params (0): Mapping Page:0 from XS2D_BUFFER:5 to PTA Offset:10 [ 124.166854] saa7231_hw_params (0): Mapping Page:1 from XS2D_BUFFER:5 to PTA Offset:11 [ 124.166856] saa7231_hw_params (0): Mapping Page:0 from XS2D_BUFFER:6 to PTA Offset:12 [ 124.166858] saa7231_hw_params (0): Mapping Page:1 from XS2D_BUFFER:6 to PTA Offset:13 [ 124.166860] saa7231_hw_params (0): Mapping Page:0 from XS2D_BUFFER:7 to PTA Offset:14 [ 124.166862] saa7231_hw_params (0): Mapping Page:1 from XS2D_BUFFER:7 to PTA Offset:15 [ 124.166884] saa7231_capture_prepare (0): () [ 124.166974] saa7231_capture_trigger (0): () cmd=1 [ 124.166978] saa7231_capture_pointer (0): DEBUG:() [ 134.166232] saa7231_capture_trigger (0): () cmd=0 [ 134.166248] saa7231_hw_free (0): DEBUG: Removing IRQ event .. [ 134.166251] saa7231_remove_irqevent (0): Removing IRQ Event:43 ...... [ 134.166254] saa7231_remove_irqevent (0): IRQ Event 43 <AS2D_AVIS> removed [ 134.166258] saa7231_hw_free (0): DEBUG: Unmap Virtual memory region .. [ 134.166323] saa7231_hw_free (0): DEBUG: Stream exiting .. [ 134.166325] saa7231_stream_exit (0): INFO: Freeing MODE:0x01 for PORT=0x06 [ 134.166327] saa7231_xs2dtl_exit (0): Free XS2DTL engine .. [ 134.166329] saa7231_dmabuf_free (0): INFO: Scatterlist unmapped [ 134.166366] saa7231_dmabuf_free (0): INFO: Scatterlist free [ 134.166368] saa7231_dmabuf_sgfree (0): SG free [ 134.166375] saa7231_dmabuf_free (0): INFO: Page table free [ 134.166376] saa7231_free_ptable (0): SG Page table free [ 134.166380] saa7231_dmabuf_free (0): INFO: Scatterlist unmapped [ 134.166384] saa7231_dmabuf_free (0): INFO: Scatterlist free [ 134.166386] saa7231_dmabuf_sgfree (0): SG free [ 134.166392] saa7231_dmabuf_free (0): INFO: Page table free [ 134.166394] saa7231_free_ptable (0): SG Page table free [ 134.166398] saa7231_dmabuf_free (0): INFO: Scatterlist unmapped [ 134.166401] saa7231_dmabuf_free (0): INFO: Scatterlist free [ 134.166403] saa7231_dmabuf_sgfree (0): SG free [ 134.166409] saa7231_dmabuf_free (0): INFO: Page table free [ 134.166410] saa7231_free_ptable (0): SG Page table free [ 134.166414] saa7231_dmabuf_free (0): INFO: Scatterlist unmapped [ 134.166417] saa7231_dmabuf_free (0): INFO: Scatterlist free [ 134.166419] saa7231_dmabuf_sgfree (0): SG free [ 134.166424] saa7231_dmabuf_free (0): INFO: Page table free [ 134.166426] saa7231_free_ptable (0): SG Page table free [ 134.166429] saa7231_dmabuf_free (0): INFO: Scatterlist unmapped [ 134.166432] saa7231_dmabuf_free (0): INFO: Scatterlist free [ 134.166434] saa7231_dmabuf_sgfree (0): SG free [ 134.166440] saa7231_dmabuf_free (0): INFO: Page table free [ 134.166441] saa7231_free_ptable (0): SG Page table free [ 134.166445] saa7231_dmabuf_free (0): INFO: Scatterlist unmapped [ 134.166448] saa7231_dmabuf_free (0): INFO: Scatterlist free [ 134.166450] saa7231_dmabuf_sgfree (0): SG free [ 134.166455] saa7231_dmabuf_free (0): INFO: Page table free [ 134.166457] saa7231_free_ptable (0): SG Page table free [ 134.166460] saa7231_dmabuf_free (0): INFO: Scatterlist unmapped [ 134.166463] saa7231_dmabuf_free (0): INFO: Scatterlist free [ 134.166465] saa7231_dmabuf_sgfree (0): SG free [ 134.166471] saa7231_dmabuf_free (0): INFO: Page table free [ 134.166473] saa7231_free_ptable (0): SG Page table free [ 134.166476] saa7231_dmabuf_free (0): INFO: Scatterlist unmapped [ 134.166479] saa7231_dmabuf_free (0): INFO: Scatterlist free [ 134.166480] saa7231_dmabuf_sgfree (0): SG free [ 134.166486] saa7231_dmabuf_free (0): INFO: Page table free [ 134.166488] saa7231_free_ptable (0): SG Page table free [ 134.166492] saa7231_hw_free (0): DEBUG: Freeing ptable ... [ 134.166494] saa7231_capture_close (0): DEBUG: Closing stream
Regards, Manu
On Thu, 11 Nov 2010, Manu Abraham wrote:
On Wed, Nov 10, 2010 at 11:35 PM, Jaroslav Kysela perex@perex.cz wrote:
On Wed, 10 Nov 2010, Manu Abraham wrote:
On Wed, Nov 10, 2010 at 8:35 PM, Jaroslav Kysela perex@perex.cz wrote:
On Wed, 10 Nov 2010, Manu Abraham wrote:
/* enable stream */ /* initializing 8 buffer with "pages" pages each .. */ stream = saa7231_stream_init(saa7231, AUDIO_CAPTURE, ADAPTER_INT, 0, pages); 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);
Unfortunately, I don't understand the role of XS2D_BUFFERS. The ALSA bufsize is the whole DMA area (you should use params_buffer_bytes() to get this value instead of calculating this using periods * period_size).
It means: Just allocate number of pages required for buffer_bytes. Don't play with periods (except the interrupts).
There are 8 SG buffers for the hardware; the maximum size of each buffer can be 512 pages, the minimum can be a single page.
Could you describe more the whole DMA layout, including IRQ acks?
It seems to me:
SG PAGE 1 points to 1 - 512 data pages (4096 bytes long) SG PAGE 2 ..... .... SG PAGE 8 .....
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.
Jaroslav
----- Jaroslav Kysela perex@perex.cz Linux Kernel Sound Maintainer ALSA Project, Red Hat, Inc.
On Thu, Nov 11, 2010 at 4:00 PM, Jaroslav Kysela perex@perex.cz wrote:
On Thu, 11 Nov 2010, Manu Abraham wrote:
On Wed, Nov 10, 2010 at 11:35 PM, Jaroslav Kysela perex@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.
Eventually it looks thus:
static struct snd_pcm_hardware saa7231_capture_info = { .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP_VALID),
.formats = (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE | SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_U16_BE),
.rates = SNDRV_PCM_RATE_32000,
.rate_min = 32000, .rate_max = 48000,
.channels_min = 1, .channels_max = 2,
.buffer_bytes_max = XS2D_BUFFERS * PAGE_SIZE * 512,
.period_bytes_min = 4096, .period_bytes_max = 2097152,
.periods_min = 8, .periods_max = 4096, };
/* * 10mS Buffer lengths * @48kHz 1920 bytes * @44.1kHz 1764 bytes * @32kHz 1280 bytes * * * period min = 10mS @48k: 1920 bytes @44.1k: 1764 bytes @32k: 1280 bytes * period max = 1S @48k:192000 bytes @44.1k:176400 bytes @32k:128000 bytes * period = 4S @48k:768000 bytes @44.1k:705600 bytes @32k:512000 bytes */ static int saa7231_capture_open(struct snd_pcm_substream *pcm) { struct saa7231_audio *audio = snd_pcm_substream_chip(pcm); struct snd_pcm_runtime *rt = pcm->runtime; struct saa7231_dev *saa7231 = audio->saa7231; int err;
dprintk(SAA7231_DEBUG, 1, "()"); rt->hw = saa7231_capture_info; snd_pcm_hw_constraint_list(rt, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraint_rates); err = snd_pcm_hw_constraint_integer(rt, SNDRV_PCM_HW_PARAM_PERIODS); if (err < 0) { dprintk(SAA7231_ERROR, 1, "snd_pcm_hw_constraint_integer() failed. ret=%d", err); return err; }
err = snd_pcm_hw_constraint_step(rt, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 4096); if (err < 0) { dprintk(SAA7231_ERROR, 1, "snd_pcm_hw_constraint_minmax() failed. ret=%d", err); return err; } err = snd_pcm_hw_constraint_step(rt, 0, SNDRV_PCM_HW_PARAM_PERIODS, 8); if (err < 0) { dprintk(SAA7231_ERROR, 1, "snd_pcm_hw_constraint_minmax() failed. ret=%d", err); return err; }
return 0; }
/* * saa7231_hw_params() * * This callback is called when the hardware parameter (hw_params) is setup * by the application, ie., once when the buffer size, the period size, * the format etc are defined for the PCM substream. */ 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, pt_size, 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 = (bufsiz + PAGE_SIZE - 1) / PAGE_SIZE;
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));
/* * For a single DMA buffer: * each page takes a u64 size in a page table * number of entries a single page can hold = PAGE_SIZE / entry size * ie entries.max = PAGE_SIZE / 8 => 4096/8 = 512 * Now, we have entries.req = 7 ("pages") per buffer * * For all in XS2D_BUFFERS: * page table should be large enough to hold all the pages in each DMA buffer * total number of pages = pages * XS2D_BUFFERS * max buffers that we need to consider = 512 * 8, this needs 8 pages for a page table * * On a general note, we can calculate pages for page table as * page_table_size = total_pages / 512, with a minimum of a single page */
pt_size = (pages * XS2D_BUFFERS) / MAX_ENTRIES_PER_PAGE; /* minimum 1 page required for the table */ if (pt_size < 1) pt_size = 1;
dprintk(SAA7231_DEBUG, 1, "Page Table array size=%d", pt_size); ptable = kzalloc((sizeof (struct page) * pt_size), 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; }
/* * saa7231_hw_free() * * This callback is called to release the resources allocated via hw_params. * For eg: releasing the buffer via snd_pcm_lib_malloc_pages() is done by * snd_pcm_lib_free_pages(substream) * This function is always called before the close callback is called. Also, * the callback maybe called multiple times. */ static int saa7231_hw_free(struct snd_pcm_substream *pcm) { struct saa7231_audio *audio = snd_pcm_substream_chip(pcm); struct saa7231_dev *saa7231 = audio->saa7231; struct saa7231_stream *stream = audio->stream; struct page **ptable = audio->ptable; struct snd_pcm_runtime *rt = pcm->runtime; void *dma_area = rt->dma_area;
dprintk(SAA7231_DEBUG, 1, "DEBUG: Removing IRQ event .."); saa7231_remove_irqevent(saa7231, 43);
if (dma_area) { dprintk(SAA7231_DEBUG, 1, "DEBUG: Unmap Virtual memory region .."); vunmap(dma_area); } dprintk(SAA7231_DEBUG, 1, "DEBUG: Stream exiting .."); saa7231_stream_exit(stream);
dprintk(SAA7231_DEBUG, 1, "DEBUG: Freeing ptable ..."); kfree(ptable); return 0; }
/* * saa7231_capture_prepare() * * This callback is called when the PCM is prepared. You can set the format * type, sample rate, etc here. The difference from hw_params is that the * prepare callback will be called each time snd_pcm_prepare() is called, * ie, when recovering from underruns. */ static int saa7231_capture_prepare(struct snd_pcm_substream *pcm) { struct saa7231_audio *audio = snd_pcm_substream_chip(pcm); struct saa7231_dev *saa7231 = audio->saa7231; struct saa7231_stream *stream = audio->stream; struct stream_ops *ops = &stream->ops;
u32 bps, spl, pitch, buflen; int err;
dprintk(SAA7231_DEBUG, 1, "()");
bps = 16; spl = 2; pitch = spl * bps / 8; buflen = BUFSIZE_PER_XS2D(audio->bufsize);
stream->params.bps = bps; stream->params.spl = spl; stream->params.pitch = pitch; stream->params.lines = buflen / pitch; stream->params.type = STREAM_AUDIO;
dprintk(SAA7231_DEBUG, 1, "Trying to Acquire stream with %dlines per stream buffer", stream->params.lines); if (ops->acquire) { err = ops->acquire(stream); if (err) { dprintk(SAA7231_ERROR, 1, "ERROR: acquiring stream, err=%d", err); return -EIO; } } return 0; }
/* * 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; struct saa7231_stream *stream = audio->stream; struct stream_ops *ops = &stream->ops; int err;
switch (cmd) { case SNDRV_PCM_TRIGGER_START: dprintk(SAA7231_DEBUG, 1, "Tring 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, "Tring 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, "Tring 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; } return 0; }
/* * saa7231_capture_pointer() * * This callback is called when the PCM middle layer inquires the current * hardware position on the buffer. The position must be returned in frames, * ranging from 0 to buffer_size - 1. * This is usually called from the buffer-update routine in the PCM middle * layer, which is invoked when snd_pcm_period_elapsed() is called in the * interrupt routine. The PCM middle layer then updates the position and * calculates the available space, wakes up poll threads etc */ 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); }
/** * Initialiaze a stream port, for the required port */ struct saa7231_stream *saa7231_stream_init(struct saa7231_dev *saa7231, enum saa7231_mode mode, enum adapter_type adap_type, int count, int pages) { int ret; struct saa7231_stream *stream; struct saa7231_dtl *dtl;
stream = kzalloc(sizeof(struct saa7231_stream), GFP_KERNEL); if (!stream) { dprintk(SAA7231_ERROR, 1, "ERROR: Failed allocating streams"); return NULL; } stream->mode = mode; stream->saa7231 = saa7231; dtl = &stream->dtl;
dprintk(SAA7231_DEBUG, 1, "DEBUG: Initializing Stream with MODE=0x%02x", mode);
switch (mode) { case AUDIO_CAPTURE: if (count >= 1) { dprintk(SAA7231_ERROR, 1, "ERROR: Invalid Stream port, port=%d", count); return NULL; }
switch (adap_type) { case ADAPTER_INT: stream->port_id = STREAM_PORT_AS2D_LOCAL; dtl->module = AS2D_LOCAL; break; case ADAPTER_EXT: stream->port_id = STREAM_PORT_AS2D_EXTERN; dtl->module = AS2D_EXTERN; break; }
ret = saa7231_xs2dtl_init(stream, pages); if (ret < 0) { dprintk(SAA7231_ERROR, 1, "ERROR: XS2DTL engine initialization failed"); return NULL; } break; case VIDEO_CAPTURE: if (count >= 1) { dprintk(SAA7231_ERROR, 1, "ERROR: Invalid Stream port, port=%d", count); return NULL; }
switch (adap_type) { case ADAPTER_INT: stream->port_id = STREAM_PORT_VS2D_AVIS; dtl->module = VS2D_AVIS; break; case ADAPTER_EXT: stream->port_id = STREAM_PORT_VS2D_ITU; dtl->module = VS2D_ITU; break; }
ret = saa7231_vs2dtl_init(stream, pages); if (ret) { dprintk(SAA7231_ERROR, 1, "ERROR: VS2DTL engine initialization failed"); return NULL; } ret = saa7231_avis_attach(stream); if (ret) { dprintk(SAA7231_ERROR, 1, "ERROR: AVIS initialization, attach() failed"); return NULL; } break; case VBI_CAPTURE: if (count >= 1) { dprintk(SAA7231_ERROR, 1, "ERROR: Invalid Stream port, port=%d", count); return NULL; }
switch (adap_type) { case ADAPTER_INT: stream->port_id = STREAM_PORT_DS2D_AVIS; dtl->module = DS2D_AVIS; break; case ADAPTER_EXT: stream->port_id = STREAM_PORT_DS2D_ITU; dtl->module = DS2D_ITU; break; }
ret = saa7231_xs2dtl_init(stream, pages); if (ret) { dprintk(SAA7231_ERROR, 1, "ERROR: XS2DTL engine initialization failed"); return NULL; } break; case DIGITAL_CAPTURE: if (count >= 2) { dprintk(SAA7231_ERROR, 1, "ERROR: Invalid Stream port, port=%d", count); return NULL; }
switch (adap_type) { case ADAPTER_INT: stream->port_id = STREAM_PORT_TS2D_DTV0 + count; dtl->module = TS2D0_DTV + (count * 0x1000); break; case ADAPTER_EXT: stream->port_id = STREAM_PORT_TS2D_EXTERN0 + count; dtl->module = TS2D0_EXTERN + (count * 0x1000); break; }
ret = saa7231_xs2dtl_init(stream, pages); if (ret) { dprintk(SAA7231_ERROR, 1, "ERROR: XS2DTL engine initialization failed"); return NULL; } BUG_ON(stream->ops.acquire == NULL); stream->params.bps = 8; stream->params.spl = 188; stream->params.lines = 348; stream->params.pitch = 188; stream->params.thrsh = 0; stream->params.flags = 0; stream->params.type = STREAM_TS; ret = stream->ops.acquire(stream); if (ret < 0) { dprintk(SAA7231_ERROR, 1, "TS setup failed, ret=%d", ret); return NULL; } break; default: dprintk(SAA7231_ERROR, 1, "ERROR: Unknown mode=0x%02x", mode); BUG_ON(1); break; }
/* * Stream port starts at 0, but the related DMA channel is offset * by +1, since MSI uses DMA channel 0. We store this offset as a * mask */ dprintk(SAA7231_ERROR, 1, "INFO: Initialized MODE:0x%02x for PORT:%d", mode, stream->port_id);
stream->init = 1; return stream; }
int saa7231_xs2dtl_init(struct saa7231_stream *stream, int pages) { int i, ret; struct stream_ops *ops = &stream->ops; struct saa7231_dev *saa7231 = stream->saa7231;
struct saa7231_dmabuf *dmabuf;
dprintk(SAA7231_DEBUG, 1, "XS2DTL engine Initializing ....."); /** * Note: the stream port array starts at 0 but the related DMA * channel at dwDMAChannel +1, because MSI uses channel 0. * Store DMA enumeration but keep "+1" in channel mask! */ switch (stream->port_id) { case STREAM_PORT_DS2D_AVIS: case STREAM_PORT_DS2D_ITU: stream->port_hw = STREAM_VBI; stream->config = 0; break;
case STREAM_PORT_AS2D_LOCAL: case STREAM_PORT_AS2D_EXTERN: stream->port_hw = STREAM_AUDIO; stream->config = 0x4; /* IIS stream */ break;
case STREAM_PORT_TS2D_DTV0: case STREAM_PORT_TS2D_DTV1: stream->port_hw = STREAM_TS; stream->config = 0x0; break;
case STREAM_PORT_TS2D_EXTERN0: case STREAM_PORT_TS2D_EXTERN1: stream->port_hw = STREAM_TS; stream->config = 0x11; /* Parallel, Port ON */ break;
case STREAM_PORT_TS2D_CAM: stream->port_hw = STREAM_TS; stream->config = 0x0181; break;
default: dprintk(SAA7231_ERROR, 1, "ERROR: Invalid DMA setup!"); stream->config = 0; return -EINVAL; } /* Allocate space for n XS2D Buffers */ dmabuf = kzalloc(sizeof (struct saa7231_dmabuf) * XS2D_BUFFERS, GFP_KERNEL); if (!dmabuf) { dprintk(SAA7231_ERROR, 1, "ERROR: Allocating %d XS2D Buffers", XS2D_BUFFERS); ret = -ENOMEM; } dprintk(SAA7231_DEBUG, 1, "Allocated %d XS2DTL Buffer @0x%p", XS2D_BUFFERS, dmabuf); stream->dmabuf = dmabuf;
dprintk(SAA7231_DEBUG, 1, "Allocating DMA Buffers ..."); for (i = 0; i < XS2D_BUFFERS; i++) { dprintk(SAA7231_DEBUG, 1, "Allocating DMA buffer:%d @0x%p..", i, &dmabuf[i]); ret = saa7231_dmabuf_alloc(saa7231, &dmabuf[i], pages); if (ret < 0) { dprintk(SAA7231_ERROR, 1, "ERROR: Failed allocating DMA buffers, error=%d", ret); return -ENOMEM; } } dprintk(SAA7231_DEBUG, 1, "Initializing PTA ..."); saa7231_init_ptables(stream);
dprintk(SAA7231_DEBUG, 1, "Setting up PORT Gates ..."); /* deactivate gating */ switch (stream->port_id) { case STREAM_PORT_DS2D_AVIS: SAA7231_WR(0, SAA7231_BAR0, STREAM, STREAM_RA_VBI_LOC_CLGATE); break; case STREAM_PORT_DS2D_ITU: SAA7231_WR(0, SAA7231_BAR0, STREAM, STREAM_RA_VIDEO_EXT_CLGATE); break; case STREAM_PORT_AS2D_LOCAL: SAA7231_WR(0, SAA7231_BAR0, STREAM, STREAM_RA_AUDIO_LOC_CLGATE); break; case STREAM_PORT_AS2D_EXTERN: SAA7231_WR(0, SAA7231_BAR0, STREAM, STREAM_RA_AUDIO_EXT_CLGATE); break; case STREAM_PORT_TS2D_DTV0: SAA7231_WR(0, SAA7231_BAR0, STREAM, STREAM_RA_TS0_LOC_CLGATE); break; case STREAM_PORT_TS2D_DTV1: SAA7231_WR(0, SAA7231_BAR0, STREAM, STREAM_RA_TS1_LOC_CLGATE); break; case STREAM_PORT_TS2D_EXTERN0: SAA7231_WR(0, SAA7231_BAR0, STREAM, STREAM_RA_TS0_EXT_CLGATE); break; case STREAM_PORT_TS2D_EXTERN1: SAA7231_WR(0, SAA7231_BAR0, STREAM, STREAM_RA_TS1_EXT_CLGATE); break; case STREAM_PORT_TS2D_CAM: SAA7231_WR(0, SAA7231_BAR0, STREAM, STREAM_RA_TSCA_EXT_CLGATE); SAA7231_WR(0, SAA7231_BAR0, STREAM, STREAM_RA_TSMUX_CLGATE);
/* TODO! disable clock usage here */ break; default: return -EINVAL; }
ops->acquire = saa7231_xs2dtl_acquire; ops->run = saa7231_xs2dtl_run; ops->pause = saa7231_xs2dtl_pause; ops->stop = saa7231_xs2dtl_stop;
ops->set_buffer = saa7231_xs2dtl_set_buffer; ops->get_buffer = saa7231_xs2dtl_get_buffer;
ops->exit = saa7231_xs2dtl_exit;
return 0; }
/** * Allocates one page of memory, which is stores the data of one * 7231 page table. The result gets stored in the passed DMA buffer * structure. */ static int saa7231_allocate_ptable(struct saa7231_dmabuf *dmabuf) { struct saa7231_dev *saa7231 = dmabuf->saa7231; struct pci_dev *pdev = saa7231->pdev; void *virt; dma_addr_t phys;
virt = (void *) __get_free_page(GFP_KERNEL); if (!virt) { dprintk(SAA7231_ERROR, 1, "ERROR: Out of pages !"); return -ENOMEM; }
/* * TODO! * A conversion from virtual to physical is sufficient alone ? * phys = __pa(virt) * Or should we do DMA to the Page Table themselves ? * In some some chip versions it seems we must manually copy the * Page Tables, as Page Table DMA doesn't work ?? * Need to figure this out, as people who are really aware of this * concept, can no longer be reached */ phys = dma_map_single(&pdev->dev, virt, SAA7231_PAGE_SIZE, DMA_FROM_DEVICE);
dprintk(SAA7231_DEBUG, 1, "Virtual 0x%p to Phys 0x%04x mapped page", virt, phys);
if (!phys) { dprintk(SAA7231_ERROR, 1, "ERROR: map memory failed !"); return -ENOMEM; }
BUG_ON((unsigned long) phys % SAA7231_PAGE_SIZE); dmabuf->pt_phys = phys; dmabuf->pt_virt = virt; return 0; }
/** * Create a SG, when an allocated buffer is passed to it, * otherwise the needed memory gets allocated by itself */ static int saa7231_dmabuf_sgalloc(struct saa7231_dmabuf *dmabuf, void *buf, int pages) { struct saa7231_dev *saa7231 = dmabuf->saa7231; struct scatterlist *sg_list; struct page *pg; void *vma = NULL, *mem = NULL;
int i;
BUG_ON(!pages); BUG_ON(!dmabuf);
/* Allocate memory for SG list */ sg_list = kzalloc(sizeof (struct scatterlist) * pages , GFP_KERNEL); if (!sg_list) { dprintk(SAA7231_ERROR, 1, "Failed to allocate memory for scatterlist."); return -ENOMEM; } sg_init_table(sg_list, pages);
if (!buf) { /* allocate memory, unaligned, virtually contiguous memory region */ mem = vmalloc((pages + 1) * SAA7231_PAGE_SIZE); if (!mem) { dprintk(SAA7231_ERROR, 1, "ERROR: vmalloc failed allocating %d pages", pages); return -ENOMEM; } dprintk(SAA7231_INFO, 1, "Virtual contiguous %d byte region with %d %dk pages", pages * SAA7231_PAGE_SIZE, pages, (SAA7231_PAGE_SIZE / 1024));
/* align memory to page */ vma = (void *) PAGE_ALIGN (((unsigned long) mem)); BUG_ON(((unsigned long) vma) % SAA7231_PAGE_SIZE); } else { dprintk(SAA7231_DEBUG, 1, "DEBUG: Request to add %d pages to SG list", pages); vma = buf; }
for (i = 0; i < pages; i++) { if (!buf) pg = vmalloc_to_page(vma + i * SAA7231_PAGE_SIZE); else pg = virt_to_page(vma + i * SAA7231_PAGE_SIZE);
BUG_ON(!pg);
sg_set_page(&sg_list[i], pg, SAA7231_PAGE_SIZE, 0); } dmabuf->sg_list = sg_list; dmabuf->virt = vma; dmabuf->vmalloc = mem; dmabuf->pages = pages; /* scatterlist length */
return 0; }
/* Allocates a DMA buffer for the specified external linear buffer. */ int saa7231_dmabuf_alloc(struct saa7231_dev *saa7231, struct saa7231_dmabuf *dmabuf, int size) { struct pci_dev *pdev = saa7231->pdev;
int ret;
BUG_ON(!saa7231); BUG_ON(!dmabuf); BUG_ON(! (size > 0));
dmabuf->dma_type = SAA7231_DMABUF_INT;
dmabuf->vmalloc = NULL; dmabuf->virt = NULL; dmabuf->pt_phys = 0; dmabuf->pt_virt = NULL;
dmabuf->pages = 0; dmabuf->saa7231 = saa7231;
/* Allocate page table */ ret = saa7231_allocate_ptable(dmabuf); if (ret) { dprintk(SAA7231_ERROR, 1, "PT alloc failed, Out of memory"); goto err1; }
/* Allocate buffer as SG */ ret = saa7231_dmabuf_sgalloc(dmabuf, NULL, size); if (ret) { /* size is in pages */ dprintk(SAA7231_ERROR, 1, "Request FAILED! for Virtual contiguous %d byte region with %d %dk pages", size * SAA7231_PAGE_SIZE, size, (SAA7231_PAGE_SIZE / 1024)); goto err2; }
ret = dma_map_sg(&pdev->dev, dmabuf->sg_list, dmabuf->pages, DMA_FROM_DEVICE);
if (ret < 0) { dprintk(SAA7231_ERROR, 1, "SG map failed, ret=%d", ret); goto err3; }
saa7231_dmabuf_sgpagefill(dmabuf, dmabuf->sg_list, ret, 0);
return 0; err3: saa7231_dmabuf_sgfree(dmabuf); err2: saa7231_free_ptable(dmabuf); err1: return ret; }
/* Fill the "page table" page with the pointers to the specified SG buffer */ static void saa7231_dmabuf_sgpagefill(struct saa7231_dmabuf *dmabuf, struct scatterlist *sg_list, int pages, int offset) { struct saa7231_dev *saa7231 = dmabuf->saa7231; struct pci_dev *pdev = saa7231->pdev; struct scatterlist *sg_cur;
u32 *page; int i, j, k = 0; dma_addr_t addr = 0;
BUG_ON(!dmabuf); BUG_ON(!sg_list); BUG_ON(!pages);
/* make page writable for the PC */ dma_sync_single_for_cpu(&pdev->dev, dmabuf->pt_phys, SAA7231_PAGE_SIZE, DMA_FROM_DEVICE);
page = dmabuf->pt_virt;
/* create page table */ for (i = 0; i < pages; i++) { sg_cur = &sg_list[i];
// BUG_ON(!((sg_cur->length + sg_cur->offset) % SAA7231_PAGE_SIZE));
if (i == 0) dmabuf->offset = (sg_cur->length + sg_cur->offset) % SAA7231_PAGE_SIZE; else BUG_ON(sg_cur->offset != 0);
for (j = 0; (j * SAA7231_PAGE_SIZE) < sg_dma_len(sg_cur); j++) {
if ((offset + sg_cur->offset) >= SAA7231_PAGE_SIZE) { offset -= SAA7231_PAGE_SIZE; continue; }
addr = ((u64 )sg_dma_address(sg_cur)) + (j * SAA7231_PAGE_SIZE) - sg_cur->offset;
BUG_ON(!addr);
if (!(i % 8) && !(i == 0)) dprintk(SAA7231_DEBUG, 0, "\n "); if ((i % 4) == 0) dprintk(SAA7231_DEBUG, 0, " "); if (i == 0) dprintk(SAA7231_DEBUG, 0, " ");
dprintk(SAA7231_DEBUG, 0, "%04x ", addr);
page[k * 2 + 0] = ADDR_LSB(addr); /* Low */ page[k * 2 + 1] = ADDR_MSB(addr); /* High */
BUG_ON(page[k * 2] % SAA7231_PAGE_SIZE); k++; } }
for (; k < (SAA7231_PAGE_SIZE / 8); k++) { page[k * 2 + 0] = ADDR_LSB(addr); page[k * 2 + 1] = ADDR_MSB(addr); }
/* make "page table" page writable for the PC */ dma_sync_single_for_device(&pdev->dev, dmabuf->pt_phys, SAA7231_PAGE_SIZE, DMA_FROM_DEVICE);
}
testbox ~ # arecord -D plughw:2,0 --format S16_LE --rate=32000 -c 2 > ~/test_capture.wav Recording WAVE 'stdin' : Signed 16 bit Little Endian, Rate 32000 Hz, Stereo arecord: pcm_read:1617: read error: Input/output error
[ 103.349513] SAA7231 0000:05:00.0: PCI INT A -> GSI 16 (level, low) -> IRQ 16 [ 103.349521] SAA7231 0000:05:00.0: setting latency timer to 64 [ 103.349544] saa7231_pci_init (0): BAR 0 Offset: 04 BAR 2 Offset: 04 [ 103.349547] saa7231_pci_init (0): BAR0 Start=fc800000 length=4M [ 103.349549] saa7231_pci_init (0): BAR2 Start=fc400000 length=4M [ 103.351180] SAA7231GE [14c7:3595], irq: 16, [ 103.351183] mmio(0): 0xf8780000 mmio(2): 0xf8c00000 [ 103.351197] SAA7231 64Bit, MSI Disabled, MSI-X=1 msgs [ 103.351203] saa7231_get_version (0): SAA7231 PCI Express V1A found [ 103.351205] saa7231_cgu_init (0): Initializing CGU [ 103.351207] saa7231_cgu_init (0): Using 54MHz RootClock [ 103.351228] saa7231_cgu_init (0): PLL Status CDEC160: 00 REF550: 00 ADPLL: 00 DSP: 00 [ 103.351231] saa7231_set_clk (0): clk_output=0 clk_frequency=2 [ 103.351232] saa7231_set_clk (0): clk_output=1 clk_frequency=2 [ 103.351234] saa7231_set_clk (0): clk_output=2 clk_frequency=2 [ 103.351236] saa7231_set_clk (0): clk_output=3 clk_frequency=2 [ 103.351238] saa7231_set_clk (0): clk_output=4 clk_frequency=2 [ 103.351239] saa7231_set_clk (0): clk_output=5 clk_frequency=2 [ 103.351241] saa7231_set_clk (0): clk_output=6 clk_frequency=2 [ 103.351243] saa7231_set_clk (0): clk_output=7 clk_frequency=2 [ 103.351244] saa7231_set_clk (0): clk_output=8 clk_frequency=2 [ 103.351246] saa7231_set_clk (0): clk_output=9 clk_frequency=2 [ 103.351248] saa7231_set_clk (0): clk_output=10 clk_frequency=2 [ 103.351250] saa7231_set_clk (0): clk_output=11 clk_frequency=2 [ 103.351251] saa7231_set_clk (0): clk_output=12 clk_frequency=2 [ 103.351253] saa7231_set_clk (0): clk_output=13 clk_frequency=2 [ 103.351255] saa7231_set_clk (0): clk_output=14 clk_frequency=2 [ 103.351257] saa7231_set_clk (0): clk_output=15 clk_frequency=2 [ 103.351259] saa7231_set_clk (0): clk_output=16 clk_frequency=2 [ 103.351260] saa7231_set_clk (0): clk_output=17 clk_frequency=2 [ 103.351262] saa7231_set_clk (0): clk_output=18 clk_frequency=2 [ 103.402026] saa7231_cgu_init (0): DEBUG: RGU_RESET_ACTIVE0 7c02001f [ 103.504025] saa7231_cgu_init (0): DEBUG: RGU_RESET_ACTIVE0 ffffffff [ 103.504029] saa7231_cgu_init (0): DEBUG: RGU_RESET_ACTIVE1 2e1f [ 103.504031] saa7231_set_clk (0): clk_output=17 clk_frequency=0 [ 103.510052] saa7231_i2c_init (0): Initializing SAA7231 I2C Core [ 103.510056] saa7231_i2c_init (0): Initializing adapter (0) SAA7231 I2C:0 [ 103.510254] saa7231_i2c_hwinit (0): Adapter (109000) SAA7231 I2C:0 RESET [ 103.510256] saa7231_i2c_hwinit (0): Initializing Adapter SAA7231 I2C:0 @ 100k [ 103.510264] saa7231_i2c_init (0): Initializing adapter (1) SAA7231 12C:1 [ 103.510526] saa7231_i2c_hwinit (0): Adapter (10a000) SAA7231 12C:1 RESET [ 103.510528] saa7231_i2c_hwinit (0): Initializing Adapter SAA7231 12C:1 @ 100k [ 103.510536] saa7231_i2c_init (0): Initializing adapter (2) SAA7231 I2C:2 [ 103.510727] saa7231_i2c_hwinit (0): Adapter (10b000) SAA7231 I2C:2 RESET [ 103.510730] saa7231_i2c_hwinit (0): Initializing Adapter SAA7231 I2C:2 @ 100k [ 103.510738] saa7231_i2c_init (0): Initializing adapter (3) SAA7231 I2C:3 [ 103.510872] saa7231_i2c_hwinit (0): Adapter (10c000) SAA7231 I2C:3 RESET [ 103.510874] saa7231_i2c_hwinit (0): Initializing Adapter SAA7231 I2C:3 @ 100k [ 103.510882] saa7231_i2c_init (0): SAA7231 I2C Core succesfully initialized [ 103.510884] saa7231_alsa_init (0): Initializing Audio .. [ 103.518765] saa7231_pci_probe (0): SAA7231 device:0 initialized [ 122.183642] saa7231_capture_open (0): () [ 122.184422] saa7231_hw_params (0): DEBUG: () [ 122.184425] saa7231_hw_params (0): bufsiz=131072 periods=8 bytes=16384 pages=32 [ 122.184428] saa7231_stream_init (0): DEBUG: Initializing Stream with MODE=0x01 [ 122.184430] saa7231_xs2dtl_init (0): XS2DTL engine Initializing ..... [ 122.184433] saa7231_xs2dtl_init (0): Allocated 8 XS2DTL Buffer @0xf52d3200 [ 122.184435] saa7231_xs2dtl_init (0): Allocating DMA Buffers ... [ 122.184437] saa7231_xs2dtl_init (0): Allocating DMA buffer:0 @0xf52d3200.. [ 122.184447] saa7231_allocate_ptable (0): Virtual 0xf53f5000 to Phys 0x353f5000 mapped page [ 122.184464] saa7231_dmabuf_sgalloc (0): Virtual contiguous 16384 byte region with 4 4k pages [ 122.184487] 6c2f1000 6c277000 6c292000 6c377000 [ 122.184493] saa7231_xs2dtl_init (0): Allocating DMA buffer:1 @0xf52d3228.. [ 122.184501] saa7231_allocate_ptable (0): Virtual 0xf53e7000 to Phys 0x353e7000 mapped page [ 122.184507] saa7231_dmabuf_sgalloc (0): Virtual contiguous 16384 byte region with 4 4k pages [ 122.184527] 6c49f000 6c28d000 6c31b000 6c2e8000 [ 122.184532] saa7231_xs2dtl_init (0): Allocating DMA buffer:2 @0xf52d3250.. [ 122.184539] saa7231_allocate_ptable (0): Virtual 0xf4014000 to Phys 0x34014000 mapped page [ 122.184545] saa7231_dmabuf_sgalloc (0): Virtual contiguous 16384 byte region with 4 4k pages [ 122.184564] 6c480000 6c481000 6c31c000 6c31d000 [ 122.184569] saa7231_xs2dtl_init (0): Allocating DMA buffer:3 @0xf52d3278.. [ 122.184577] saa7231_allocate_ptable (0): Virtual 0xf42bb000 to Phys 0x342bb000 mapped page [ 122.184582] saa7231_dmabuf_sgalloc (0): Virtual contiguous 16384 byte region with 4 4k pages [ 122.184602] 6c31f000 6c2f8000 6c2f9000 6c2fa000 [ 122.184606] saa7231_xs2dtl_init (0): Allocating DMA buffer:4 @0xf52d32a0.. [ 122.184614] saa7231_allocate_ptable (0): Virtual 0xf42ba000 to Phys 0x342ba000 mapped page [ 122.184622] saa7231_dmabuf_sgalloc (0): Virtual contiguous 16384 byte region with 4 4k pages [ 122.184641] 6c2fc000 6c2fd000 6c2fe000 6c2ff000 [ 122.184646] saa7231_xs2dtl_init (0): Allocating DMA buffer:5 @0xf52d32c8.. [ 122.184654] saa7231_allocate_ptable (0): Virtual 0xf42b9000 to Phys 0x342b9000 mapped page [ 122.184659] saa7231_dmabuf_sgalloc (0): Virtual contiguous 16384 byte region with 4 4k pages [ 122.184678] 6c301000 6c302000 6c303000 6c304000 [ 122.184683] saa7231_xs2dtl_init (0): Allocating DMA buffer:6 @0xf52d32f0.. [ 122.184690] saa7231_allocate_ptable (0): Virtual 0xf42b8000 to Phys 0x342b8000 mapped page [ 122.184695] saa7231_dmabuf_sgalloc (0): Virtual contiguous 16384 byte region with 4 4k pages [ 122.184714] 6c306000 6c307000 6c308000 6c309000 [ 122.184719] saa7231_xs2dtl_init (0): Allocating DMA buffer:7 @0xf52d3318.. [ 122.184726] saa7231_allocate_ptable (0): Virtual 0xf42b7000 to Phys 0x342b7000 mapped page [ 122.184732] saa7231_dmabuf_sgalloc (0): Virtual contiguous 16384 byte region with 4 4k pages [ 122.184751] 6c30b000 6c30c000 6c30d000 6c30e000 [ 122.184756] saa7231_xs2dtl_init (0): Initializing PTA ... [ 122.184758] saa7231_init_ptables (0): DEBUG: Initializing PORT:6 DMA_CH:7 with 8 Buffers [ 122.184760] saa7231_xs2dtl_init (0): Setting up PORT Gates ... [ 122.184762] saa7231_stream_init (0): INFO: Initialized MODE:0x01 for PORT:6 [ 122.184765] saa7231_add_irqevent (0): Adding AS2D_AVIS IRQ Event:43 ... [ 122.184767] saa7231_setup_vector (0): Adding Vector:43 [ 122.184771] saa7231_setup_vector (0): Enabling Vector:43 [ 122.184775] saa7231_add_irqevent (0): Succesfully added AS2D_AVIS as Event handler:43 [ 122.184777] saa7231_hw_params (0): Mapping 8 buffers with 4 pages each [ 122.184778] saa7231_hw_params (0): Page Table array size=1 [ 122.184780] saa7231_hw_params (0): Mapping Page:0 from XS2D_BUFFER:0 to PTA Offset:0 [ 122.184782] saa7231_hw_params (0): Mapping Page:1 from XS2D_BUFFER:0 to PTA Offset:1 [ 122.184784] saa7231_hw_params (0): Mapping Page:2 from XS2D_BUFFER:0 to PTA Offset:2 [ 122.184786] saa7231_hw_params (0): Mapping Page:3 from XS2D_BUFFER:0 to PTA Offset:3 [ 122.184788] saa7231_hw_params (0): Mapping Page:0 from XS2D_BUFFER:1 to PTA Offset:4 [ 122.184790] saa7231_hw_params (0): Mapping Page:1 from XS2D_BUFFER:1 to PTA Offset:5 [ 122.184791] saa7231_hw_params (0): Mapping Page:2 from XS2D_BUFFER:1 to PTA Offset:6 [ 122.184793] saa7231_hw_params (0): Mapping Page:3 from XS2D_BUFFER:1 to PTA Offset:7 [ 122.184795] saa7231_hw_params (0): Mapping Page:0 from XS2D_BUFFER:2 to PTA Offset:8 [ 122.184797] saa7231_hw_params (0): Mapping Page:1 from XS2D_BUFFER:2 to PTA Offset:9 [ 122.184799] saa7231_hw_params (0): Mapping Page:2 from XS2D_BUFFER:2 to PTA Offset:10 [ 122.184801] saa7231_hw_params (0): Mapping Page:3 from XS2D_BUFFER:2 to PTA Offset:11 [ 122.184803] saa7231_hw_params (0): Mapping Page:0 from XS2D_BUFFER:3 to PTA Offset:12 [ 122.184805] saa7231_hw_params (0): Mapping Page:1 from XS2D_BUFFER:3 to PTA Offset:13 [ 122.184807] saa7231_hw_params (0): Mapping Page:2 from XS2D_BUFFER:3 to PTA Offset:14 [ 122.184809] saa7231_hw_params (0): Mapping Page:3 from XS2D_BUFFER:3 to PTA Offset:15 [ 122.184810] saa7231_hw_params (0): Mapping Page:0 from XS2D_BUFFER:4 to PTA Offset:16 [ 122.184812] saa7231_hw_params (0): Mapping Page:1 from XS2D_BUFFER:4 to PTA Offset:17 [ 122.184814] saa7231_hw_params (0): Mapping Page:2 from XS2D_BUFFER:4 to PTA Offset:18 [ 122.184816] saa7231_hw_params (0): Mapping Page:3 from XS2D_BUFFER:4 to PTA Offset:19 [ 122.184818] saa7231_hw_params (0): Mapping Page:0 from XS2D_BUFFER:5 to PTA Offset:20 [ 122.184820] saa7231_hw_params (0): Mapping Page:1 from XS2D_BUFFER:5 to PTA Offset:21 [ 122.184822] saa7231_hw_params (0): Mapping Page:2 from XS2D_BUFFER:5 to PTA Offset:22 [ 122.184824] saa7231_hw_params (0): Mapping Page:3 from XS2D_BUFFER:5 to PTA Offset:23 [ 122.184826] saa7231_hw_params (0): Mapping Page:0 from XS2D_BUFFER:6 to PTA Offset:24 [ 122.184828] saa7231_hw_params (0): Mapping Page:1 from XS2D_BUFFER:6 to PTA Offset:25 [ 122.184830] saa7231_hw_params (0): Mapping Page:2 from XS2D_BUFFER:6 to PTA Offset:26 [ 122.184832] saa7231_hw_params (0): Mapping Page:3 from XS2D_BUFFER:6 to PTA Offset:27 [ 122.184834] saa7231_hw_params (0): Mapping Page:0 from XS2D_BUFFER:7 to PTA Offset:28 [ 122.184835] saa7231_hw_params (0): Mapping Page:1 from XS2D_BUFFER:7 to PTA Offset:29 [ 122.184837] saa7231_hw_params (0): Mapping Page:2 from XS2D_BUFFER:7 to PTA Offset:30 [ 122.184839] saa7231_hw_params (0): Mapping Page:3 from XS2D_BUFFER:7 to PTA Offset:31 [ 122.184864] saa7231_capture_prepare (0): () [ 122.184866] saa7231_capture_prepare (0): Trying to Acquire stream with 4096lines per stream buffer [ 122.184868] saa7231_xs2dtl_acquire (0): Activating clock .. mode=0x01, port_id=0x06 [ 122.184871] saa7231_activate_clocks (0): DEBUG: Activating Clock for Mode=0x01, port=0x06 [ 122.184873] tmGetClockInstance (0): DEBUG: Mode=0x01, dmaport=0x06 [ 122.184875] tmGetClockInstance (0): ret=0 [ 122.184880] saa7231_activate_clocks (0): INFO: activate use case index 0 [ 122.184882] saa7231_init_ptables (0): DEBUG: Initializing PORT:6 DMA_CH:7 with 8 Buffers [ 122.190510] saa7231_capture_trigger (0): Tring to START stream, cmd=1 [ 122.201045] [ 122.201047] ================================= [ 122.201050] [ INFO: inconsistent lock state ] [ 122.201052] 2.6.34.7 #2 [ 122.201053] --------------------------------- [ 122.201055] inconsistent {IN-HARDIRQ-W} -> {HARDIRQ-ON-W} usage. [ 122.201058] arecord/5860 [HC0[0]:SC0[0]:HE1:SE1] takes: [ 122.201060] (&(&substream->self_group.lock)->rlock){?.-...}, at: [<c13271b5>] snd_pcm_stream_lock_irq+0x20/0x23 [ 122.201068] {IN-HARDIRQ-W} state was registered at: [ 122.201069] [<c105a27c>] __lock_acquire+0x23e/0xb47 [ 122.201074] [<c105ac11>] lock_acquire+0x8c/0xab [ 122.201077] [<c144a8bb>] _raw_spin_lock+0x20/0x2f [ 122.201081] [<c132811e>] snd_pcm_period_elapsed+0x39/0xb4 [ 122.201084] [<f843040f>] azx_interrupt+0x8e/0x106 [snd_hda_intel] [ 122.201090] [<c1079a16>] handle_IRQ_event+0x4b/0xf1 [ 122.201093] [<c107b469>] handle_fasteoi_irq+0x86/0xbd [ 122.201096] irq event stamp: 4520 [ 122.201098] hardirqs last enabled at (4519): [<c100287b>] sysenter_exit+0xf/0x16 [ 122.201101] hardirqs last disabled at (4520): [<c144ac7b>] _raw_read_lock_irq+0x11/0x35 [ 122.201105] softirqs last enabled at (3784): [<c1039b96>] __do_softirq+0x15a/0x169 [ 122.201108] softirqs last disabled at (3757): [<c10042fc>] do_softirq+0x63/0xaf [ 122.201111] [ 122.201111] other info that might help us debug this: [ 122.201113] 2 locks held by arecord/5860: [ 122.201115] #0: (snd_pcm_link_rwlock){.?.-..}, at: [<c13271aa>] snd_pcm_stream_lock_irq+0x15/0x23 [ 122.201120] #1: (&(&substream->self_group.lock)->rlock){?.-...}, at: [<c13271b5>] snd_pcm_stream_lock_irq+0x20/0x23 [ 122.201124] [ 122.201125] stack backtrace: [ 122.201127] Pid: 5860, comm: arecord Not tainted 2.6.34.7 #2 [ 122.201129] Call Trace: [ 122.201132] [<c1448b56>] ? printk+0x14/0x16 [ 122.201134] [<c105938f>] valid_state+0x12a/0x13d [ 122.201137] [<c1059493>] mark_lock+0xf1/0x1d9 [ 122.201140] [<c1059b3c>] ? check_usage_backwards+0x0/0x68 [ 122.201142] [<c10595be>] mark_held_locks+0x43/0x5b [ 122.201145] [<c144ad98>] ? _raw_spin_unlock_irq+0x27/0x2b [ 122.201148] [<c10597d7>] trace_hardirqs_on_caller+0xe7/0x121 [ 122.201151] [<c105981c>] trace_hardirqs_on+0xb/0xd [ 122.201153] [<c144ad98>] _raw_spin_unlock_irq+0x27/0x2b [ 122.201157] [<c102d6ac>] finish_task_switch+0x5c/0x86 [ 122.201160] [<c102d650>] ? finish_task_switch+0x0/0x86 [ 122.201162] [<c144925d>] schedule+0x53d/0x5b4 [ 122.201165] [<c14495ec>] schedule_timeout+0x7e/0x9b [ 122.201169] [<c103f3ea>] ? process_timeout+0x0/0xf [ 122.201172] [<c1449623>] schedule_timeout_uninterruptible+0x1a/0x1c [ 122.201175] [<c103f8e9>] msleep+0x15/0x1b [ 122.201183] [<f83b5974>] saa7231_xs2dtl_run+0x93/0x123 [saa7231_core] [ 122.201190] [<f83b9749>] saa7231_capture_trigger+0x5c/0x158 [saa7231_core] [ 122.201193] [<c13226e3>] snd_pcm_do_start+0x23/0x26 [ 122.201196] [<c132263a>] snd_pcm_action_single+0x2a/0x50 [ 122.201199] [<c1323ce8>] snd_pcm_action+0x6f/0x7b [ 122.201202] [<c1323d8a>] snd_pcm_start+0x19/0x1b [ 122.201204] [<c13283a0>] snd_pcm_lib_read1+0x61/0x1ff [ 122.201207] [<c13285be>] snd_pcm_lib_read+0x33/0x54 [ 122.201210] [<c13273e5>] ? snd_pcm_lib_read_transfer+0x0/0x70 [ 122.201213] [<c1325ec1>] snd_pcm_capture_ioctl1+0x8e/0x2ed [ 122.201215] [<c1326148>] snd_pcm_capture_ioctl+0x28/0x35 [ 122.201219] [<c10c7ec5>] vfs_ioctl+0x2c/0x96 [ 122.201222] [<c1326120>] ? snd_pcm_capture_ioctl+0x0/0x35 [ 122.201224] [<c10c843f>] do_vfs_ioctl+0x46c/0x4aa [ 122.201228] [<c10bd0f3>] ? fsnotify_modify+0x54/0x5f [ 122.201231] [<c10bd1fe>] ? do_sync_write+0x0/0xca [ 122.201234] [<c10be3e3>] ? fget_light+0xe/0xaf [ 122.201236] [<c10c84b0>] sys_ioctl+0x33/0x4d [ 122.201239] [<c100284c>] sysenter_do_call+0x12/0x32 [ 133.191022] saa7231_xs2dtl_run (0): ERROR, Preload PTA failed [ 133.191027] saa7231_capture_trigger (0): ERROR: starting stream, err=-22 [ 133.191030] saa7231_capture_trigger (0): Tring to STOP stream, cmd=0 [ 133.191179] saa7231_hw_free (0): DEBUG: Removing IRQ event .. [ 133.191195] saa7231_remove_irqevent (0): Removing IRQ Event:43 ...... [ 133.191225] saa7231_remove_irqevent (0): IRQ Event 43 <AS2D_AVIS> removed [ 133.191229] saa7231_hw_free (0): DEBUG: Unmap Virtual memory region .. [ 133.191241] saa7231_hw_free (0): DEBUG: Stream exiting .. [ 133.191244] saa7231_stream_exit (0): INFO: Freeing MODE:0x01 for PORT=0x06 [ 133.191247] saa7231_xs2dtl_exit (0): Free XS2DTL engine .. [ 133.191249] saa7231_dmabuf_free (0): INFO: Scatterlist unmapped [ 133.191253] saa7231_dmabuf_free (0): INFO: Scatterlist free [ 133.191255] saa7231_dmabuf_sgfree (0): SG free [ 133.191261] saa7231_dmabuf_free (0): INFO: Page table free [ 133.191263] saa7231_free_ptable (0): SG Page table free [ 133.191267] saa7231_dmabuf_free (0): INFO: Scatterlist unmapped [ 133.191271] saa7231_dmabuf_free (0): INFO: Scatterlist free [ 133.191273] saa7231_dmabuf_sgfree (0): SG free [ 133.191278] saa7231_dmabuf_free (0): INFO: Page table free [ 133.191280] saa7231_free_ptable (0): SG Page table free [ 133.191284] saa7231_dmabuf_free (0): INFO: Scatterlist unmapped [ 133.191288] saa7231_dmabuf_free (0): INFO: Scatterlist free [ 133.191289] saa7231_dmabuf_sgfree (0): SG free [ 133.191295] saa7231_dmabuf_free (0): INFO: Page table free [ 133.191296] saa7231_free_ptable (0): SG Page table free [ 133.191300] saa7231_dmabuf_free (0): INFO: Scatterlist unmapped [ 133.191304] saa7231_dmabuf_free (0): INFO: Scatterlist free [ 133.191305] saa7231_dmabuf_sgfree (0): SG free [ 133.191311] saa7231_dmabuf_free (0): INFO: Page table free [ 133.191313] saa7231_free_ptable (0): SG Page table free [ 133.191316] saa7231_dmabuf_free (0): INFO: Scatterlist unmapped [ 133.191320] saa7231_dmabuf_free (0): INFO: Scatterlist free [ 133.191322] saa7231_dmabuf_sgfree (0): SG free [ 133.191327] saa7231_dmabuf_free (0): INFO: Page table free [ 133.191329] saa7231_free_ptable (0): SG Page table free [ 133.191332] saa7231_dmabuf_free (0): INFO: Scatterlist unmapped [ 133.191336] saa7231_dmabuf_free (0): INFO: Scatterlist free [ 133.191338] saa7231_dmabuf_sgfree (0): SG free [ 133.191343] saa7231_dmabuf_free (0): INFO: Page table free [ 133.191345] saa7231_free_ptable (0): SG Page table free [ 133.191348] saa7231_dmabuf_free (0): INFO: Scatterlist unmapped [ 133.191351] saa7231_dmabuf_free (0): INFO: Scatterlist free [ 133.191352] saa7231_dmabuf_sgfree (0): SG free [ 133.191359] saa7231_dmabuf_free (0): INFO: Page table free [ 133.191361] saa7231_free_ptable (0): SG Page table free [ 133.191364] saa7231_dmabuf_free (0): INFO: Scatterlist unmapped [ 133.191367] saa7231_dmabuf_free (0): INFO: Scatterlist free [ 133.191369] saa7231_dmabuf_sgfree (0): SG free [ 133.191374] saa7231_dmabuf_free (0): INFO: Page table free [ 133.191377] saa7231_free_ptable (0): SG Page table free [ 133.191381] saa7231_hw_free (0): DEBUG: Freeing ptable ... [ 133.191383] saa7231_capture_close (0): DEBUG: Closing stream
On Thu, Nov 11, 2010 at 6:04 PM, Manu Abraham abraham.manu@gmail.com wrote:
On Thu, Nov 11, 2010 at 4:00 PM, Jaroslav Kysela perex@perex.cz wrote:
On Thu, 11 Nov 2010, Manu Abraham wrote:
On Wed, Nov 10, 2010 at 11:35 PM, Jaroslav Kysela perex@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 ...
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; }
/* * 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; struct saa7231_stream *stream = audio->stream; struct stream_ops *ops = &stream->ops; 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; } return 0; }
static int saa7231_xs2dtl_run(struct saa7231_stream *stream) { unsigned long run = 0; u32 delay;
u32 reg; struct saa7231_dev *saa7231 = stream->saa7231; struct saa7231_dtl *dtl = &stream->dtl; struct saa7231_dmabuf *dmabuf = stream->dmabuf; u32 module = dtl->module; int port = stream->port_id;
SAA7231_WR(DMAADDR(0, port, dmabuf, 0), SAA7231_BAR0, module, S2D_CHx_B0_B_START_ADDRESS(0)); SAA7231_WR(DMAADDR(0, port, dmabuf, 1), SAA7231_BAR0, module, S2D_CHx_B1_B_START_ADDRESS(0));
/* tell the device that the mmu got updated */ reg = SAA7231_RD(SAA7231_BAR0, MMU, MMU_DMA_CONFIG(stream->port_id)); reg &= ~0x40; SAA7231_WR(reg, SAA7231_BAR0, MMU, MMU_DMA_CONFIG(stream->port_id)); SAA7231_WR((reg | 0x40), SAA7231_BAR0, MMU, MMU_DMA_CONFIG(stream->port_id));
dtl->addr_prev = 0xffffff;
/* * monitor PT load operation but wait a short time before * checking the PT valid. Otherwise we will see the old state. */ delay = 1000; do { if (!run) { msleep(10); delay--; } run = SAA7231_RD(SAA7231_BAR0, MMU, MMU_DMA_CONFIG(stream->port_id)) & 0x80; } while (!run && delay);
/* check status of the PTA load operation */ if (!run) { dprintk(SAA7231_ERROR, 1, "ERROR, Preload PTA failed"); return -EINVAL; }
/* read channel control register, enable channel */ reg = SAA7231_RD(SAA7231_BAR0, module, S2D_CHx_CTRL(0)); SAA7231_WR(reg | 0x1, SAA7231_BAR0, module, S2D_CHx_CTRL(0)); reg = SAA7231_RD(SAA7231_BAR0, module, S2D_S2D_CTRL);
/* clear all pending interrupts */ SAA7231_WR(0x3fff, SAA7231_BAR0, module, S2D_INT_CLR_STATUS(0));
/* enable interrupts */ SAA7231_WR(0x1800, SAA7231_BAR0, module, S2D_INT_SET_ENABLE(0));
/* enable capture */ reg |= 0x1; SAA7231_WR(reg, SAA7231_BAR0, module, S2D_S2D_CTRL); dtl->stream_run = 1;
return 0; }
testbox ~ # arecord -D plughw:2,0 --format S16_LE --rate=32000 -c 2 > ~/test_capture.wav Recording WAVE 'stdin' : Signed 16 bit Little Endian, Rate 32000 Hz, Stereo arecord: pcm_read:1617: read error: Input/output error
[ 276.117516] SAA7231 0000:05:00.0: PCI INT A -> GSI 16 (level, low) -> IRQ 16 [ 276.117524] SAA7231 0000:05:00.0: setting latency timer to 64 [ 276.117547] saa7231_pci_init (0): BAR 0 Offset: 04 BAR 2 Offset: 04 [ 276.117549] saa7231_pci_init (0): BAR0 Start=fc800000 length=4M [ 276.117551] saa7231_pci_init (0): BAR2 Start=fc400000 length=4M [ 276.118754] SAA7231GE [14c7:3595], irq: 16, [ 276.118757] mmio(0): 0xf8800000 mmio(2): 0xf9e00000 [ 276.118771] SAA7231 64Bit, MSI Disabled, MSI-X=1 msgs [ 276.118778] saa7231_get_version (0): SAA7231 PCI Express V1A found [ 276.118780] saa7231_cgu_init (0): Initializing CGU [ 276.118782] saa7231_cgu_init (0): Using 54MHz RootClock [ 276.118803] saa7231_cgu_init (0): PLL Status CDEC160: 00 REF550: 00 ADPLL: 00 DSP: 00 [ 276.118805] saa7231_set_clk (0): clk_output=0 clk_frequency=2 [ 276.118807] saa7231_set_clk (0): clk_output=1 clk_frequency=2 [ 276.118809] saa7231_set_clk (0): clk_output=2 clk_frequency=2 [ 276.118810] saa7231_set_clk (0): clk_output=3 clk_frequency=2 [ 276.118812] saa7231_set_clk (0): clk_output=4 clk_frequency=2 [ 276.118814] saa7231_set_clk (0): clk_output=5 clk_frequency=2 [ 276.118815] saa7231_set_clk (0): clk_output=6 clk_frequency=2 [ 276.118817] saa7231_set_clk (0): clk_output=7 clk_frequency=2 [ 276.118819] saa7231_set_clk (0): clk_output=8 clk_frequency=2 [ 276.118820] saa7231_set_clk (0): clk_output=9 clk_frequency=2 [ 276.118822] saa7231_set_clk (0): clk_output=10 clk_frequency=2 [ 276.118824] saa7231_set_clk (0): clk_output=11 clk_frequency=2 [ 276.118826] saa7231_set_clk (0): clk_output=12 clk_frequency=2 [ 276.118827] saa7231_set_clk (0): clk_output=13 clk_frequency=2 [ 276.118829] saa7231_set_clk (0): clk_output=14 clk_frequency=2 [ 276.118831] saa7231_set_clk (0): clk_output=15 clk_frequency=2 [ 276.118832] saa7231_set_clk (0): clk_output=16 clk_frequency=2 [ 276.118834] saa7231_set_clk (0): clk_output=17 clk_frequency=2 [ 276.118836] saa7231_set_clk (0): clk_output=18 clk_frequency=2 [ 276.170029] saa7231_cgu_init (0): DEBUG: RGU_RESET_ACTIVE0 7c02001f [ 276.271028] saa7231_cgu_init (0): DEBUG: RGU_RESET_ACTIVE0 ffffffff [ 276.271033] saa7231_cgu_init (0): DEBUG: RGU_RESET_ACTIVE1 2e1f [ 276.271035] saa7231_set_clk (0): clk_output=17 clk_frequency=0 [ 276.277053] saa7231_i2c_init (0): Initializing SAA7231 I2C Core [ 276.277057] saa7231_i2c_init (0): Initializing adapter (0) SAA7231 I2C:0 [ 276.277344] saa7231_i2c_hwinit (0): Adapter (109000) SAA7231 I2C:0 RESET [ 276.277347] saa7231_i2c_hwinit (0): Initializing Adapter SAA7231 I2C:0 @ 100k [ 276.277355] saa7231_i2c_init (0): Initializing adapter (1) SAA7231 12C:1 [ 276.277625] saa7231_i2c_hwinit (0): Adapter (10a000) SAA7231 12C:1 RESET [ 276.277627] saa7231_i2c_hwinit (0): Initializing Adapter SAA7231 12C:1 @ 100k [ 276.277635] saa7231_i2c_init (0): Initializing adapter (2) SAA7231 I2C:2 [ 276.277703] saa7231_i2c_hwinit (0): Adapter (10b000) SAA7231 I2C:2 RESET [ 276.277705] saa7231_i2c_hwinit (0): Initializing Adapter SAA7231 I2C:2 @ 100k [ 276.277713] saa7231_i2c_init (0): Initializing adapter (3) SAA7231 I2C:3 [ 276.277973] saa7231_i2c_hwinit (0): Adapter (10c000) SAA7231 I2C:3 RESET [ 276.277976] saa7231_i2c_hwinit (0): Initializing Adapter SAA7231 I2C:3 @ 100k [ 276.277983] saa7231_i2c_init (0): SAA7231 I2C Core succesfully initialized [ 276.277985] saa7231_alsa_init (0): Initializing Audio .. [ 276.283197] saa7231_pci_probe (0): SAA7231 device:0 initialized [ 285.973561] saa7231_capture_open (0): () [ 285.974297] saa7231_hw_params (0): DEBUG: () [ 285.974299] saa7231_hw_params (0): bufsiz=131072 periods=8 bytes=16384 pages=32 [ 285.974302] saa7231_stream_init (0): DEBUG: Initializing Stream with MODE=0x01 [ 285.974305] saa7231_xs2dtl_init (0): XS2DTL engine Initializing ..... [ 285.974307] saa7231_xs2dtl_init (0): Allocated 8 XS2DTL Buffer @0xf42dc200 [ 285.974309] saa7231_xs2dtl_init (0): Allocating DMA Buffers ... [ 285.974311] saa7231_xs2dtl_init (0): Allocating DMA buffer:0 @0xf42dc200.. [ 285.974322] saa7231_allocate_ptable (0): Virtual 0xf38dc000 to Phys 0x338dc000 mapped page [ 285.974338] saa7231_dmabuf_sgalloc (0): Virtual contiguous 16384 byte region with 4 4k pages [ 285.974361] 6c763000 6c762000 6f8bd000 6f810000 [ 285.974367] saa7231_xs2dtl_init (0): Allocating DMA buffer:1 @0xf42dc228.. [ 285.974375] saa7231_allocate_ptable (0): Virtual 0xf38dd000 to Phys 0x338dd000 mapped page [ 285.974381] saa7231_dmabuf_sgalloc (0): Virtual contiguous 16384 byte region with 4 4k pages [ 285.974401] 6fa10000 6c6b0000 6c6b5000 6c6b4000 [ 285.974406] saa7231_xs2dtl_init (0): Allocating DMA buffer:2 @0xf42dc250.. [ 285.974414] saa7231_allocate_ptable (0): Virtual 0xf3832000 to Phys 0x33832000 mapped page [ 285.974421] saa7231_dmabuf_sgalloc (0): Virtual contiguous 16384 byte region with 4 4k pages [ 285.974441] 6c6b8000 6c6b3000 6c6b2000 6c581000 [ 285.974445] saa7231_xs2dtl_init (0): Allocating DMA buffer:3 @0xf42dc278.. [ 285.974453] saa7231_allocate_ptable (0): Virtual 0xf3927000 to Phys 0x33927000 mapped page [ 285.974459] saa7231_dmabuf_sgalloc (0): Virtual contiguous 16384 byte region with 4 4k pages [ 285.974479] 6c783000 6c782000 6c699000 6c698000 [ 285.974484] saa7231_xs2dtl_init (0): Allocating DMA buffer:4 @0xf42dc2a0.. [ 285.974491] saa7231_allocate_ptable (0): Virtual 0xf38a9000 to Phys 0x338a9000 mapped page [ 285.974498] saa7231_dmabuf_sgalloc (0): Virtual contiguous 16384 byte region with 4 4k pages [ 285.974517] 6c6bc000 6c879000 6fbf5000 6c777000 [ 285.974522] saa7231_xs2dtl_init (0): Allocating DMA buffer:5 @0xf42dc2c8.. [ 285.974529] saa7231_allocate_ptable (0): Virtual 0xf538e000 to Phys 0x3538e000 mapped page [ 285.974535] saa7231_dmabuf_sgalloc (0): Virtual contiguous 16384 byte region with 4 4k pages [ 285.974555] 6c77c000 6c593000 6fbf6000 6c58d000 [ 285.974560] saa7231_xs2dtl_init (0): Allocating DMA buffer:6 @0xf42dc2f0.. [ 285.974568] saa7231_allocate_ptable (0): Virtual 0xf3974000 to Phys 0x33974000 mapped page [ 285.974573] saa7231_dmabuf_sgalloc (0): Virtual contiguous 16384 byte region with 4 4k pages [ 285.974593] 6fbe2000 6fbe0000 6c6a2000 6c6b7000 [ 285.974599] saa7231_xs2dtl_init (0): Allocating DMA buffer:7 @0xf42dc318.. [ 285.974606] saa7231_allocate_ptable (0): Virtual 0xf38a5000 to Phys 0x338a5000 mapped page [ 285.974612] saa7231_dmabuf_sgalloc (0): Virtual contiguous 16384 byte region with 4 4k pages [ 285.974632] 6c692000 6c60a000 6c75b000 6f825000 [ 285.974637] saa7231_xs2dtl_init (0): Initializing PTA ... [ 285.974639] saa7231_init_ptables (0): DEBUG: Initializing PORT:6 DMA_CH:7 with 8 Buffers [ 285.974641] saa7231_xs2dtl_init (0): Setting up PORT Gates ... [ 285.974644] saa7231_stream_init (0): INFO: Initialized MODE:0x01 for PORT:6 [ 285.974646] saa7231_add_irqevent (0): Adding AS2D_AVIS IRQ Event:43 ... [ 285.974648] saa7231_setup_vector (0): Adding Vector:43 [ 285.974652] saa7231_setup_vector (0): Enabling Vector:43 [ 285.974656] saa7231_add_irqevent (0): Succesfully added AS2D_AVIS as Event handler:43 [ 285.974658] saa7231_hw_params (0): Mapping 8 buffers with 4 pages each [ 285.974659] saa7231_hw_params (0): Page Table array size=32 [ 285.974662] saa7231_hw_params (0): Mapping Page:0 from XS2D_BUFFER:0 to PTA Offset:0 [ 285.974664] saa7231_hw_params (0): Mapping Page:1 from XS2D_BUFFER:0 to PTA Offset:1 [ 285.974666] saa7231_hw_params (0): Mapping Page:2 from XS2D_BUFFER:0 to PTA Offset:2 [ 285.974668] saa7231_hw_params (0): Mapping Page:3 from XS2D_BUFFER:0 to PTA Offset:3 [ 285.974670] saa7231_hw_params (0): Mapping Page:0 from XS2D_BUFFER:1 to PTA Offset:4 [ 285.974673] saa7231_hw_params (0): Mapping Page:1 from XS2D_BUFFER:1 to PTA Offset:5 [ 285.974675] saa7231_hw_params (0): Mapping Page:2 from XS2D_BUFFER:1 to PTA Offset:6 [ 285.974677] saa7231_hw_params (0): Mapping Page:3 from XS2D_BUFFER:1 to PTA Offset:7 [ 285.974679] saa7231_hw_params (0): Mapping Page:0 from XS2D_BUFFER:2 to PTA Offset:8 [ 285.974681] saa7231_hw_params (0): Mapping Page:1 from XS2D_BUFFER:2 to PTA Offset:9 [ 285.974683] saa7231_hw_params (0): Mapping Page:2 from XS2D_BUFFER:2 to PTA Offset:10 [ 285.974685] saa7231_hw_params (0): Mapping Page:3 from XS2D_BUFFER:2 to PTA Offset:11 [ 285.974687] saa7231_hw_params (0): Mapping Page:0 from XS2D_BUFFER:3 to PTA Offset:12 [ 285.974689] saa7231_hw_params (0): Mapping Page:1 from XS2D_BUFFER:3 to PTA Offset:13 [ 285.974691] saa7231_hw_params (0): Mapping Page:2 from XS2D_BUFFER:3 to PTA Offset:14 [ 285.974693] saa7231_hw_params (0): Mapping Page:3 from XS2D_BUFFER:3 to PTA Offset:15 [ 285.974695] saa7231_hw_params (0): Mapping Page:0 from XS2D_BUFFER:4 to PTA Offset:16 [ 285.974697] saa7231_hw_params (0): Mapping Page:1 from XS2D_BUFFER:4 to PTA Offset:17 [ 285.974699] saa7231_hw_params (0): Mapping Page:2 from XS2D_BUFFER:4 to PTA Offset:18 [ 285.974701] saa7231_hw_params (0): Mapping Page:3 from XS2D_BUFFER:4 to PTA Offset:19 [ 285.974704] saa7231_hw_params (0): Mapping Page:0 from XS2D_BUFFER:5 to PTA Offset:20 [ 285.974706] saa7231_hw_params (0): Mapping Page:1 from XS2D_BUFFER:5 to PTA Offset:21 [ 285.974708] saa7231_hw_params (0): Mapping Page:2 from XS2D_BUFFER:5 to PTA Offset:22 [ 285.974710] saa7231_hw_params (0): Mapping Page:3 from XS2D_BUFFER:5 to PTA Offset:23 [ 285.974712] saa7231_hw_params (0): Mapping Page:0 from XS2D_BUFFER:6 to PTA Offset:24 [ 285.974714] saa7231_hw_params (0): Mapping Page:1 from XS2D_BUFFER:6 to PTA Offset:25 [ 285.974716] saa7231_hw_params (0): Mapping Page:2 from XS2D_BUFFER:6 to PTA Offset:26 [ 285.974718] saa7231_hw_params (0): Mapping Page:3 from XS2D_BUFFER:6 to PTA Offset:27 [ 285.974720] saa7231_hw_params (0): Mapping Page:0 from XS2D_BUFFER:7 to PTA Offset:28 [ 285.974722] saa7231_hw_params (0): Mapping Page:1 from XS2D_BUFFER:7 to PTA Offset:29 [ 285.974724] saa7231_hw_params (0): Mapping Page:2 from XS2D_BUFFER:7 to PTA Offset:30 [ 285.974726] saa7231_hw_params (0): Mapping Page:3 from XS2D_BUFFER:7 to PTA Offset:31 [ 285.974749] saa7231_capture_prepare (0): () [ 285.974751] saa7231_capture_prepare (0): Trying to Acquire stream with 4096 lines per stream buffer [ 285.974753] saa7231_xs2dtl_acquire (0): Activating clock .. mode=0x01, port_id=0x06 [ 285.974756] saa7231_activate_clocks (0): DEBUG: Activating Clock for Mode=0x01, port=0x06 [ 285.974758] tmGetClockInstance (0): DEBUG: Mode=0x01, dmaport=0x06 [ 285.974760] tmGetClockInstance (0): ret=0 [ 285.974766] saa7231_activate_clocks (0): INFO: activate use case index 0 [ 285.974768] saa7231_init_ptables (0): DEBUG: Initializing PORT:6 DMA_CH:7 with 8 Buffers [ 285.974871] saa7231_capture_trigger (0): Trying to START stream, cmd=1 [ 285.985285] [ 285.985286] ================================= [ 285.985289] [ INFO: inconsistent lock state ] [ 285.985291] 2.6.34.7 #2 [ 285.985293] --------------------------------- [ 285.985295] inconsistent {IN-HARDIRQ-W} -> {HARDIRQ-ON-W} usage. [ 285.985297] arecord/6052 [HC0[0]:SC0[0]:HE1:SE1] takes: [ 285.985299] (&(&substream->self_group.lock)->rlock){?.....}, at: [<c13271b5>] snd_pcm_stream_lock_irq+0x20/0x23 [ 285.985309] {IN-HARDIRQ-W} state was registered at: [ 285.985311] [<c105a27c>] __lock_acquire+0x23e/0xb47 [ 285.985318] [<c105ac11>] lock_acquire+0x8c/0xab [ 285.985322] [<c144a8bb>] _raw_spin_lock+0x20/0x2f [ 285.985326] [<c132811e>] snd_pcm_period_elapsed+0x39/0xb4 [ 285.985329] [<f843040f>] azx_interrupt+0x8e/0x106 [snd_hda_intel] [ 285.985336] [<c1079a16>] handle_IRQ_event+0x4b/0xf1 [ 285.985340] [<c107b469>] handle_fasteoi_irq+0x86/0xbd [ 285.985344] irq event stamp: 4550 [ 285.985346] hardirqs last enabled at (4549): [<c100287b>] sysenter_exit+0xf/0x16 [ 285.985350] hardirqs last disabled at (4550): [<c144ac7b>] _raw_read_lock_irq+0x11/0x35 [ 285.985353] softirqs last enabled at (3756): [<c1039b96>] __do_softirq+0x15a/0x169 [ 285.985357] softirqs last disabled at (3731): [<c10042fc>] do_softirq+0x63/0xaf [ 285.985360] [ 285.985361] other info that might help us debug this: [ 285.985363] 2 locks held by arecord/6052: [ 285.985364] #0: (snd_pcm_link_rwlock){.?....}, at: [<c13271aa>] snd_pcm_stream_lock_irq+0x15/0x23 [ 285.985369] #1: (&(&substream->self_group.lock)->rlock){?.....}, at: [<c13271b5>] snd_pcm_stream_lock_irq+0x20/0x23 [ 285.985374] [ 285.985375] stack backtrace: [ 285.985378] Pid: 6052, comm: arecord Not tainted 2.6.34.7 #2 [ 285.985379] Call Trace: [ 285.985383] [<c1448b56>] ? printk+0x14/0x16 [ 285.985385] [<c105938f>] valid_state+0x12a/0x13d [ 285.985388] [<c1059493>] mark_lock+0xf1/0x1d9 [ 285.985391] [<c1059b3c>] ? check_usage_backwards+0x0/0x68 [ 285.985394] [<c10595be>] mark_held_locks+0x43/0x5b [ 285.985396] [<c144ad98>] ? _raw_spin_unlock_irq+0x27/0x2b [ 285.985399] [<c10597d7>] trace_hardirqs_on_caller+0xe7/0x121 [ 285.985402] [<c105981c>] trace_hardirqs_on+0xb/0xd [ 285.985405] [<c144ad98>] _raw_spin_unlock_irq+0x27/0x2b [ 285.985409] [<c102d6ac>] finish_task_switch+0x5c/0x86 [ 285.985412] [<c102d650>] ? finish_task_switch+0x0/0x86 [ 285.985415] [<c144925d>] schedule+0x53d/0x5b4 [ 285.985417] [<c14495ec>] schedule_timeout+0x7e/0x9b [ 285.985422] [<c103f3ea>] ? process_timeout+0x0/0xf [ 285.985425] [<c1449623>] schedule_timeout_uninterruptible+0x1a/0x1c [ 285.985428] [<c103f8e9>] msleep+0x15/0x1b [ 285.985439] [<f83b5974>] saa7231_xs2dtl_run+0x93/0x123 [saa7231_core] [ 285.985447] [<f83b9746>] saa7231_capture_trigger+0x59/0xed [saa7231_core] [ 285.985450] [<c13226e3>] snd_pcm_do_start+0x23/0x26 [ 285.985453] [<c132263a>] snd_pcm_action_single+0x2a/0x50 [ 285.985455] [<c1323ce8>] snd_pcm_action+0x6f/0x7b [ 285.985458] [<c1323d8a>] snd_pcm_start+0x19/0x1b [ 285.985461] [<c13283a0>] snd_pcm_lib_read1+0x61/0x1ff [ 285.985464] [<c13285be>] snd_pcm_lib_read+0x33/0x54 [ 285.985467] [<c13273e5>] ? snd_pcm_lib_read_transfer+0x0/0x70 [ 285.985469] [<c1325ec1>] snd_pcm_capture_ioctl1+0x8e/0x2ed [ 285.985472] [<c1326148>] snd_pcm_capture_ioctl+0x28/0x35 [ 285.985477] [<c10c7ec5>] vfs_ioctl+0x2c/0x96 [ 285.985480] [<c1326120>] ? snd_pcm_capture_ioctl+0x0/0x35 [ 285.985483] [<c10c843f>] do_vfs_ioctl+0x46c/0x4aa [ 285.985486] [<c10bd0f3>] ? fsnotify_modify+0x54/0x5f [ 285.985489] [<c10bd1fe>] ? do_sync_write+0x0/0xca [ 285.985491] [<c10be3e3>] ? fget_light+0xe/0xaf [ 285.985494] [<c10c84b0>] sys_ioctl+0x33/0x4d [ 285.985497] [<c100284c>] sysenter_do_call+0x12/0x32 [ 296.977014] saa7231_xs2dtl_run (0): ERROR, Preload PTA failed [ 296.977019] saa7231_capture_trigger (0): ERROR: starting stream, err=-22 [ 296.977022] saa7231_capture_trigger (0): Trying to STOP stream, cmd=0 [ 296.977182] saa7231_hw_free (0): DEBUG: Removing IRQ event .. [ 296.977184] saa7231_remove_irqevent (0): Removing IRQ Event:43 ...... [ 296.977187] saa7231_remove_irqevent (0): IRQ Event 43 <AS2D_AVIS> removed [ 296.977191] saa7231_hw_free (0): DEBUG: Unmap Virtual memory region .. [ 296.977204] saa7231_hw_free (0): DEBUG: Stream exiting .. [ 296.977207] saa7231_stream_exit (0): INFO: Freeing MODE:0x01 for PORT=0x06 [ 296.977209] saa7231_xs2dtl_exit (0): Free XS2DTL engine .. [ 296.977211] saa7231_dmabuf_free (0): INFO: Scatterlist unmapped [ 296.977216] saa7231_dmabuf_free (0): INFO: Scatterlist free [ 296.977218] saa7231_dmabuf_sgfree (0): SG free [ 296.977224] saa7231_dmabuf_free (0): INFO: Page table free [ 296.977226] saa7231_free_ptable (0): SG Page table free [ 296.977229] saa7231_dmabuf_free (0): INFO: Scatterlist unmapped [ 296.977233] saa7231_dmabuf_free (0): INFO: Scatterlist free [ 296.977234] saa7231_dmabuf_sgfree (0): SG free [ 296.977240] saa7231_dmabuf_free (0): INFO: Page table free [ 296.977242] saa7231_free_ptable (0): SG Page table free [ 296.977245] saa7231_dmabuf_free (0): INFO: Scatterlist unmapped [ 296.977249] saa7231_dmabuf_free (0): INFO: Scatterlist free [ 296.977250] saa7231_dmabuf_sgfree (0): SG free [ 296.977256] saa7231_dmabuf_free (0): INFO: Page table free [ 296.977258] saa7231_free_ptable (0): SG Page table free [ 296.977261] saa7231_dmabuf_free (0): INFO: Scatterlist unmapped [ 296.977265] saa7231_dmabuf_free (0): INFO: Scatterlist free [ 296.977266] saa7231_dmabuf_sgfree (0): SG free [ 296.977272] saa7231_dmabuf_free (0): INFO: Page table free [ 296.977274] saa7231_free_ptable (0): SG Page table free [ 296.977277] saa7231_dmabuf_free (0): INFO: Scatterlist unmapped [ 296.977281] saa7231_dmabuf_free (0): INFO: Scatterlist free [ 296.977282] saa7231_dmabuf_sgfree (0): SG free [ 296.977288] saa7231_dmabuf_free (0): INFO: Page table free [ 296.977290] saa7231_free_ptable (0): SG Page table free [ 296.977293] saa7231_dmabuf_free (0): INFO: Scatterlist unmapped [ 296.977297] saa7231_dmabuf_free (0): INFO: Scatterlist free [ 296.977298] saa7231_dmabuf_sgfree (0): SG free [ 296.977304] saa7231_dmabuf_free (0): INFO: Page table free [ 296.977306] saa7231_free_ptable (0): SG Page table free [ 296.977309] saa7231_dmabuf_free (0): INFO: Scatterlist unmapped [ 296.977313] saa7231_dmabuf_free (0): INFO: Scatterlist free [ 296.977314] saa7231_dmabuf_sgfree (0): SG free [ 296.977320] saa7231_dmabuf_free (0): INFO: Page table free [ 296.977322] saa7231_free_ptable (0): SG Page table free [ 296.977325] saa7231_dmabuf_free (0): INFO: Scatterlist unmapped [ 296.977329] saa7231_dmabuf_free (0): INFO: Scatterlist free [ 296.977331] saa7231_dmabuf_sgfree (0): SG free [ 296.977337] saa7231_dmabuf_free (0): INFO: Page table free [ 296.977338] saa7231_free_ptable (0): SG Page table free [ 296.977342] saa7231_hw_free (0): DEBUG: Freeing ptable ... [ 296.977344] saa7231_capture_close (0): DEBUG: Closing stream
Regards, Manu
On Fri, 12 Nov 2010, Manu Abraham wrote:
On Thu, Nov 11, 2010 at 6:04 PM, Manu Abraham abraham.manu@gmail.com wrote:
On Thu, Nov 11, 2010 at 4:00 PM, Jaroslav Kysela perex@perex.cz wrote:
On Thu, 11 Nov 2010, Manu Abraham wrote:
On Wed, Nov 10, 2010 at 11:35 PM, Jaroslav Kysela perex@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.
Jaroslav
----- Jaroslav Kysela perex@perex.cz Linux Kernel Sound Maintainer ALSA Project, Red Hat, Inc.
On Fri, Nov 12, 2010 at 1:35 PM, Jaroslav Kysela perex@perex.cz wrote:
On Fri, 12 Nov 2010, Manu Abraham wrote:
On Thu, Nov 11, 2010 at 6:04 PM, Manu Abraham abraham.manu@gmail.com wrote:
On Thu, Nov 11, 2010 at 4:00 PM, Jaroslav Kysela perex@perex.cz wrote:
On Thu, 11 Nov 2010, Manu Abraham wrote:
On Wed, Nov 10, 2010 at 11:35 PM, Jaroslav Kysela perex@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
Hi,
Had some amount of success, but not completely functional yet.
at the moment, I do get a overrun (at least that's what arecord claims). In fact after opening due to audio capture I do get the first interrupt, but before the next interrupt arrives, arecord closes the device and reopens the device (from the logs, I do see that the device is being continuously opened and closed for each of those mentioned overruns ?).
Eventually the device always ends up with an interrupt showing data with the first 4 pages.
Any idea, why arecord keeps opening and closing for each overrun ?
Thanks, Manu
testbox v4l # arecord -D plughw:2,0 --format S16_LE --rate=32000 -c 2
/tmp/test.wav
Recording WAVE 'stdin' : Signed 16 bit Little Endian, Rate 32000 Hz, Stereo overrun!!! (at least 0.005 ms long) overrun!!! (at least 0.002 ms long) overrun!!! (at least 0.002 ms long) overrun!!! (at least 0.003 ms long) overrun!!! (at least 0.003 ms long) overrun!!! (at least 0.001 ms long) overrun!!! (at least 0.002 ms long) overrun!!! (at least 0.001 ms long) overrun!!! (at least 0.001 ms long) overrun!!! (at least 0.001 ms long) overrun!!! (at least 0.001 ms long) overrun!!! (at least 0.001 ms long)
testbox v4l # cat /var/log/messages |grep evhandler Dec 15 23:33:26 testbox kernel: [22380.791159] saa7231_audio_evhandler (0): DEBUG: got buffer with index:1 port:6 Dec 15 23:33:26 testbox kernel: [22380.807889] saa7231_audio_evhandler (0): DEBUG: got buffer with index:1 port:6 Dec 15 23:33:26 testbox kernel: [22380.824799] saa7231_audio_evhandler (0): DEBUG: got buffer with index:1 port:6 Dec 15 23:33:26 testbox kernel: [22380.840634] saa7231_audio_evhandler (0): DEBUG: got buffer with index:1 port:6 Dec 15 23:33:26 testbox kernel: [22380.856821] saa7231_audio_evhandler (0): DEBUG: got buffer with index:1 port:6 Dec 15 23:33:26 testbox kernel: [22380.872661] saa7231_audio_evhandler (0): DEBUG: got buffer with index:1 port:6 Dec 15 23:33:26 testbox kernel: [22380.888827] saa7231_audio_evhandler (0): DEBUG: got buffer with index:1 port:6 Dec 15 23:33:26 testbox kernel: [22380.904667] saa7231_audio_evhandler (0): DEBUG: got buffer with index:1 port:6 Dec 15 23:33:26 testbox kernel: [22380.920849] saa7231_audio_evhandler (0): DEBUG: got buffer with index:1 port:6
[22395.146703] saa7231_irq_handler (0): status=0x800 vector=43 event=43 handler:f83d63a6 [22395.146716] saa7231_audio_evhandler (0): DEBUG: got buffer with index:1 port:6 [22395.146734] saa7231_capture_pointer (0): DEBUG:() [22395.146737] saa7231_capture_trigger (0): Trying to STOP stream, cmd=0
[22395.146780] saa7231_capture_prepare (0): () [22395.146783] saa7231_capture_prepare (0): Trying to Acquire stream with 4096 lines per stream buffer [22395.146786] saa7231_xs2dtl_acquire (0): Activating clock .. mode=0x01, port_id=0x06 [22395.146788] saa7231_activate_clocks (0): DEBUG: Activating Clock for Mode=0x01, port=0x06 [22395.146793] saa7231_activate_clocks (0): INFO: tmIActivateSystemUseCase noticed active use case! [22395.146806] saa7231_init_ptables (0): DEBUG: Initializing PORT:6 DMA_CH:7 with 8 Buffers [22395.157028] saa7231_capture_trigger (0): Trying to START stream, cmd=1 [22395.157031] saa7231_xs2dtl_run (0): module=0x124000 Run [22395.162525] saa7231_irq_handler (0): status=0x800 vector=43 event=43 handler:f83d63a6 [22395.162537] saa7231_audio_evhandler (0): DEBUG: got buffer with index:1 port:6 [22395.162551] saa7231_capture_pointer (0): DEBUG:() [22395.162553] saa7231_capture_trigger (0): Trying to STOP stream, cmd=0
[22395.162586] saa7231_capture_prepare (0): () [22395.162588] saa7231_capture_prepare (0): Trying to Acquire stream with 4096 lines per stream buffer [22395.162591] saa7231_xs2dtl_acquire (0): Activating clock .. mode=0x01, port_id=0x06 [22395.162593] saa7231_activate_clocks (0): DEBUG: Activating Clock for Mode=0x01, port=0x06 [22395.162598] saa7231_activate_clocks (0): INFO: tmIActivateSystemUseCase noticed active use case! [22395.162611] saa7231_init_ptables (0): DEBUG: Initializing PORT:6 DMA_CH:7 with 8 Buffers [22395.173028] saa7231_capture_trigger (0): Trying to START stream, cmd=1 [22395.173032] saa7231_xs2dtl_run (0): module=0x124000 Run [22395.178682] saa7231_irq_handler (0): status=0x800 vector=43 event=43 handler:f83d63a6 [22395.178695] saa7231_audio_evhandler (0): DEBUG: got buffer with index:1 port:6 [22395.178714] saa7231_capture_pointer (0): DEBUG:() [22395.178716] saa7231_capture_trigger (0): Trying to STOP stream, cmd=0
[22395.178759] saa7231_capture_prepare (0): () [22395.178762] saa7231_capture_prepare (0): Trying to Acquire stream with 4096 lines per stream buffer [22395.178765] saa7231_xs2dtl_acquire (0): Activating clock .. mode=0x01, port_id=0x06 [22395.178767] saa7231_activate_clocks (0): DEBUG: Activating Clock for Mode=0x01, port=0x06 [22395.178772] saa7231_activate_clocks (0): INFO: tmIActivateSystemUseCase noticed active use case! [22395.178785] saa7231_init_ptables (0): DEBUG: Initializing PORT:6 DMA_CH:7 with 8 Buffers [22395.189028] saa7231_capture_trigger (0): Trying to START stream, cmd=1 [22395.189030] saa7231_xs2dtl_run (0): module=0x124000 Run [22395.194864] saa7231_irq_handler (0): status=0x800 vector=43 event=43 handler:f83d63a6 [22395.194877] saa7231_audio_evhandler (0): DEBUG: got buffer with index:1 port:6 [22395.194893] saa7231_capture_pointer (0): DEBUG:() [22395.194896] saa7231_capture_trigger (0): Trying to STOP stream, cmd=0
[22395.194932] saa7231_capture_prepare (0): () [22395.194934] saa7231_capture_prepare (0): Trying to Acquire stream with 4096 lines per stream buffer [22395.194937] saa7231_xs2dtl_acquire (0): Activating clock .. mode=0x01, port_id=0x06 [22395.194939] saa7231_activate_clocks (0): DEBUG: Activating Clock for Mode=0x01, port=0x06 [22395.194944] saa7231_activate_clocks (0): INFO: tmIActivateSystemUseCase noticed active use case! [22395.194957] saa7231_init_ptables (0): DEBUG: Initializing PORT:6 DMA_CH:7 with 8 Buffers [22395.206170] saa7231_hw_free (0): DEBUG: Removing IRQ event .. [22395.206174] saa7231_remove_irqevent (0): Removing IRQ Event:43 ...... [22395.206176] saa7231_remove_irqevent (0): IRQ Event 43 <AS2D_AVIS> removed [22395.206202] saa7231_hw_free (0): DEBUG: Removing IRQ event .. [22395.206242] saa7231_remove_irqevent (0): Removing IRQ Event:43 ...... [22395.206244] saa7231_capture_close (0): DEBUG: Closing stream
static struct snd_pcm_hardware saa7231_capture_info = { .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP_VALID),
.formats = (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE | SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_U16_BE),
.rates = SNDRV_PCM_RATE_32000,
.rate_min = 32000, .rate_max = 48000,
.channels_min = 1, .channels_max = 2,
.buffer_bytes_max = XS2D_BUFFERS * PAGE_SIZE * 512,
.period_bytes_min = 4096, .period_bytes_max = 2097152,
.periods_min = 8, .periods_max = 4096, };
static int saa7231_audio_evhandler(struct saa7231_dev *saa7231, int vector) { struct saa7231_audio *audio = saa7231->audio; struct saa7231_stream *stream = audio->stream;
int index = 0, err = 0;
if (stream->ops.get_buffer) { err = stream->ops.get_buffer(stream, &index); if (err) dprintk(SAA7231_ERROR, 1, "ERROR: get_buffer failed, err=%d", err);
dprintk(SAA7231_DEBUG, 1, "DEBUG: got buffer with index:%d port:%d", index, stream->port_id); } audio->index = index; queue_work(saa7231->wq, &audio->irq_pending_work); return 0; }
static void saa7231_audio_irq_work(struct work_struct *work) { struct saa7231_audio *audio = container_of(work, struct saa7231_audio, irq_pending_work);
snd_pcm_period_elapsed(audio->pcm); }
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; struct snd_pcm_runtime *rt = pcm->runtime;
dprintk(SAA7231_DEBUG, 1, "DEBUG:()"); return bytes_to_frames(rt, audio->index * audio->pages * PAGE_SIZE); }
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 avis_audio *decoder; struct avis_audio_ops *ops; struct audio_config config;
struct saa7231_dmabuf *dmabuf; struct page **ptable;
void *mem; void *dma_area;
int ret, 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; audio->periods = periods; audio->bytes = bytes; audio->pages = pages;
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"); ret = -ENOMEM; goto err; } audio->stream = stream; buffer = stream->dmabuf; mutex_lock(&saa7231->dev_lock);
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"); ret = -ENOMEM; goto err; } 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;
config.srate = SRATE_32k; /* default sample rate */ config.source = AUDIOSRC_CVBS1; /* default source */ config.l_volume = VOL_MAX; config.r_volume = VOL_MAX;
ret = saa7231_audio_attach(stream, &config); if (ret) { dprintk(SAA7231_ERROR, 1, "Audio decoder attach failed, err=%d", ret); goto err; }
decoder = stream->adec; ops = &decoder->ops;
decoder->port = AUDIOPORT_DAC1;
if (ops->powerup) { ret = ops->powerup(stream); if (ret) { dprintk(SAA7231_ERROR, 1, "Failed to power up, err=%d", ret); ret = -EIO; goto err; } }
if (ops->open) { ret = ops->open(stream); if (ret) { dprintk(SAA7231_ERROR, 1, "Failed to open stream, err=%d", ret); return -EIO; } }
if (ops->ena_port) { ret = ops->ena_port(stream, AUDIOPORT_SPDIF, 1); if (ret) { dprintk(SAA7231_ERROR, 1, "Failed to enable port, err=%d", ret); return -EIO; } }
if (ops->set_src) { ret = ops->set_src(stream, AUDIOSRC_CVBS1); if (ret) { dprintk(SAA7231_ERROR, 1, "Failed to enable source, err=%d", ret); return -EIO; } }
err: mutex_unlock(&saa7231->dev_lock); return ret; }
On Fri, Nov 12, 2010 at 1:35 PM, Jaroslav Kysela perex@perex.cz wrote:
On Fri, 12 Nov 2010, Manu Abraham wrote:
On Thu, Nov 11, 2010 at 6:04 PM, Manu Abraham abraham.manu@gmail.com wrote:
On Thu, Nov 11, 2010 at 4:00 PM, Jaroslav Kysela perex@perex.cz wrote:
On Thu, 11 Nov 2010, Manu Abraham wrote:
On Wed, Nov 10, 2010 at 11:35 PM, Jaroslav Kysela perex@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.
Jaroslav
Jaroslav Kysela perex@perex.cz Linux Kernel Sound Maintainer ALSA Project, Red Hat, Inc.
participants (2)
-
Jaroslav Kysela
-
Manu Abraham