summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--modules/alsa/Android.mk35
-rw-r--r--modules/alsa/alsa_omap4.cpp551
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);
+ }
+
+}
+
+}