[alsa-devel] MSM DSP
hi, I've bought an HTC dream,before buying I looked at the feasibility of porting GNU/Linux on it(so not Android). There is a patch(for maing the driver standard) or a standard(everything is non-standard in android) driver for most of the components. The biggest problem seems the sound. So knowing that codeaurora( https://www.codeaurora.org/gitweb/quic/le/?p=kernel/msm.git ) had alsa in its kenrel I first tried to compile that kernel...but due to numerous bugs(It didn't compile and needed too much fixing) in it android-msm-2.6.29 branch I decided to import alsa in the linuxtogo kernel,and I pushed the sources at a temporary htc-alsa branch at git://gitorious.org/replicant/gnulinuxkernel.git (it's called gnulinux in order to differentiate from an android kernel,I didn't find a good name for it)
from the commit message available here : http://gitorious.org/replicant/gnulinuxkernel/commit/92945ccd0921e940fc2675d...
I had to change snd_rpc_ids.vers = 0x00020001 into snd_rpc_ids.vers = 0xaa2b1a44 ,and to make minor build system changes and I had aplay working rapidely... but only aplay... mplayer was waiting indefinitely for the following system call: ioctl(6, 0x400c4150 ls -l /proc/$(pido mplayer)/fd/6 gave /dev/snd/pcmC0D0p and 0x400c4150 gave that: SNDRV_PCM_IOCTL_WRITEI_FRAMES = _IOW('A', 0x50, struct snd_xferi)
Then with printks(I had no serial cable(I've looked for a craddle/docking station with the special HTC connector for easy soldering...but I didn't found one) so I couldn't use kgdb) I went until that point: rc = wait_event_interruptible(the_locks.write_wait, (frame->used == 0)|| (prtd->stopped)); That's in the alsa_send_buffer function in /sound/soc/msm/msm-pcm.c Then I continued the tracing puting printks everywhere...and I found that: with aplay: msm_pcm_playback_copy in the /sound/soc/msm/msm7k-pcm.c was called rc = alsa_send_buffer(prtd, buf, fbytes, NULL); was called once inside alsa_send_buffer (in /sound/soc/msm/msm-pcm.c) the count loop was only executed once because count was exhausted on the first run then copy_count is incremented and the audio is configured then at second call of the alsa_send_buffer the audio is configured and works. here's an output of the aplay parameters:
$ aplay -v 05\ -\ Jet\ Set.wav Playing WAVE '05 - Jet Set.wav' : Signed 16 bit Little Endian, Rate 44100 Hz, Stereo Plug PCM: Hardware PCM card 0 'msm-audio' device 0 subdevice 0 Its setup is: stream : PLAYBACK access : RW_INTERLEAVED format : S16_LE subformat : STD channels : 2 rate : 44100 exact rate : 44100 (44100/1) msbits : 16 buffer_size : 4800 period_size : 1200 period_time : 27210 tick_time : 0 tstamp_mode : NONE period_step : 1 sleep_min : 0 avail_min : 1200 xfer_align : 1200 start_threshold : 4800 stop_threshold : 4800 silence_threshold: 0 silence_size : 0 boundary : 1258291200
note that frame = prtd->out + prtd->out_tail; makes frame->used have a bad value the first time it is run but as the count is only run once it doesn't import.
the next runs prtd->out + prtd->out_tail have a good value
with mplayer the story was quite different,even if the beginning was the same : msm_pcm_playback_copy in the /sound/soc/msm/msm7k-pcm.c was also called,rc = alsa_send_buffer(prtd, buf, fbytes, NULL); was also called inside alsa_send_buffer (in /sound/soc/msm/msm-pcm.c) but once in alsa_send_buffer,more precisely under the count loop the story changes: count is not exhausted the first time,because in "xfer = count > frame->size ? frame->size : count;" the smallest between count and frame->size is assigned,and so after it does "count -= xfer" and so count is not 0,because the buffer size detected by mplayer is 19200 insterad of 4800 so here what it does in detail: it passes 19200 as count here: fbytes = frames_to_bytes(runtime, frames); rc = alsa_send_buffer(prtd, buf, fbytes, NULL); that goes into that: ssize_t alsa_send_buffer(struct msm_audio *prtd, const char __user *buf, size_t count, loff_t *pos) and then the count variable is 19200 in alsa_send_buffer then there is xfer = count > frame->size ? frame->size : count; so it assigns the minimum between count and frame->size to xfer count is still 19200 frame->size is 4800 so xfer becomes 4800 then count -= xfer decrement count but only of 4800 so count is not exhausted and runs another time then : frame = prtd->out + prtd->out_tail; makes frame->used have a bad value,and then it blocks here: rc = wait_event_interruptible(the_locks.write_wait,(frame->used == 0) || (prtd->stopped)); because frame->used is not 0.
here an mplayer output: Forced audio codec: mad Opening audio decoder: [pcm] Uncompressed PCM audio decoder dec_audio: Allocating 2048 + 65536 = 67584 bytes for output buffer. AUDIO: 44100 Hz, 2 ch, s16le, 1411.2 kbit/100.00% (ratio: 176400->176400) Selected audio codec: [pcm] afm: pcm (Uncompressed PCM) ========================================================================== Building audio filter chain for 44100Hz/2ch/s16le -> 0Hz/0ch/??... [libaf] Adding filter dummy [dummy] Was reinitialized: 44100Hz/2ch/s16le [dummy] Was reinitialized: 44100Hz/2ch/s16le Trying preferred audio driver 'alsa', options '[none]' alsa-init: requested format: 44100 Hz, 2 channels, 9 alsa-init: using ALSA 1.0.15 alsa-init: setup for 1/2 channel(s) alsa-init: using device default alsa-init: pcm opened in blocking mode alsa-init: got buffersize=19200 alsa-init: got period size 300 alsa: 44100 Hz/2 channels/4 bpf/19200 bytes buffer/Signed 16 bit Little Endian AO: [alsa] 44100Hz 2ch s16le (2 bytes per sample) AO: Description: ALSA-0.9.x-1.x audio output AO: Author: Alex Beregszaszi, Zsolt Barat joy@streamminister.de AO: Comment: under developement Building audio filter chain for 44100Hz/2ch/s16le -> 44100Hz/2ch/s16le... [dummy] Was reinitialized: 44100Hz/2ch/s16le [dummy] Was reinitialized: 44100Hz/2ch/s16le Video: no video Freeing 0 unused video chunks. Starting playback... Increasing filtered audio buffer size from 0 to 21248
so the quick solution was to force mplayer to use 4800 as buffer size with something like that:
diff --git a/sound/soc/msm/msm7k-pcm.c b/sound/soc/msm/msm7k-pcm.c index 38e8283..bfbc66d 100644 --- a/sound/soc/msm/msm7k-pcm.c +++ b/sound/soc/msm/msm7k-pcm.c @@ -115,7 +115,7 @@ static struct snd_pcm_hardware msm_pcm_playback_hardware = { .rate_max = USE_RATE_MAX, .channels_min = USE_CHANNELS_MIN, .channels_max = USE_CHANNELS_MAX, - .buffer_bytes_max = MAX_BUFFER_PLAYBACK_SIZE, + .buffer_bytes_max = 4800, .period_bytes_min = 64, .period_bytes_max = MAX_PERIOD_SIZE, .periods_min = USE_PERIODS_MIN,
but then mplayer seem to loop on an ioctl: mplayer is frozen here and doesn't respond to other input than ctrl+c: 0.0 (00.0) of 206.0 (03:26.0) ??,?% strace shows a lot of : nanosleep followed by SNDRV_PCM_IOCTL_STATUS (ioctl(6, 0x806c4120, 0xbe8bc9c8) = 0) an analyse of the status shows a constant : status->delay is 1200,and status->avail is 0,they don't vary over time and are printed just after that code in snd_pcm_status in sound/core/pcm_native.c if (runtime->status->state == SNDRV_PCM_STATE_RUNNING || runtime->status->state == SNDRV_PCM_STATE_DRAINING) status->delay = runtime->buffer_size - status->avail; else status->delay = 0;
I was told in #mplayerdev that it was because the DSP didn't play the frames...indeed I hear no sound....
So how should I fix that mess? *did the quick and dirty fix cause the second problem? *should I use the DSP directly and make a pulseaudio plugin that talks to the android driver, instead(the driver seems very buggy) The good would be support for mp3 and similar things within the DSP,and that it should be more stable. The bad is that I already spent *a lot* of time with the alsa kernel driver...and that writing that will also require some userspaces changes such as: *modification of the phone routing in the "FSO" ( http://www.freesmartphone.org/index.php/Main_Page ) dbus framework to use pulseaudio *would require changes in the build system for using pulseaudio(openembedded(http://wiki.openembedded.net/index.php/Main_Page )) *may be slow...openmoko stopped to use pulseaudio because of speed issues.
Denis
Please fix your MUA to word wrap at 80 columns, not doing this makes your posts much harder to read and reply to. It'd also be helpful to place blank lines between paragraphs to improve legibility.
On Tue, Nov 03, 2009 at 05:02:22PM +0100, GNUtoo wrote:
codeaurora( https://www.codeaurora.org/gitweb/quic/le/?p=kernel/msm.git ) had alsa in its kenrel I first tried to compile that kernel...but due to numerous bugs(It didn't compile and needed too much fixing) in it android-msm-2.6.29 branch I decided to import alsa in the linuxtogo kernel,and I pushed the sources at a temporary htc-alsa branch at git://gitorious.org/replicant/gnulinuxkernel.git (it's called gnulinux in order to differentiate from an android kernel,I didn't find a good name for it)
Please submit any code you have to the mailing list, it's much easier to review things via the standard workflows and getting the drivers into mainline would be good.
The problems you're reporting suggest that the driver either isn't communicating the requirements it has for data buffers to the applications correctly or it isn't correctly notifying applications when buffers have drained. mplayer is much more sensitive to both of these issues.
On Wed, 2009-11-04 at 10:49 +0000, Mark Brown wrote:
Please fix your MUA to word wrap at 80 columns, not doing this makes your posts much harder to read and reply to. It'd also be helpful to place blank lines between paragraphs to improve legibility.
So sorry,I was told that evolution does it by default. There must have been a wrong manipulation by me or a bug... I'm so sorry.
On Tue, Nov 03, 2009 at 05:02:22PM +0100, GNUtoo wrote:
codeaurora( https://www.codeaurora.org/gitweb/quic/le/?p=kernel/msm.git ) had alsa in its kenrel I first tried to compile that kernel...but due to numerous bugs(It didn't compile and needed too much fixing) in it android-msm-2.6.29 branch I decided to import alsa in the linuxtogo kernel,and I pushed the sources at a temporary htc-alsa branch at git://gitorious.org/replicant/gnulinuxkernel.git (it's called gnulinux in order to differentiate from an android kernel,I didn't find a good name for it)
Please submit any code you have to the mailing list, it's much easier to review things via the standard workflows and getting the drivers into mainline would be good.
I'll make git send a mail. But note that the code of the alsa driver depends on a custom android sound system that is in : arch/arm/mach-msm/qdsp5/ so it's platform specific.
The problems you're reporting suggest that the driver either isn't communicating the requirements it has for data buffers to the applications correctly or it isn't correctly notifying applications when buffers have drained. mplayer is much more sensitive to both of these issues.
Thanks a lot
Denis.
Please submit any code you have to the mailing list, it's much easier to review things via the standard workflows and getting the drivers into mainline would be good.
Here it is...I hope it's correct and in the proper format sorry for the delay.
Denis.
On Thu, Nov 05, 2009 at 04:17:24PM +0100, Denis 'GNUtoo' Carikli wrote:
Please submit any code you have to the mailing list, it's much easier to review things via the standard workflows and getting the drivers into mainline would be good.
Here it is...I hope it's correct and in the proper format sorry for the delay.
You appear to have omitted the actual patch...
On Thu, 2009-11-05 at 15:23 +0000, Mark Brown wrote:
On Thu, Nov 05, 2009 at 04:17:24PM +0100, Denis 'GNUtoo' Carikli wrote:
Please submit any code you have to the mailing list, it's much easier to review things via the standard workflows and getting the drivers into mainline would be good.
Here it is...I hope it's correct and in the proper format sorry for the delay.
You appear to have omitted the actual patch...
sorry I had that: Died at /usr/libexec/git-core/git-send-email line 966.
I've bad luck with that mailing list...lol
Denis.
I had to make two little change to make it compile: snd_ep = msm_rpc_connect_compatible(snd_rpc_ids.prog, became: snd_ep = msm_rpc_connect(snd_rpc_ids.prog, and I also changed snd_rpc_ids.vers(with ifdefs) to a known and working magick number:
I also had to change from ARCH_MSM_ARM11 to ARCH_MSM in the configuration
it still has serious runtime problems such as: *Can produce kernel oops under theses conditions: start alsamixer and if the second bar is on 0 or 4, so it can play music with aplay,then increase the routing alsamixer bar to the max. Then decrease the routing bar to 4 or less Then It may have a null pointer problem That bug could be because it tries to route to route to speakers and handset at the same time(SND_DEVICE_HEADSET_AND_SPEAKER in android): that is to say it could be the same bug than here: http://gitorious.org/replicant/msm7k/commit/370d37a088368ca8cc478e76c928a1ce... but I need time to verify that *can pannick(reboots the phone) if you send things to /dev/dsp when the oss emulation is activated *only aplay works(mplayer,gstreamer don't work) for mplayer an ioctl didn't return...so it get stuck before playing. The explanation of the bug can be found here: http://mailman.alsa-project.org/pipermail/alsa-devel/2009-November/022697.ht...
Note the following things: *this patch depends on,and doesn't contain arch/arm/mach-msm/qdsp5 *I removed the support for more recents chips because of code-size issue in a mailing list
Signed-off-by: Denis 'GNUtoo' Carikli GNUtoo@no-log.org --- sound/soc/Kconfig | 1 + sound/soc/Makefile | 1 + sound/soc/msm/Kconfig | 23 ++ sound/soc/msm/Makefile | 11 + sound/soc/msm/msm-dai.c | 143 ++++++++++ sound/soc/msm/msm-pcm.c | 643 +++++++++++++++++++++++++++++++++++++++++++++ sound/soc/msm/msm-pcm.h | 200 ++++++++++++++ sound/soc/msm/msm7201.c | 337 ++++++++++++++++++++++++ sound/soc/msm/msm7k-pcm.c | 574 ++++++++++++++++++++++++++++++++++++++++ 9 files changed, 1933 insertions(+), 0 deletions(-) create mode 100644 sound/soc/msm/Kconfig create mode 100644 sound/soc/msm/Makefile create mode 100644 sound/soc/msm/msm-dai.c create mode 100644 sound/soc/msm/msm-pcm.c create mode 100644 sound/soc/msm/msm-pcm.h create mode 100644 sound/soc/msm/msm7201.c create mode 100644 sound/soc/msm/msm7k-pcm.c
Hi!
I had to make two little change to make it compile: snd_ep = msm_rpc_connect_compatible(snd_rpc_ids.prog, became: snd_ep = msm_rpc_connect(snd_rpc_ids.prog, and I also changed snd_rpc_ids.vers(with ifdefs) to a known and working magick number:
I also had to change from ARCH_MSM_ARM11 to ARCH_MSM in the configuration
Thanks for pointer.
it still has serious runtime problems such as: *Can produce kernel oops under theses conditions: start alsamixer and if the second bar is on 0 or 4, so it can play music with aplay,then increase the routing alsamixer bar to the max. Then decrease the routing bar to 4 or less Then It may have a null pointer problem That bug could be because it tries to route to route to speakers and handset at the same time(SND_DEVICE_HEADSET_AND_SPEAKER in android): that is to say it could be the same bug than here: http://gitorious.org/replicant/msm7k/commit/370d37a088368ca8cc478e76c928a1ce... but I need time to verify that *can pannick(reboots the phone) if you send things to /dev/dsp when the oss emulation is activated *only aplay works(mplayer,gstreamer don't work) for mplayer an ioctl didn't return...so it get stuck before playing. The explanation of the bug can be found here: http://mailman.alsa-project.org/pipermail/alsa-devel/2009-November/022697.ht...
Perhaps it should go to staging?
Note the following things: *this patch depends on,and doesn't contain arch/arm/mach-msm/qdsp5 *I removed the support for more recents chips because of code-size issue in a mailing list
I had qdsp5 support in staging tree, check 2.6.31. It is not there _now_ but I hope to reinstall it. Pavel
On Sun, Nov 08, 2009 at 11:46:10PM +0100, Denis 'GNUtoo' Carikli wrote:
Please do try to rememeber to CC maintainers on patches...
I've read some, but not all of this since I really can't follow how the code is supposed to be constructed. At the minute there doesn't feel like there's enough abstraction between the various parts of the code, with lots of things cropping up in unexpected places and making it harder to follow.
It would probably be useful to refactor things so that you've got a support layer for the DSP that deals with any resource sharing issues and then build more standard-looking CODEC, DAI and DMA drivers (the DAI driver would probably be largely empty, I guess) with a machine driver gluing them together.
it still has serious runtime problems such as: *Can produce kernel oops under theses conditions: start alsamixer and if the second bar is on 0 or 4, so it can play music with aplay,then increase the routing alsamixer bar to the max. Then decrease the routing bar to 4 or less
What are "the second bar" and "routing bar"? Remember, most of the people reading your mail will never have seen your system running.
+config SND_MSM_DAI_SOC
- tristate "SoC CPU/CODEC DAI for the MSM chip"
- depends on SND_MSM_SOC || SND_QSD_SOC
- help
To add support for ALSA PCM driver for MSM board.
No need to make this one uer visible.
+config SND_MSM_SOC_MSM7K
tristate "SoC Audio support for MSM7K"
depends on SND_MSM_SOC
Or this. I know some architectures do this but we should probably just move them to visibly select only boards.
- codec->name = "MSM-CARD";
Probably something like "MSM7xxx internal CODEC" or similar.
+static int __init msm_dai_init(void) +{
- return snd_soc_register_dais(msm_dais, ARRAY_SIZE(msm_dais));
+}
+static void __exit msm_dai_exit(void) +{
- snd_soc_unregister_dais(msm_dais, ARRAY_SIZE(msm_dais));
+}
Your DAIs ought to be registered as platform devices under arch/arm, probe as normal platform devices and then register the DAIs when these probe.
+#define audio_send_queue_recbs(prtd, cmd, len) \
- msm_adsp_write(prtd->audrec, QDSP_uPAudRecBitStreamQueue, cmd, len)
+#define audio_send_queue_rec(prtd, cmd, len) \
- msm_adsp_write(prtd->audrec, QDSP_uPAudRecCmdQueue, cmd, len)
Inline functions, please.
+int msm_audio_volume_update(unsigned id,
int volume, int pan)
+{
- unsigned vol_raw;
- vol_raw = compute_db_raw(volume);
- printk(KERN_INFO "volume: %8x vol_raw: %8x \n", volume, vol_raw);
- return audpp_set_volume_and_pan(id, vol_raw, pan);
+} +EXPORT_SYMBOL(msm_audio_volume_update);
Hrm?
unsigned id = msg[2];
unsigned idx = msg[3] - 1;
if (id != AUDPP_MSG_HOSTPCM_ID_ARM_RX) {
printk(KERN_ERR "bogus id\n");
break;
With stuff like this displaying what the bogus IDs are would be useful.
/* Update with actual sent buffer size */
if (prtd->out[idx].used != BUF_INVALID_LEN)
prtd->pcm_irq_pos += prtd->out[idx].used;
if (prtd->pcm_irq_pos > prtd->pcm_size)
prtd->pcm_irq_pos = prtd->pcm_count;
This looks suspicious - it looks like what's going on is that you're getting a notification from the DSP that it has consumed a data buffer but this might go out of bounds and get clamped.
if (prtd->ops->playback)
prtd->ops->playback(prtd);
spin_lock_irqsave(&the_locks.write_dsp_lock, flag);
if (prtd->running) {
prtd->out[idx].used = 0;
frame = prtd->out + prtd->out_tail;
if (frame->used) {
audio_dsp_send_buffer(prtd,
prtd->out_tail,
frame->used);
prtd->out_tail ^= 1;
I've got a feeling that this code would be a lot easier to follow if you used a linked list of buffers. You could statically allocate all the buffers still, but it'd make it much more explicit what was going on if you had a list of buffers queued for transmission to the DSP and a list of free buffers.
Given what it looks like this is doing I'd really expect snd_pcm_period_elapsed to be called directly from here - the playback and capture code paths appear to be completely separate and the indirections you're using appear not to be needed. I'd also expect it to be called after you've finished updating the buffers rather than before, especially given that you appear to call it outside the lock on buffer updates.
} else {
prtd->out_needed++;
}
wake_up(&the_locks.write_wait);
Why this?
- if (prtd->dir == SNDRV_PCM_STREAM_PLAYBACK) {
prtd->out_buffer_size = PLAYBACK_DMASZ;
prtd->out_sample_rate = 44100;
prtd->out_channel_mode = AUDPP_CMD_PCM_INTF_STEREO_V;
prtd->out_weight = 100;
You're not setting any
+int alsa_audio_configure(struct msm_audio *prtd) +{
- struct audmgr_config cfg;
- int rc;
- if (prtd->enabled)
return 0;
- /* refuse to start if we're not ready with first buffer */
- if (!prtd->out[0].used)
return -EIO;
- cfg.tx_rate = 0;
- cfg.rx_rate = RPC_AUD_DEF_SAMPLE_RATE_48000;
Your code appears to hard code both 44.1k and 48k inconsistently.
audmgr_disable(&prtd->audmgr);
return -ENODEV;
- }
- prtd->enabled = 1;
- return 0;
+} +EXPORT_SYMBOL(alsa_audio_configure);
This looks extremely suspect...
+ssize_t alsa_send_buffer(struct msm_audio *prtd, const char __user *buf,
size_t count, loff_t *pos)
+{
- unsigned long flag;
- const char __user *start = buf;
- struct buffer *frame;
- size_t xfer;
- int rc = 0;
- mutex_lock(&the_locks.write_lock);
- while (count > 0) {
frame = prtd->out + prtd->out_head;
rc = wait_event_interruptible(the_locks.write_wait,
(frame->used == 0)
|| (prtd->stopped));
if (rc < 0)
break;
if (prtd->stopped) {
rc = -EBUSY;
break;
}
xfer = count > frame->size ? frame->size : count;
if (copy_from_user(frame->data, buf, xfer)) {
rc = -EFAULT;
break;
}
frame->used = xfer;
prtd->out_head ^= 1;
count -= xfer;
buf += xfer;
spin_lock_irqsave(&the_locks.write_dsp_lock, flag);
frame = prtd->out + prtd->out_tail;
if (frame->used && prtd->out_needed) {
audio_dsp_send_buffer(prtd, prtd->out_tail,
frame->used);
prtd->out_tail ^= 1;
These ^s really don't help legibility.
prtd->out_needed--;
}
spin_unlock_irqrestore(&the_locks.write_dsp_lock, flag);
- }
- mutex_unlock(&the_locks.write_lock);
- if (buf > start)
return buf - start;
- return rc;
+} +EXPORT_SYMBOL(alsa_send_buffer);
...as does this. There should be no need to export all this stuff, and if you are exporting it it ought to be _GPL since all the ASoC APIs are _GPL.
I'd also really like to see some explanation of what the locking scheme is supposed to be - I can't quite follow what's supposed to be protected against what and when but I suspect there's cases where either read or write lock is taken and the_locks.lock is really needed.
+EXPORT_SYMBOL(alsa_audio_disable);
+#include <../arch/arm/mach-msm/qdsp5/adsp.h> +#include <../arch/arm/mach-msm/qdsp5/audmgr.h>
Put these in an include directory.
+#define FRAME_NUM (8) +#define FRAME_SIZE (2052 * 2) +#define MONO_DATA_SIZE (2048) +#define STEREO_DATA_SIZE (MONO_DATA_SIZE * 2) +#define CAPTURE_DMASZ (FRAME_SIZE * FRAME_NUM)
+#define BUFSZ (960 * 5) +#define PLAYBACK_DMASZ (BUFSZ * 2)
All this stuff needs namespacing.
+/* Support unconventional sample rates 12000, 24000 as well */ +#define USE_RATE \
(SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_KNOT)
Your code doesn't appear to actually cope with all these rates. Also, these ought to be pushed down into the drivers rather than exported in the header file - there's no need for other drivers to see them.
+static int snd_msm_volume_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
+{
- int change;
- int volume;
- volume = ucontrol->value.integer.value[0];
- spin_lock_irq(&the_locks.mixer_lock);
- change = (msm_vol_ctl.volume != volume);
- if (change) {
msm_vol_ctl.update = 1;
msm_vol_ctl.volume = volume;
- }
- spin_unlock_irq(&the_locks.mixer_lock);
This doesn't actually appear to do anything to propagate the changes to the DSP?
+static struct snd_kcontrol_new snd_msm_controls[] = {
- MSM_EXT_TLV("PCM Playback Volume", 0, snd_msm_volume_info, \
- snd_msm_volume_get, snd_msm_volume_put, 0, db_scale_linear),
- MSM_EXT("device", 1, snd_msm_device_info, snd_msm_device_get, \
snd_msm_device_put, 0),
+};
I have no idea what "device" is supposed to be.
+static struct snd_soc_dai_link msm_dai = {
- .name = "ASOC",
- .stream_name = "ASOC",
- .codec_dai = &msm_dais[0],
- .cpu_dai = &msm_dais[1],
- .init = msm_soc_dai_init,
+};
Please pick some human readable names describing the setup (eg, "MSM7k internal CODEC").
On Fri, 2009-11-13 at 14:50 +0000, Mark Brown wrote:
On Sun, Nov 08, 2009 at 11:46:10PM +0100, Denis 'GNUtoo' Carikli wrote:
Please do try to rememeber to CC maintainers on patches...
I've read some, but not all of this since I really can't follow how the code is supposed to be constructed. At the minute there doesn't feel like there's enough abstraction between the various parts of the code, with lots of things cropping up in unexpected places and making it harder to follow.
It would probably be useful to refactor things so that you've got a support layer for the DSP that deals with any resource sharing issues and then build more standard-looking CODEC, DAI and DMA drivers (the DAI driver would probably be largely empty, I guess) with a machine driver gluing them together.
it still has serious runtime problems such as: *Can produce kernel oops under theses conditions: start alsamixer and if the second bar is on 0 or 4, so it can play music with aplay,then increase the routing alsamixer bar to the max. Then decrease the routing bar to 4 or less
What are "the second bar" and "routing bar"? Remember, most of the people reading your mail will never have seen your system running.
+config SND_MSM_DAI_SOC
- tristate "SoC CPU/CODEC DAI for the MSM chip"
- depends on SND_MSM_SOC || SND_QSD_SOC
- help
To add support for ALSA PCM driver for MSM board.
No need to make this one uer visible.
+config SND_MSM_SOC_MSM7K
tristate "SoC Audio support for MSM7K"
depends on SND_MSM_SOC
Or this. I know some architectures do this but we should probably just move them to visibly select only boards.
- codec->name = "MSM-CARD";
Probably something like "MSM7xxx internal CODEC" or similar.
+static int __init msm_dai_init(void) +{
- return snd_soc_register_dais(msm_dais, ARRAY_SIZE(msm_dais));
+}
+static void __exit msm_dai_exit(void) +{
- snd_soc_unregister_dais(msm_dais, ARRAY_SIZE(msm_dais));
+}
Your DAIs ought to be registered as platform devices under arch/arm, probe as normal platform devices and then register the DAIs when these probe.
+#define audio_send_queue_recbs(prtd, cmd, len) \
- msm_adsp_write(prtd->audrec, QDSP_uPAudRecBitStreamQueue, cmd, len)
+#define audio_send_queue_rec(prtd, cmd, len) \
- msm_adsp_write(prtd->audrec, QDSP_uPAudRecCmdQueue, cmd, len)
Inline functions, please.
+int msm_audio_volume_update(unsigned id,
int volume, int pan)
+{
- unsigned vol_raw;
- vol_raw = compute_db_raw(volume);
- printk(KERN_INFO "volume: %8x vol_raw: %8x \n", volume, vol_raw);
- return audpp_set_volume_and_pan(id, vol_raw, pan);
+} +EXPORT_SYMBOL(msm_audio_volume_update);
Hrm?
unsigned id = msg[2];
unsigned idx = msg[3] - 1;
if (id != AUDPP_MSG_HOSTPCM_ID_ARM_RX) {
printk(KERN_ERR "bogus id\n");
break;
With stuff like this displaying what the bogus IDs are would be useful.
/* Update with actual sent buffer size */
if (prtd->out[idx].used != BUF_INVALID_LEN)
prtd->pcm_irq_pos += prtd->out[idx].used;
if (prtd->pcm_irq_pos > prtd->pcm_size)
prtd->pcm_irq_pos = prtd->pcm_count;
This looks suspicious - it looks like what's going on is that you're getting a notification from the DSP that it has consumed a data buffer but this might go out of bounds and get clamped.
if (prtd->ops->playback)
prtd->ops->playback(prtd);
spin_lock_irqsave(&the_locks.write_dsp_lock, flag);
if (prtd->running) {
prtd->out[idx].used = 0;
frame = prtd->out + prtd->out_tail;
if (frame->used) {
audio_dsp_send_buffer(prtd,
prtd->out_tail,
frame->used);
prtd->out_tail ^= 1;
I've got a feeling that this code would be a lot easier to follow if you used a linked list of buffers. You could statically allocate all the buffers still, but it'd make it much more explicit what was going on if you had a list of buffers queued for transmission to the DSP and a list of free buffers.
Given what it looks like this is doing I'd really expect snd_pcm_period_elapsed to be called directly from here - the playback and capture code paths appear to be completely separate and the indirections you're using appear not to be needed. I'd also expect it to be called after you've finished updating the buffers rather than before, especially given that you appear to call it outside the lock on buffer updates.
} else {
prtd->out_needed++;
}
wake_up(&the_locks.write_wait);
Why this?
- if (prtd->dir == SNDRV_PCM_STREAM_PLAYBACK) {
prtd->out_buffer_size = PLAYBACK_DMASZ;
prtd->out_sample_rate = 44100;
prtd->out_channel_mode = AUDPP_CMD_PCM_INTF_STEREO_V;
prtd->out_weight = 100;
You're not setting any
+int alsa_audio_configure(struct msm_audio *prtd) +{
- struct audmgr_config cfg;
- int rc;
- if (prtd->enabled)
return 0;
- /* refuse to start if we're not ready with first buffer */
- if (!prtd->out[0].used)
return -EIO;
- cfg.tx_rate = 0;
- cfg.rx_rate = RPC_AUD_DEF_SAMPLE_RATE_48000;
Your code appears to hard code both 44.1k and 48k inconsistently.
audmgr_disable(&prtd->audmgr);
return -ENODEV;
- }
- prtd->enabled = 1;
- return 0;
+} +EXPORT_SYMBOL(alsa_audio_configure);
This looks extremely suspect...
+ssize_t alsa_send_buffer(struct msm_audio *prtd, const char __user *buf,
size_t count, loff_t *pos)
+{
- unsigned long flag;
- const char __user *start = buf;
- struct buffer *frame;
- size_t xfer;
- int rc = 0;
- mutex_lock(&the_locks.write_lock);
- while (count > 0) {
frame = prtd->out + prtd->out_head;
rc = wait_event_interruptible(the_locks.write_wait,
(frame->used == 0)
|| (prtd->stopped));
if (rc < 0)
break;
if (prtd->stopped) {
rc = -EBUSY;
break;
}
xfer = count > frame->size ? frame->size : count;
if (copy_from_user(frame->data, buf, xfer)) {
rc = -EFAULT;
break;
}
frame->used = xfer;
prtd->out_head ^= 1;
count -= xfer;
buf += xfer;
spin_lock_irqsave(&the_locks.write_dsp_lock, flag);
frame = prtd->out + prtd->out_tail;
if (frame->used && prtd->out_needed) {
audio_dsp_send_buffer(prtd, prtd->out_tail,
frame->used);
prtd->out_tail ^= 1;
These ^s really don't help legibility.
prtd->out_needed--;
}
spin_unlock_irqrestore(&the_locks.write_dsp_lock, flag);
- }
- mutex_unlock(&the_locks.write_lock);
- if (buf > start)
return buf - start;
- return rc;
+} +EXPORT_SYMBOL(alsa_send_buffer);
...as does this. There should be no need to export all this stuff, and if you are exporting it it ought to be _GPL since all the ASoC APIs are _GPL.
I'd also really like to see some explanation of what the locking scheme is supposed to be - I can't quite follow what's supposed to be protected against what and when but I suspect there's cases where either read or write lock is taken and the_locks.lock is really needed.
+EXPORT_SYMBOL(alsa_audio_disable);
+#include <../arch/arm/mach-msm/qdsp5/adsp.h> +#include <../arch/arm/mach-msm/qdsp5/audmgr.h>
Put these in an include directory.
+#define FRAME_NUM (8) +#define FRAME_SIZE (2052 * 2) +#define MONO_DATA_SIZE (2048) +#define STEREO_DATA_SIZE (MONO_DATA_SIZE * 2) +#define CAPTURE_DMASZ (FRAME_SIZE * FRAME_NUM)
+#define BUFSZ (960 * 5) +#define PLAYBACK_DMASZ (BUFSZ * 2)
All this stuff needs namespacing.
+/* Support unconventional sample rates 12000, 24000 as well */ +#define USE_RATE \
(SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_KNOT)
Your code doesn't appear to actually cope with all these rates. Also, these ought to be pushed down into the drivers rather than exported in the header file - there's no need for other drivers to see them.
+static int snd_msm_volume_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
+{
- int change;
- int volume;
- volume = ucontrol->value.integer.value[0];
- spin_lock_irq(&the_locks.mixer_lock);
- change = (msm_vol_ctl.volume != volume);
- if (change) {
msm_vol_ctl.update = 1;
msm_vol_ctl.volume = volume;
- }
- spin_unlock_irq(&the_locks.mixer_lock);
This doesn't actually appear to do anything to propagate the changes to the DSP?
+static struct snd_kcontrol_new snd_msm_controls[] = {
- MSM_EXT_TLV("PCM Playback Volume", 0, snd_msm_volume_info, \
- snd_msm_volume_get, snd_msm_volume_put, 0, db_scale_linear),
- MSM_EXT("device", 1, snd_msm_device_info, snd_msm_device_get, \
snd_msm_device_put, 0),
+};
I have no idea what "device" is supposed to be.
+static struct snd_soc_dai_link msm_dai = {
- .name = "ASOC",
- .stream_name = "ASOC",
- .codec_dai = &msm_dais[0],
- .cpu_dai = &msm_dais[1],
- .init = msm_soc_dai_init,
+};
Please pick some human readable names describing the setup (eg, "MSM7k internal CODEC").
Thanks a lot!!! I'll try to fix most of the problems and resend a patch,I'll also continue the debugging...which will take some time....
About the debugging: *I noticed that if I change the .buffer_bytes_max to 4800 aplay doesn't work anymore...I'll try to find the good values... *I also noticed that aplay doesn't use the SNDRV_PCM_IOCTL_STATUS which would explain why aplay works and not mplayer... so I'll try to fix that too...
Denis.
On Sun, Nov 15, 2009 at 09:06:40PM +0100, GNUtoo wrote:
About the debugging: *I noticed that if I change the .buffer_bytes_max to 4800 aplay doesn't work anymore...I'll try to find the good values... *I also noticed that aplay doesn't use the SNDRV_PCM_IOCTL_STATUS which would explain why aplay works and not mplayer... so I'll try to fix that too...
These both sound like the code for managing pushing of the buffers to the DSP - like I said, it looks suspect on reading too so I'd not be surprised if fixing the comprehensibility issues also make things work better.
Recently I looked again at the code. And I found this:
prtd->out[0].used = BUF_INVALID_LEN; prtd->out_head = 1; /* point to second buffer on startup */
in msm_pcm_open in msm7k-pcm.c
So here how it works: when the sound card is opened the fist buffer is assigned to BUF_INVALID_LEN and is skipped(prtd->out_head = 1).
Then at a point alsa_send_buffer (in msm-pcm.c) is called. But because mplayer uses a bigger buffer it goes this way: The first run goes fine: rc = wait_event_interruptible(the_locks.write_wait, (frame->used == 0) || (prtd->stopped)); is passed. Then in "xfer = count > frame->size ? frame->size : count;",the minimum between frame size and count is assigned to xfer. Then in copy_from_user(frame->data, buf, xfer) the audio frame is copied from userspace. Then xfer is assigned to frame->used Then "prtd->out_head ^= 1;" needs some little explanations: There are 2 buffers prtd->out[0] and prtd->out[1] And both out_head and out_tail can only be 0 or 1 So when prtd->out_head is 0,prtd->out_head ^= 1 changes to 1 into a 0 and vice versa.
Note that I recently changed things like frame = prtd->out + prtd->out_tail into that kind of things: frame = &prtd->out[prtd->out_tail] which makes it more Readable,and still works.
prtd->out is a struct buffer which is described in msm-pcm.h: struct msm_audio { struct buffer out[2]; ...
the struct buffer is also described in msm-pcm.h like this: struct buffer { void *data; unsigned size; unsigned used; unsigned addr; };
So...prtd->out_head was 1 because it was assigned to 1 in msm_pcm_open in msm7k-pcm.c Now it becomes 0
Then it does that: count -= xfer; which doesn't zero count it because mplayer used some big buffer size
Then it sends the data to the dsp...and doesn't exit the "while (count > 0) {" loop because count is not 0
And Finally it reaches that line a second time: "rc = wait_event_interruptible(the_locks.write_wait, (frame->used == 0) || (prtd->stopped));" But...frame->used was, and is still BUF_INVALID_LEN so it blocks.
By the way David Lanzendörfer fixed a lot of issues with kernel oops/panic with the OSS emulation...and merged the adsp and the alsa layer in one driver(that means that they are in a same directory and that the resource sharing issues is gone.)
I bet we are close to something working as mplayer can now work trough oss emulation or trough sdl(tough there are some buffer underrun problem sometimes with sdl and nearly all the time with oss) But we still have some work to do in order to clean the driver and fix the issues you described before re-submiting it.
Denis.
Appendix: ---------
here's the content of alsa_send_buffer: size_t alsa_send_buffer(struct msm_audio *prtd, const char __user *buf, size_t count, loff_t *pos) { unsigned long flag; const char __user *start = buf; struct buffer *frame; size_t xfer; int rc = 0;
mutex_lock(&the_locks.write_lock); while (count > 0) { frame = &prtd->out[prtd->out_head]; rc = wait_event_interruptible(the_locks.write_wait, (frame->used == 0) || (prtd->stopped)); if (rc < 0) break; if (prtd->stopped) { rc = -EBUSY; break; } xfer = count > frame->size ? frame->size : count; if (copy_from_user(frame->data, buf, xfer)) { rc = -EFAULT; break; } frame->used = xfer; prtd->out_head ^= 1; count -= xfer; buf += xfer;
spin_lock_irqsave(&the_locks.write_dsp_lock, flag); frame = &prtd->out[prtd->out_tail]; if (frame->used && prtd->out_needed) { audio_dsp_send_buffer(prtd, prtd->out_tail, frame->used); prtd->out_tail ^= 1; prtd->out_needed--; } spin_unlock_irqrestore(&the_locks.write_dsp_lock, flag); } mutex_unlock(&the_locks.write_lock); if (buf > start) return buf - start; return rc;
And here's the content of msm_pcm_open:
static int msm_pcm_open(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct msm_audio *prtd; int ret = 0;
prtd = kzalloc(sizeof(struct msm_audio), GFP_KERNEL); if (prtd == NULL) { ret = -ENOMEM; return ret; } if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { msm_vol_ctl.update = 1; /* Update Volume, with Cached value */ runtime->hw = msm_pcm_playback_hardware; prtd->dir = SNDRV_PCM_STREAM_PLAYBACK; prtd->playback_substream = substream; } else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { runtime->hw = msm_pcm_capture_hardware; prtd->dir = SNDRV_PCM_STREAM_CAPTURE; prtd->capture_substream = substream; }
ret = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &constraints_sample_rates); if (ret < 0) goto out;
/* Ensure that buffer size is a multiple of period size */ ret = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); if (ret < 0) goto out;
prtd->ops = &snd_msm_audio_ops; prtd->out[0].used = BUF_INVALID_LEN; prtd->out_head = 1; /* point to second buffer on startup */ runtime->private_data = prtd;
ret = alsa_adsp_configure(prtd); if (ret) goto out; copy_count = 0; return 0;
out: kfree(prtd); return ret; }
Someone named Thingol in #alsa helped me and told me how to fix it:
<Thingol> if so, then maybe it would help to move the call to alsa_audio_configure (probably before the end of that while loop), but you have to make sure it only gets called once.
I did that and it unblocked mplayer... Audio now works nearly flawlessly(you can still hear some cracks but that's nearly not noticeable)
We also need to look in the silence callback because for fixing something, David Lanzendörfer(who fixed the oss emulations problems) ifdefed the whole content of snd_pcm_format_set_silence. (maybe that's why there are some cracks in the audio...)
Denis.
On Fri, 2009-11-13 at 14:50 +0000, Mark Brown wrote:
/* Update with actual sent buffer size */
if (prtd->out[idx].used != BUF_INVALID_LEN)
prtd->pcm_irq_pos +=
prtd->out[idx].used;
if (prtd->pcm_irq_pos > prtd->pcm_size)
prtd->pcm_irq_pos = prtd->pcm_count;
This looks suspicious - it looks like what's going on is that you're getting a notification from the DSP that it has consumed a data buffer but this might go out of bounds and get clamped.
The first time that it's called prtd->out[idx].used = BUF_INVALID_LEN so it's skipped(it's the same first bogus time than in my previous mails).
The second time prtd->out[idx].used is equal to the period_bytes_max which is equal to the .buffer_bytes_max / 2 prtd->pcm_irq_pos is 0 then is incremented to prtd->out[idx].used So it's clamped only the first time.
By the way I've used that info to skip it in the alsa_send_buffer so it looks like this: in alsa_send_buffer I've tried to add a check for the invalid len before of after the "if (frame->used && prtd->out_needed) {"
here an example of it beeing before "if (frame->used && prtd->out_needed) {" @@ -411,6 +418,10 @@ ssize_t alsa_send_buffer(struct msm_audio *prtd, const char __user *buf,
spin_lock_irqsave(&the_locks.write_dsp_lock, flag); frame = prtd->out + prtd->out_tail; + if (frame->used == BUF_INVALID_LEN){ + spin_unlock_irqrestore(&the_locks.write_dsp_lock, flag); + break; + } if (frame->used && prtd->out_needed) { audio_dsp_send_buffer(prtd, prtd->out_tail, frame->used); @@ -418,10 +429,12 @@ ssize_t alsa_send_buffer(struct msm_audio *prtd, const char __user *buf, prtd->out_needed--; }
spin_unlock_irqrestore(&the_locks.write_dsp_lock, flag); for the try where it was after the spin_unlock was not necessary because it was put just after the spin_unlock
Then... mplayer said: alsa-init: got buffersize=19200 alsa-init: got period size 600 and it played a song for half a second and then the kernel panicked
here the code of mplayer: // gets buffersize for control if ((err = snd_pcm_hw_params_get_buffer_size(alsa_hwparams, &bufsize)) < 0) { mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_ALSA_UnableToGetBufferSize, snd_strerror(err)); return 0; } else { ao_data.buffersize = bufsize * bytes_per_sample; mp_msg(MSGT_AO,MSGL_V,"alsa-init: got buffersize=%i\n", ao_data.buffersize); }
if ((err = snd_pcm_hw_params_get_period_size(alsa_hwparams, &chunk_size, NULL)) < 0) { mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_ALSA_UnableToGetPeriodSize, snd_strerror(err)); return 0; } else { mp_msg(MSGT_AO,MSGL_V,"alsa-init: got period size %li\n", chunk_size); } ao_data.outburst = chunk_size * bytes_per_sample;
a strace shows that:
ioctl(0, TIOCGWINSZ, {ws_row=51, ws_col=202, ws_xpixel=0, ws_ypixel=0}) = 0 ) = 401, "A: 0.0 (00.0) of 2.0 (02.0) ??"..., 40A: 0.0 (00.0) of 2.0 (02.0) ??,?% gettimeofday({1259531032, 572021}, NULL) = 0 select(1, [0], NULL, NULL, {0, 0}) = 0SNDRV_PCM_IOCTL_STATUS (Timeout) ioctl(6, 0x806c4120, 0xbee5dac0) = 0 nanosleep({0, 27000000}, NULL) = 0 ioctl(6, 0x806c4120, 0xbee5dac0) = 0 nanosleep({0, 27000000}, Read from remote host 192.168.0.202: Connection reset by peer Connection to 192.168.0.202 closed.
0x806c4120 corresponds to A 0x20 that corresponds to SNDRV_PCM_IOCTL_STATUS = _IOR('A', 0x20, struct snd_pcm_status) in asound.h
After that I moved the snd_pcm_period_elapsed like this:
@@ -154,7 +158,10 @@ void alsa_dsp_event(void *data, unsigned id, uint16_t *msg) wake_up(&the_locks.write_wait); }
spin_unlock_irqrestore(&the_locks.write_dsp_lock, flag); + snd_pcm_period_elapsed(prtd->playback_substream); break;
and removed it from the old location:
diff --git a/sound/soc/msm/msm7k-pcm.c b/sound/soc/msm/msm7k-pcm.c index 38e8283..3e54184 100644 --- a/sound/soc/msm/msm7k-pcm.c +++ b/sound/soc/msm/msm7k-pcm.c @@ -152,8 +152,6 @@ static struct snd_pcm_hw_constraint_list constraints_sample_rates = {
static void playback_event_handler(void *data) { - struct msm_audio *prtd = data; - snd_pcm_period_elapsed(prtd->playback_substream); }
If I remember well aplay doesn't use the SNDRV_PCM_IOCTL_STATUS
Denis.
I've tried to limit to 4800 and now it plays for one quarter of seconds then loop on the status...and so stops playing...
Denis.
Hello, I have a sound card with I2C/PCM interface supported. I am in the process of evaluate the same using USB interface with HID interface class (ie bInterfaceClass ->3 Human Interface Device). Could you please suggest, if the usb interface HID class could be used for sending Audio and PCM signals?
Thanks Swami
On Mon, Nov 30, 2009 at 10:38:21AM +0530, M R Swami Reddy wrote:
I have a sound card with I2C/PCM interface supported. I am in the process of evaluate the same using USB interface with HID interface class (ie bInterfaceClass ->3 Human Interface Device). Could you please suggest, if the usb interface HID class could be used for sending Audio and PCM signals?
As of the USB logic, the ALSA driver should grab the audio interface while at the same time, the HID driver uses the other interface. Assuming that both interfaces are in the same USB config.
HTH, Daniel
M R Swami Reddy wrote:
Could you please suggest, if the usb interface HID class could be used for sending Audio and PCM signals?
No; audio data should be transferred using isochronous transfers, and the HID class does not use those.
Is there any reason why you cannot use a standard audio class interface?
Best regards, Clemens
On Mon, Nov 30, 2009 at 12:16:42AM +0100, GNUtoo wrote:
On Fri, 2009-11-13 at 14:50 +0000, Mark Brown wrote:
Please remember to keep people CCed on mails.
This looks suspicious - it looks like what's going on is that you're getting a notification from the DSP that it has consumed a data buffer but this might go out of bounds and get clamped.
The first time that it's called prtd->out[idx].used = BUF_INVALID_LEN so it's skipped(it's the same first bogus time than in my previous mails).
My more general comment on this stuff was that all the buffer management code was extremely difficult to follow and hence likely to have errors.
participants (7)
-
Clemens Ladisch
-
Daniel Mack
-
Denis 'GNUtoo' Carikli
-
GNUtoo
-
M R Swami Reddy
-
Mark Brown
-
Pavel Machek