From 219d03e97df76d7729b1993dfd9def6b1dc3325d Mon Sep 17 00:00:00 2001 From: Manjula Gupta Date: Mon, 14 Jun 2010 17:55:36 +0530 Subject: tegra ALSA: Update ALSA Soc in 2.6.32 kernel Merging all ALSA changes from 2.6.29 kernel to 2.6.32 kernel. To avoid conflicts and dependencies merging all the changes in one commit. This commit include the following changes from 2.6.29: - Handle Underrun Case In case of underrun application triggers stop and starts the playback again, don't exit the thread in case of stop. underrun can happen when an application does not feed new samples in time to alsa-lib (due to CPU usage). With this change breaks may be observed in case of underrun which cannot be avoided, but the application will finish the playback and exits gracefully. - Fix Playback hang in case of abnormal termination In case of stop mixer might not send buffer done for all the buffers queued as there could be many buffers and it will be inefficeint to send buffer done for all the buffers, instead of buffer done mixer notifies client with mixer stop done event. Added the code to register for stop done event with the mixer, wait for mixer stop done, instead of buffer done on stop. Removing the redundant shutdown flag as it's not required. - Adding ALSA Controls Added master volume and mute control in ALSA SoC. Verified using the amixer application. - Do not queue buffers to the mixer in stopped In SNDRV_PCM_TRIGGER_STOP state of the driver, if a buffer gets queued then it might not be returned back as it never gets rendered. In this scenario play_thread never exits as it keep on waiting for receiving all the buffers back from mixer before exiting. - Replacing completion object with wait_queue_head_t Removed completion object appl_ptr_comp used for waiting for app buffers when buffer_in_quque=0. If a completion object wakes-up after every 120 seconds if not signalled during that time.Now using wait_queue_head_t which suits the purpose better. Change-Id: I17490c97b8400423d247545e6142e4aa2573070b Reviewed-on: http://git-master/r/2578 Tested-by: Manjula Gupta Reviewed-by: Gary King --- sound/soc/tegra/tegra_codec_rpc.c | 72 ++++++++++++++++++++++++++++++++++++++- sound/soc/tegra/tegra_pcm_rpc.c | 46 ++++++++++++------------- sound/soc/tegra/tegra_sndfx.h | 31 ++++++++++++----- sound/soc/tegra/tegra_transport.c | 10 ++++-- sound/soc/tegra/tegra_transport.h | 4 ++- 5 files changed, 128 insertions(+), 35 deletions(-) (limited to 'sound/soc/tegra') diff --git a/sound/soc/tegra/tegra_codec_rpc.c b/sound/soc/tegra/tegra_codec_rpc.c index 943436bdefaf..3e22d670f2d8 100644 --- a/sound/soc/tegra/tegra_codec_rpc.c +++ b/sound/soc/tegra/tegra_codec_rpc.c @@ -28,6 +28,75 @@ #include "tegra_transport.h" +extern struct tegra_audio_data* tegra_snd_cx; + +static int tegra_master_volume_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = NvAudioFxVolumeMax; + return 0; +} + +static int tegra_master_volume_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + if (tegra_snd_cx) { + ucontrol->value.integer.value[0] = tegra_snd_cx->i2s1volume; + ucontrol->value.integer.value[1] = tegra_snd_cx->i2s1volume; + } + else { + ucontrol->value.integer.value[0] = NvAudioFxVolumeDefault; + ucontrol->value.integer.value[1] = NvAudioFxVolumeDefault; + } + + return 0; +} + +static int tegra_master_volume_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int change = 0, val; + NvAudioFxVolumeDescriptor vd; + val = ucontrol->value.integer.value[0] & 0xffff; + vd.LeftVolume = val; + vd.RightVolume = val; + if(val) { + vd.Mute = 0; + } + else { + vd.Mute = 1; + } + + if (tegra_snd_cx && tegra_snd_cx->mixer_handle) { + if(tegra_snd_cx->i2s1volume != val) { + tegra_snd_cx->i2s1volume = val; + tegra_snd_cx->xrt_fxn.SetProperty( + tegra_snd_cx->mvolume, + NvAudioFxVolumeProperty_Volume, + sizeof(NvAudioFxVolumeDescriptor), + &vd); + change = 1; + snd_printk(KERN_ERR "Put Volume Change = 1\n"); + } + } + + return change; +} + +static struct snd_kcontrol_new tegra_codec_control = +{ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Master Playback Volume", + .private_value = 0xffff, + .info = tegra_master_volume_info, + .get = tegra_master_volume_get, + .put = tegra_master_volume_put +}; + static int tegra_generic_codec_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) @@ -140,7 +209,8 @@ static int codec_soc_probe(struct platform_device *pdev) goto card_err; } - return ret; + return snd_ctl_add(codec->card, + snd_ctl_new1(&tegra_codec_control, codec)); card_err: snd_soc_free_pcms(socdev); diff --git a/sound/soc/tegra/tegra_pcm_rpc.c b/sound/soc/tegra/tegra_pcm_rpc.c index 6b486d10110a..bfa2a4d459c9 100644 --- a/sound/soc/tegra/tegra_pcm_rpc.c +++ b/sound/soc/tegra/tegra_pcm_rpc.c @@ -22,7 +22,7 @@ #include "tegra_transport.h" -static struct tegra_audio_data* tegra_snd_cx = NULL; +struct tegra_audio_data* tegra_snd_cx = NULL; static const struct snd_pcm_hardware tegra_pcm_hardware = { .info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_PAUSE |\ @@ -96,6 +96,8 @@ static int play_thread( void *arg) NvAudioFxProperty_State, sizeof(NvAudioFxState), &state); + down(&prtd->stop_done_sem); + buffer_in_queue = 0; prtd->state = NVALSA_INVALID_STATE; default: ; @@ -170,7 +172,6 @@ static int play_thread( void *arg) if (period_offset >= runtime->period_size) { prtd->last_pos = prtd->cur_pos; snd_pcm_period_elapsed(substream); - } if (prtd->cur_pos >= runtime->buffer_size) { @@ -178,10 +179,6 @@ static int play_thread( void *arg) } } } - while (buffer_in_queue > 0) { - down(&prtd->buf_done_sem); - buffer_in_queue--; - } return 0; } @@ -204,7 +201,7 @@ static int rec_thread( void *arg ) rtbuffersize = frames_to_bytes(runtime, runtime->buffer_size); buffer_to_prime = (rtbuffersize / TEGRA_DEFAULT_BUFFER_SIZE); - while (!prtd->shutdown_thrd ) { + for (;;) { switch (prtd->state) { case SNDRV_PCM_TRIGGER_START: pin_format.Format.FormatTag = 1; @@ -242,17 +239,14 @@ static int rec_thread( void *arg ) prtd->state = NVALSA_INVALID_STATE; break; case SNDRV_PCM_TRIGGER_STOP: - while (buffer_in_queue > 0) { - down(&prtd->buf_done_sem); - buffer_in_queue--; - } - state = NvAudioFxState_Stop; tegra_snd_cx->xrt_fxn.SetProperty( prtd->stdinpath->Stream, NvAudioFxProperty_State, sizeof(NvAudioFxState), &state); + down(&prtd->stop_done_sem); + buffer_in_queue = 0; prtd->state = NVALSA_INVALID_STATE; goto EXIT; default: @@ -439,6 +433,11 @@ static int init_mixer(struct snd_pcm_substream *substream) if (!tegra_snd_cx->mixer_buffer[1]) { snd_printk(KERN_ERR"TransportMixerMapBuffer failed!\n"); } + + tegra_snd_cx->mvolume = tegra_snd_cx->xrt_fxn.MixerCreateObject( + tegra_snd_cx->mixer_handle, + NvAudioFxI2s1VolumeId); + tegra_snd_cx->i2s1volume = NvAudioFxVolumeDefault; } return 0; @@ -482,13 +481,10 @@ static int pcm_common_close(struct snd_pcm_substream *substream) if (prtd->rec_thread) kthread_stop(prtd->rec_thread); - prtd->shutdown_thrd = 1; - up(&prtd->buf_done_sem); - - if (tegra_snd_cx->m_FxNotifier.Event & NvAudioFxEventBufferDone) { + if (tegra_snd_cx->m_FxNotifier.Event) { memset(&message, 0, sizeof(NvAudioFxMessage)); - message.Event = NvAudioFxEventBufferDone; + message.Event = NvAudioFxEventAll; message.pContext = NULL; if(substream->stream == SNDRV_PCM_STREAM_PLAYBACK) message.hFx = (NvAudioFxHandle)prtd->stdoutpath->Stream; @@ -501,7 +497,7 @@ static int pcm_common_close(struct snd_pcm_substream *substream) sizeof(NvAudioFxMessage), &message); - tegra_snd_cx->m_FxNotifier.Event &= ~(NvAudioFxEventBufferDone); + tegra_snd_cx->m_FxNotifier.Event &= ~NvAudioFxEventAll; } if (prtd->stdoutpath) { @@ -540,7 +536,6 @@ static int tegra_pcm_open(struct snd_pcm_substream *substream) prtd->stdinpath = 0; prtd->state = NVALSA_INVALID_STATE; prtd->stream = substream->stream; - prtd->shutdown_thrd = 0; ret = init_mixer(substream); if (ret) @@ -549,6 +544,7 @@ static int tegra_pcm_open(struct snd_pcm_substream *substream) init_completion(&prtd->thread_comp); init_waitqueue_head(&prtd->buf_wait); sema_init(&prtd->buf_done_sem, 0); + sema_init(&prtd->stop_done_sem, 0); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK){ prtd->mixer_buffer = tegra_snd_cx->mixer_buffer[0]; @@ -570,7 +566,8 @@ static int tegra_pcm_open(struct snd_pcm_substream *substream) } memset(&message, 0, sizeof(NvAudioFxMessage)); - message.Event = NvAudioFxEventBufferDone; + message.Event = (NvAudioFxEventBufferDone | + NvAudioFxEventStateChange); message.hFx = (NvAudioFxHandle)prtd->stdoutpath->Stream; message.pContext = prtd; @@ -586,7 +583,8 @@ static int tegra_pcm_open(struct snd_pcm_substream *substream) goto fail; } - tegra_snd_cx->m_FxNotifier.Event |= (NvAudioFxEventBufferDone); + tegra_snd_cx->m_FxNotifier.Event |= (NvAudioFxEventBufferDone | + NvAudioFxEventStateChange); prtd->play_thread = kthread_run(play_thread, substream, @@ -617,7 +615,8 @@ static int tegra_pcm_open(struct snd_pcm_substream *substream) } memset(&message, 0, sizeof(NvAudioFxMessage)); - message.Event = NvAudioFxEventBufferDone; + message.Event = (NvAudioFxEventBufferDone | + NvAudioFxEventStateChange); message.hFx = (NvAudioFxHandle)prtd->stdinpath->Stream; message.pContext = prtd; @@ -631,7 +630,8 @@ static int tegra_pcm_open(struct snd_pcm_substream *substream) ret = -EFAULT; goto fail; } - tegra_snd_cx->m_FxNotifier.Event |= (NvAudioFxEventBufferDone); + tegra_snd_cx->m_FxNotifier.Event |= (NvAudioFxEventBufferDone | + NvAudioFxEventStateChange); prtd->rec_thread = kthread_run(rec_thread, substream, diff --git a/sound/soc/tegra/tegra_sndfx.h b/sound/soc/tegra/tegra_sndfx.h index 5b59b7587a82..2cc38e557290 100644 --- a/sound/soc/tegra/tegra_sndfx.h +++ b/sound/soc/tegra/tegra_sndfx.h @@ -281,6 +281,29 @@ typedef struct NvAudioFxSpreaderDescriptorRec NvU32 SpeakerWidth; } NvAudioFxSpreaderDescriptor; +// I2S inputs. + +typedef NvS32 NvAudioFxI2sInputSelect; + +// Default is configurable based on the device. +#define NvAudioFxI2sInputSelect_Default (0x0) +#define NvAudioFxI2sInputSelect_Bluetooth (0x1) +#define NvAudioFxI2sInputSelect_BuiltinMic (0x2) +#define NvAudioFxI2sInputSelect_LineIn (0x3) +#define NvAudioFxI2sInputSelect_Mic (0x4) +#define NvAudioFxI2sInputSelect_Phone (0x5) +#define NvAudioFxI2sInputSelect_Radio (0x6) + +// Description of the NvAudioFxI2sProperty_AllocChannel property. + +typedef struct NvAudioFxI2sChannelDescriptorRec +{ + NvAudioFxPin Pin; + NvU32 Id; +} NvAudioFxI2sChannelDescriptor; + +// Parameteric EQ Filter types. + typedef enum { NvAudioFxIirFilter_Undefined, @@ -395,14 +418,6 @@ typedef struct NvAudioFxNotifierConnectionDescriptorRec NvU8 PortName[16]; } NvAudioFxNotifierConnectionDescriptor; -// Description of the NvAudioFxI2sProperty_AllocChannel property. - -typedef struct NvAudioFxI2sChannelDescriptorRec -{ - NvAudioFxPin Pin; - NvU32 Id; -} NvAudioFxI2sChannelDescriptor; - // Description of the NvAudioFxProperty_AddEvent and // NvAudioFxProperty_RemoveEvent properties. diff --git a/sound/soc/tegra/tegra_transport.c b/sound/soc/tegra/tegra_transport.c index b7c8e71d1992..161ed15f1e0b 100644 --- a/sound/soc/tegra/tegra_transport.c +++ b/sound/soc/tegra/tegra_transport.c @@ -523,8 +523,14 @@ static void tegra_audiofx_notifier_thread(void *arg) } break; - case NvAudioFxEventStateChange: - break; + case NvAudioFxEventStateChange:{ + NvAudioFxStateChangeMessage* scm = + (NvAudioFxStateChangeMessage*)message; + struct pcm_runtime_data* prtd = + (struct pcm_runtime_data*)scm->m.pContext; + up(&prtd->stop_done_sem); + } + break; default: snd_printk(KERN_ERR"Unhandled event\n"); diff --git a/sound/soc/tegra/tegra_transport.h b/sound/soc/tegra/tegra_transport.h index 0093fef6383b..40e4c9eaa11d 100644 --- a/sound/soc/tegra/tegra_transport.h +++ b/sound/soc/tegra/tegra_transport.h @@ -390,11 +390,11 @@ struct pcm_runtime_data { int timeout; int state; int stream; - int shutdown_thrd; unsigned int audiofx_frames; struct completion thread_comp; wait_queue_head_t buf_wait; struct semaphore buf_done_sem; + struct semaphore stop_done_sem; StandardPath* stdoutpath; StandardPath* stdinpath; u64 cur_pos; @@ -410,6 +410,8 @@ struct tegra_audio_data { unsigned int mapped_buf_size; NvAudioFxMixBufferHandle mixer_buffer[2]; NvRmMemHandle mem_handle[2]; + NvAudioFxObjectHandle mvolume; + int i2s1volume; struct mutex lock; }; -- cgit v1.2.3