diff options
author | Vishal Bhoj <vishal.bhoj@linaro.org> | 2011-10-11 09:50:49 +0530 |
---|---|---|
committer | Vishal Bhoj <vishal.bhoj@linaro.org> | 2011-10-11 09:50:49 +0530 |
commit | 79167a44bef2aa696923ac264f54f3a24516e022 (patch) | |
tree | 9a903b9d7b29791a6855c191250d1f680c599c78 | |
download | pandaboard-79167a44bef2aa696923ac264f54f3a24516e022.tar.gz |
-rw-r--r-- | modules/alsa/Android.mk | 35 | ||||
-rw-r--r-- | modules/alsa/alsa_omap4.cpp | 551 |
2 files changed, 586 insertions, 0 deletions
diff --git a/modules/alsa/Android.mk b/modules/alsa/Android.mk new file mode 100644 index 0000000..76a1aef --- /dev/null +++ b/modules/alsa/Android.mk @@ -0,0 +1,35 @@ +ifeq ($(strip $(BOARD_USES_ALSA_AUDIO)),true) + +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_PRELINK_MODULE := false + +LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw + +LOCAL_CFLAGS := -D_POSIX_SOURCE -Wno-multichar + +LOCAL_C_INCLUDES += \ + external/alsa-lib/include \ + hardware/alsa_sound + +ifneq ($(ALSA_DEFAULT_SAMPLE_RATE),) + LOCAL_CFLAGS += -DALSA_DEFAULT_SAMPLE_RATE=$(ALSA_DEFAULT_SAMPLE_RATE) +endif + +LOCAL_C_INCLUDES += external/alsa-lib/include + +LOCAL_SRC_FILES:= alsa_omap4.cpp + +LOCAL_SHARED_LIBRARIES := \ + libaudio \ + libasound \ + liblog + +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE:= alsa.omap4 + +include $(BUILD_SHARED_LIBRARY) + +endif diff --git a/modules/alsa/alsa_omap4.cpp b/modules/alsa/alsa_omap4.cpp new file mode 100644 index 0000000..3d987d4 --- /dev/null +++ b/modules/alsa/alsa_omap4.cpp @@ -0,0 +1,551 @@ +/* alsa_default.cpp + ** + ** Copyright 2009 Wind River Systems + ** + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** + ** http://www.apache.org/licenses/LICENSE-2.0 + ** + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + */ + +#define LOG_TAG "ALSAModule" +#include <utils/Log.h> + +#include "AudioHardwareALSA.h" +#include <media/AudioRecord.h> + +#undef DISABLE_HARWARE_RESAMPLING + +#define ALSA_NAME_MAX 128 + +#define ALSA_STRCAT(x,y) \ + if (strlen(x) + strlen(y) < ALSA_NAME_MAX) \ + strcat(x, y); + +#ifndef ALSA_DEFAULT_SAMPLE_RATE +#define ALSA_DEFAULT_SAMPLE_RATE 44100 // in Hz +#endif + +#define DEFAULT_DEVICE "plughw:0,0" + +namespace android +{ + +static int s_device_open(const hw_module_t*, const char*, hw_device_t**); +static int s_device_close(hw_device_t*); +static status_t s_init(alsa_device_t *, ALSAHandleList &); +static status_t s_open(alsa_handle_t *, uint32_t, int); +static status_t s_close(alsa_handle_t *); +static status_t s_route(alsa_handle_t *, uint32_t, int); + +static void setAlsaControls(alsa_handle_t *, uint32_t, int); + +static hw_module_methods_t s_module_methods = { + open : s_device_open +}; + +extern "C" const hw_module_t HAL_MODULE_INFO_SYM = { + tag : HARDWARE_MODULE_TAG, + version_major : 1, + version_minor : 0, + id : ALSA_HARDWARE_MODULE_ID, + name : "ALSA module", + author : "Wind River", + methods : &s_module_methods, + dso : 0, + reserved : { 0, }, +}; + +static int s_device_open(const hw_module_t* module, const char* name, + hw_device_t** device) +{ + alsa_device_t *dev; + dev = (alsa_device_t *) malloc(sizeof(*dev)); + if (!dev) return -ENOMEM; + + memset(dev, 0, sizeof(*dev)); + + /* initialize the procs */ + dev->common.tag = HARDWARE_DEVICE_TAG; + dev->common.version = 0; + dev->common.module = (hw_module_t *) module; + dev->common.close = s_device_close; + dev->init = s_init; + dev->open = s_open; + dev->close = s_close; + dev->route = s_route; + + *device = &dev->common; + return 0; +} + +static int s_device_close(hw_device_t* device) +{ + free(device); + return 0; +} + +// ---------------------------------------------------------------------------- + +static const int DEFAULT_SAMPLE_RATE = ALSA_DEFAULT_SAMPLE_RATE; + +static const char *devicePrefix[SND_PCM_STREAM_LAST + 1] = { + /* SND_PCM_STREAM_PLAYBACK : */"AndroidPlayback", + /* SND_PCM_STREAM_CAPTURE : */"AndroidCapture", +}; + +static alsa_handle_t _defaultsOut = { + module : 0, + devices : AudioSystem::DEVICE_OUT_ALL, + curDev : 0, + curMode : 0, + handle : 0, + format : SND_PCM_FORMAT_S16_LE, // AudioSystem::PCM_16_BIT + channels : 2, + sampleRate : DEFAULT_SAMPLE_RATE, + latency : 200000, // Desired Delay in usec + bufferSize : DEFAULT_SAMPLE_RATE / 5, // Desired Number of samples + modPrivate : 0, +}; + +static alsa_handle_t _defaultsIn = { + module : 0, + devices : AudioSystem::DEVICE_IN_ALL, + curDev : 0, + curMode : 0, + handle : 0, + format : SND_PCM_FORMAT_S16_LE, // AudioSystem::PCM_16_BIT + channels : 1, + sampleRate : AudioRecord::DEFAULT_SAMPLE_RATE, + latency : 250000, // Desired Delay in usec + bufferSize : 2048, // Desired Number of samples + modPrivate : 0, +}; + +struct device_suffix_t { + const AudioSystem::audio_devices device; + const char *suffix; +}; + +/* The following table(s) need to match in order of the route bits + */ +static const device_suffix_t deviceSuffix[] = { + {AudioSystem::DEVICE_OUT_EARPIECE, "_Earpiece"}, + {AudioSystem::DEVICE_OUT_SPEAKER, "_Speaker"}, + {AudioSystem::DEVICE_OUT_BLUETOOTH_SCO, "_Bluetooth"}, + {AudioSystem::DEVICE_OUT_WIRED_HEADSET, "_Headset"}, + {AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP, "_Bluetooth-A2DP"}, +}; + +static const int deviceSuffixLen = (sizeof(deviceSuffix) + / sizeof(device_suffix_t)); + +// ---------------------------------------------------------------------------- + +snd_pcm_stream_t direction(alsa_handle_t *handle) +{ + return (handle->devices & AudioSystem::DEVICE_OUT_ALL) ? SND_PCM_STREAM_PLAYBACK + : SND_PCM_STREAM_CAPTURE; +} + +const char *deviceName(alsa_handle_t *handle, uint32_t device, int mode) +{ + static char devString[ALSA_NAME_MAX]; + int hasDevExt = 0; + return DEFAULT_DEVICE; +} + +const char *streamName(alsa_handle_t *handle) +{ + return snd_pcm_stream_name(direction(handle)); +} + +status_t setHardwareParams(alsa_handle_t *handle) +{ + snd_pcm_hw_params_t *hardwareParams; + status_t err; + + snd_pcm_uframes_t bufferSize = handle->bufferSize; + unsigned int requestedRate = handle->sampleRate; + unsigned int latency = handle->latency; + + // snd_pcm_format_description() and snd_pcm_format_name() do not perform + // proper bounds checking. + bool validFormat = (static_cast<int> (handle->format) + > SND_PCM_FORMAT_UNKNOWN) && (static_cast<int> (handle->format) + <= SND_PCM_FORMAT_LAST); + const char *formatDesc = validFormat ? snd_pcm_format_description( + handle->format) : "Invalid Format"; + const char *formatName = validFormat ? snd_pcm_format_name(handle->format) + : "UNKNOWN"; + + if (snd_pcm_hw_params_malloc(&hardwareParams) < 0) { + LOG_ALWAYS_FATAL("Failed to allocate ALSA hardware parameters!"); + return NO_INIT; + } + + err = snd_pcm_hw_params_any(handle->handle, hardwareParams); + if (err < 0) { + LOGE("Unable to configure hardware: %s", snd_strerror(err)); + goto done; + } + + // Set the interleaved read and write format. + err = snd_pcm_hw_params_set_access(handle->handle, hardwareParams, + SND_PCM_ACCESS_RW_INTERLEAVED); + if (err < 0) { + LOGE("Unable to configure PCM read/write format: %s", + snd_strerror(err)); + goto done; + } + + err = snd_pcm_hw_params_set_format(handle->handle, hardwareParams, + handle->format); + if (err < 0) { + LOGE("Unable to configure PCM format %s (%s): %s", + formatName, formatDesc, snd_strerror(err)); + goto done; + } + + LOGV("Set %s PCM format to %s (%s)", streamName(), formatName, formatDesc); + + err = snd_pcm_hw_params_set_channels(handle->handle, hardwareParams, + handle->channels); + if (err < 0) { + LOGE("Unable to set channel count to %i: %s", + handle->channels, snd_strerror(err)); + goto done; + } + + LOGV("Using %i %s for %s.", handle->channels, + handle->channels == 1 ? "channel" : "channels", streamName()); + + err = snd_pcm_hw_params_set_rate_near(handle->handle, hardwareParams, + &requestedRate, 0); + + if (err < 0) + LOGE("Unable to set %s sample rate to %u: %s", + streamName(handle), handle->sampleRate, snd_strerror(err)); + else if (requestedRate != handle->sampleRate) + // Some devices have a fixed sample rate, and can not be changed. + // This may cause resampling problems; i.e. PCM playback will be too + // slow or fast. + LOGW("Requested rate (%u HZ) does not match actual rate (%u HZ)", + handle->sampleRate, requestedRate); + else + LOGV("Set %s sample rate to %u HZ", stream, requestedRate); + +#ifdef DISABLE_HARWARE_RESAMPLING + // Disable hardware re-sampling. + err = snd_pcm_hw_params_set_rate_resample(handle->handle, + hardwareParams, + static_cast<int>(resample)); + if (err < 0) { + LOGE("Unable to %s hardware resampling: %s", + resample ? "enable" : "disable", + snd_strerror(err)); + goto done; + } +#endif + + // Make sure we have at least the size we originally wanted + err = snd_pcm_hw_params_set_buffer_size_near(handle->handle, hardwareParams, + &bufferSize); + + if (err < 0) { + LOGE("Unable to set buffer size to %d: %s", + (int)bufferSize, snd_strerror(err)); + goto done; + } + + // Setup buffers for latency + err = snd_pcm_hw_params_set_buffer_time_near(handle->handle, + hardwareParams, &latency, NULL); + if (err < 0) { + /* That didn't work, set the period instead */ + unsigned int periodTime = latency / 4; + err = snd_pcm_hw_params_set_period_time_near(handle->handle, + hardwareParams, &periodTime, NULL); + if (err < 0) { + LOGE("Unable to set the period time for latency: %s", snd_strerror(err)); + goto done; + } + snd_pcm_uframes_t periodSize; + err = snd_pcm_hw_params_get_period_size(hardwareParams, &periodSize, + NULL); + if (err < 0) { + LOGE("Unable to get the period size for latency: %s", snd_strerror(err)); + goto done; + } + bufferSize = periodSize * 4; + if (bufferSize < handle->bufferSize) bufferSize = handle->bufferSize; + err = snd_pcm_hw_params_set_buffer_size_near(handle->handle, + hardwareParams, &bufferSize); + if (err < 0) { + LOGE("Unable to set the buffer size for latency: %s", snd_strerror(err)); + goto done; + } + } else { + // OK, we got buffer time near what we expect. See what that did for bufferSize. + err = snd_pcm_hw_params_get_buffer_size(hardwareParams, &bufferSize); + if (err < 0) { + LOGE("Unable to get the buffer size for latency: %s", snd_strerror(err)); + goto done; + } + // Does set_buffer_time_near change the passed value? It should. + err = snd_pcm_hw_params_get_buffer_time(hardwareParams, &latency, NULL); + if (err < 0) { + LOGE("Unable to get the buffer time for latency: %s", snd_strerror(err)); + goto done; + } + unsigned int periodTime = latency / 4; + err = snd_pcm_hw_params_set_period_time_near(handle->handle, + hardwareParams, &periodTime, NULL); + if (err < 0) { + LOGE("Unable to set the period time for latency: %s", snd_strerror(err)); + goto done; + } + } + + LOGV("Buffer size: %d", (int)bufferSize); + LOGV("Latency: %d", (int)latency); + + handle->bufferSize = bufferSize; + handle->latency = latency; + + // Commit the hardware parameters back to the device. + err = snd_pcm_hw_params(handle->handle, hardwareParams); + if (err < 0) LOGE("Unable to set hardware parameters: %s", snd_strerror(err)); + + done: + snd_pcm_hw_params_free(hardwareParams); + + return err; +} + +status_t setSoftwareParams(alsa_handle_t *handle) +{ + snd_pcm_sw_params_t * softwareParams; + int err; + + snd_pcm_uframes_t bufferSize = 0; + snd_pcm_uframes_t periodSize = 0; + snd_pcm_uframes_t startThreshold, stopThreshold; + + if (snd_pcm_sw_params_malloc(&softwareParams) < 0) { + LOG_ALWAYS_FATAL("Failed to allocate ALSA software parameters!"); + return NO_INIT; + } + + // Get the current software parameters + err = snd_pcm_sw_params_current(handle->handle, softwareParams); + if (err < 0) { + LOGE("Unable to get software parameters: %s", snd_strerror(err)); + goto done; + } + + // Configure ALSA to start the transfer when the buffer is almost full. + snd_pcm_get_params(handle->handle, &bufferSize, &periodSize); + + if (handle->devices & AudioSystem::DEVICE_OUT_ALL) { + // For playback, configure ALSA to start the transfer when the + // buffer is full. + startThreshold = bufferSize - 1; + stopThreshold = bufferSize; + } else { + // For recording, configure ALSA to start the transfer on the + // first frame. + startThreshold = 1; + stopThreshold = bufferSize; + } + + err = snd_pcm_sw_params_set_start_threshold(handle->handle, softwareParams, + startThreshold); + if (err < 0) { + LOGE("Unable to set start threshold to %lu frames: %s", + startThreshold, snd_strerror(err)); + goto done; + } + + err = snd_pcm_sw_params_set_stop_threshold(handle->handle, softwareParams, + stopThreshold); + if (err < 0) { + LOGE("Unable to set stop threshold to %lu frames: %s", + stopThreshold, snd_strerror(err)); + goto done; + } + + // Allow the transfer to start when at least periodSize samples can be + // processed. + err = snd_pcm_sw_params_set_avail_min(handle->handle, softwareParams, + periodSize); + if (err < 0) { + LOGE("Unable to configure available minimum to %lu: %s", + periodSize, snd_strerror(err)); + goto done; + } + + // Commit the software parameters back to the device. + err = snd_pcm_sw_params(handle->handle, softwareParams); + if (err < 0) LOGE("Unable to configure software parameters: %s", + snd_strerror(err)); + + done: + snd_pcm_sw_params_free(softwareParams); + + return err; +} + +// ---------------------------------------------------------------------------- + +static status_t s_init(alsa_device_t *module, ALSAHandleList &list) +{ + list.clear(); + + snd_pcm_uframes_t bufferSize = _defaultsOut.bufferSize; + + for (size_t i = 1; (bufferSize & ~i) != 0; i <<= 1) + bufferSize &= ~i; + + _defaultsOut.module = module; + _defaultsOut.bufferSize = bufferSize; + + list.push_back(_defaultsOut); + + bufferSize = _defaultsIn.bufferSize; + + for (size_t i = 1; (bufferSize & ~i) != 0; i <<= 1) + bufferSize &= ~i; + + _defaultsIn.module = module; + _defaultsIn.bufferSize = bufferSize; + + list.push_back(_defaultsIn); + + return NO_ERROR; +} + +static status_t s_open(alsa_handle_t *handle, uint32_t devices, int mode) +{ + // Close off previously opened device. + // It would be nice to determine if the underlying device actually + // changes, but we might be recovering from an error or manipulating + // mixer settings (see asound.conf). + // + s_close(handle); + + LOGD("open called for devices %08x in mode %d...", devices, mode); + + const char *stream = streamName(handle); + const char *devName = deviceName(handle, devices, mode); + + // ASoC multicomponent requires a valid path (frontend/backend) for + // the device to be opened + setAlsaControls(handle, devices, mode); + + int err; + + for (;;) { + // The PCM stream is opened in blocking mode, per ALSA defaults. The + // AudioFlinger seems to assume blocking mode too, so asynchronous mode + // should not be used. + err = snd_pcm_open(&handle->handle, devName, direction(handle), + SND_PCM_ASYNC); + if (err == 0) break; + + // See if there is a less specific name we can try. + // Note: We are changing the contents of a const char * here. + char *tail = strrchr(devName, '_'); + if (!tail) break; + *tail = 0; + } + + if (err < 0) { + // None of the Android defined audio devices exist. Open a generic one. + devName = "default"; + err = snd_pcm_open(&handle->handle, devName, direction(handle), 0); + } + + if (err < 0) { + LOGE("Failed to Initialize any ALSA %s device: %s", + stream, strerror(err)); + return NO_INIT; + } + + err = setHardwareParams(handle); + + if (err == NO_ERROR) err = setSoftwareParams(handle); + + LOGI("Initialized ALSA %s device %s", stream, devName); + + handle->curDev = devices; + handle->curMode = mode; + + return err; +} + +static status_t s_close(alsa_handle_t *handle) +{ + status_t err = NO_ERROR; + snd_pcm_t *h = handle->handle; + handle->handle = 0; + handle->curDev = 0; + handle->curMode = 0; + if (h) { + snd_pcm_drain(h); + err = snd_pcm_close(h); + } + + return err; +} + +static status_t s_route(alsa_handle_t *handle, uint32_t devices, int mode) +{ + LOGD("route called for devices %08x in mode %d...", devices, mode); + + if (handle->handle && handle->curDev == devices && handle->curMode == mode) return NO_ERROR; + + return s_open(handle, devices, mode); +} + + +static void setAlsaControls(alsa_handle_t *handle, uint32_t devices, int mode) +{ + LOGD("***************%s: devices %08x mode %d", __FUNCTION__, devices, mode); + ALSAControl control("hw:00"); + /* check whether the devices is input or not */ + /* for output devices */ + if (devices & 0x0000FFFF) + { + LOGD("*********%s: devices %08x mode %d", __FUNCTION__, devices, mode); + control.set("DL1 Mixer Multimedia",1); + control.set("DL1 Media Playback Volume",118); + control.set("Sidetone Mixer Playback",1); + control.set("SDT DL Volume",120); + control.set("DL1 PDM Switch",1); + control.set("HS Left Playback","HS DAC"); + control.set("HS Right Playback","HS DAC"); + control.set("Headset Playback Volume",13); + } + else + { + LOGD("Configuring Input Device"); + control.set("AMIC_UL PDM Switch",1); + control.set("MUX_UL00","AMic0"); + control.set("MUX_UL01","AMic1"); + control.set("Analog Left Capture Route","Headset Mic"); + control.set("Analog Right Capture Route","Headset Mic"); + control.set("Capture Preamplifier Volume",2); + control.set("Capture Volume",4); + } + +} + +} |