summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAaron Echols <atechols@gmail.com>2012-10-10 16:08:18 -0700
committerAaron Echols <atechols@gmail.com>2012-10-10 16:08:18 -0700
commit901a04c71f7c9cf2572e7da73ec859c0e8e78cb4 (patch)
treed63cffeaebdcaa0a5d27f069f4961c8382fc2888
parente86decfec11ab03e16fc65e67fce1c042cddd2fe (diff)
downloadtf700t-901a04c71f7c9cf2572e7da73ec859c0e8e78cb4.tar.gz
tf700t: switch to opensource audio hal
Change-Id: I7dd8c75ed5df7f32487646d6ba3d825977ed4f90
-rw-r--r--audio/Android.mk32
-rw-r--r--audio/audio_hw.c1340
-rw-r--r--audio/audio_route.c494
-rw-r--r--audio/audio_route.h33
-rw-r--r--device_tf700t.mk5
-rw-r--r--prebuilt/mixer_paths.xml38
-rw-r--r--proprietary-files.txt2
7 files changed, 1942 insertions, 2 deletions
diff --git a/audio/Android.mk b/audio/Android.mk
new file mode 100644
index 0000000..5003282
--- /dev/null
+++ b/audio/Android.mk
@@ -0,0 +1,32 @@
+# Copyright (C) 2012 The Android Open Source Project
+#
+# 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.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := audio.primary.cardhu
+LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
+LOCAL_SRC_FILES := \
+ audio_hw.c \
+ audio_route.c
+LOCAL_C_INCLUDES += \
+ external/tinyalsa/include \
+ external/expat/lib \
+ $(call include-path-for, audio-utils)
+LOCAL_SHARED_LIBRARIES := liblog libcutils libtinyalsa libaudioutils libexpat
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_SHARED_LIBRARY)
+
diff --git a/audio/audio_hw.c b/audio/audio_hw.c
new file mode 100644
index 0000000..02ead96
--- /dev/null
+++ b/audio/audio_hw.c
@@ -0,0 +1,1340 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * 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 "audio_hw_primary"
+/*#define LOG_NDEBUG 0*/
+
+#include <errno.h>
+#include <pthread.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <sys/time.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+
+#include <cutils/log.h>
+#include <cutils/properties.h>
+#include <cutils/str_parms.h>
+
+#include <hardware/audio.h>
+#include <hardware/hardware.h>
+
+#include <system/audio.h>
+
+#include <tinyalsa/asoundlib.h>
+
+#include <audio_utils/resampler.h>
+
+#include "audio_route.h"
+
+#define PCM_CARD 1
+#define PCM_DEVICE 0
+#define PCM_DEVICE_SCO 2
+
+#define OUT_PERIOD_SIZE 1024
+#define OUT_SHORT_PERIOD_COUNT 2
+#define OUT_LONG_PERIOD_COUNT 8
+#define OUT_SAMPLING_RATE 44100
+
+#define IN_PERIOD_SIZE 1024
+#define IN_PERIOD_COUNT 4
+#define IN_SAMPLING_RATE 44100
+
+#define SCO_PERIOD_SIZE 1024
+#define SCO_PERIOD_COUNT 4
+#define SCO_SAMPLING_RATE 44100
+
+/* minimum sleep time in out_write() when write threshold is not reached */
+#define MIN_WRITE_SLEEP_US 2000
+#define MAX_WRITE_SLEEP_US ((OUT_PERIOD_SIZE * OUT_SHORT_PERIOD_COUNT * 1000000) \
+ / OUT_SAMPLING_RATE)
+
+#define DSP_DEV_PATH "/dev/dsp_fm34"
+#define DSP_IOC_MAGIC 0xf3
+#define DSP_CONTROL _IOW(DSP_IOC_MAGIC, 1,int)
+#define START_RECORDING 1
+#define END_RECORDING 0
+#define PLAYBACK 2
+
+#define AUDIO_DEV_PATH "/dev/rt5631"
+#define AUDIO_IOC_MAGIC 0xf7
+#define AUDIO_CAPTURE_MODE _IOW(AUDIO_IOC_MAGIC, 6,int)
+#define INPUT_SOURCE_NORMAL 100
+#define INPUT_SOURCE_AGC 301
+#define OUTPUT_SOURCE_NORMAL 200
+
+bool isRecording = false;
+
+enum {
+ OUT_BUFFER_TYPE_UNKNOWN,
+ OUT_BUFFER_TYPE_SHORT,
+ OUT_BUFFER_TYPE_LONG,
+};
+
+struct pcm_config pcm_config_out = {
+ .channels = 2,
+ .rate = OUT_SAMPLING_RATE,
+ .period_size = OUT_PERIOD_SIZE,
+ .period_count = OUT_LONG_PERIOD_COUNT,
+ .format = PCM_FORMAT_S16_LE,
+ .start_threshold = OUT_PERIOD_SIZE * OUT_SHORT_PERIOD_COUNT,
+};
+
+struct pcm_config pcm_config_in = {
+ .channels = 2,
+ .rate = IN_SAMPLING_RATE,
+ .period_size = IN_PERIOD_SIZE,
+ .period_count = IN_PERIOD_COUNT,
+ .format = PCM_FORMAT_S16_LE,
+ .start_threshold = 1,
+ .stop_threshold = (IN_PERIOD_SIZE * IN_PERIOD_COUNT),
+};
+
+struct pcm_config pcm_config_sco = {
+ .channels = 1,
+ .rate = SCO_SAMPLING_RATE,
+ .period_size = SCO_PERIOD_SIZE,
+ .period_count = SCO_PERIOD_COUNT,
+ .format = PCM_FORMAT_S16_LE,
+};
+
+struct audio_device {
+ struct audio_hw_device hw_device;
+
+ pthread_mutex_t lock; /* see note below on mutex acquisition order */
+ unsigned int devices;
+ bool standby;
+ bool mic_mute;
+ struct audio_route *ar;
+ int orientation;
+ bool screen_off;
+
+ struct stream_out *active_out;
+ struct stream_in *active_in;
+};
+
+struct stream_out {
+ struct audio_stream_out stream;
+
+ pthread_mutex_t lock; /* see note below on mutex acquisition order */
+ struct pcm *pcm;
+ struct pcm_config *pcm_config;
+ bool standby;
+
+ struct resampler_itfe *resampler;
+ int16_t *buffer;
+ size_t buffer_frames;
+
+ int write_threshold;
+ int cur_write_threshold;
+ int buffer_type;
+
+ struct audio_device *dev;
+};
+
+struct stream_in {
+ struct audio_stream_in stream;
+
+ pthread_mutex_t lock; /* see note below on mutex acquisition order */
+ struct pcm *pcm;
+ struct pcm_config *pcm_config;
+ bool standby;
+
+ unsigned int requested_rate;
+ struct resampler_itfe *resampler;
+ struct resampler_buffer_provider buf_provider;
+ int16_t *buffer;
+ size_t buffer_size;
+ size_t frames_in;
+ int read_status;
+
+ struct audio_device *dev;
+};
+
+enum {
+ ORIENTATION_LANDSCAPE,
+ ORIENTATION_PORTRAIT,
+ ORIENTATION_SQUARE,
+ ORIENTATION_UNDEFINED,
+};
+
+static uint32_t out_get_sample_rate(const struct audio_stream *stream);
+static size_t out_get_buffer_size(const struct audio_stream *stream);
+static audio_format_t out_get_format(const struct audio_stream *stream);
+static uint32_t in_get_sample_rate(const struct audio_stream *stream);
+static size_t in_get_buffer_size(const struct audio_stream *stream);
+static audio_format_t in_get_format(const struct audio_stream *stream);
+static int get_next_buffer(struct resampler_buffer_provider *buffer_provider,
+ struct resampler_buffer* buffer);
+static void release_buffer(struct resampler_buffer_provider *buffer_provider,
+ struct resampler_buffer* buffer);
+
+/*
+ * NOTE: when multiple mutexes have to be acquired, always take the
+ * audio_device mutex first, followed by the stream_in and/or
+ * stream_out mutexes.
+ */
+
+/* Helper functions */
+
+static void select_devices(struct audio_device *adev)
+{
+ int headphone_on;
+ int speaker_on;
+ int main_mic_on;
+ int fm34_dev, rt5631_dev;
+ int ret;
+
+ fm34_dev = open(DSP_DEV_PATH,0);
+
+ rt5631_dev = open(AUDIO_DEV_PATH,0);
+
+ if (fm34_dev < 0)
+ ALOGE("open %s failed", DSP_DEV_PATH);
+
+ if (rt5631_dev < 0)
+ ALOGE("open %s failed", AUDIO_DEV_PATH);
+
+ headphone_on = adev->devices & (AUDIO_DEVICE_OUT_WIRED_HEADSET |
+ AUDIO_DEVICE_OUT_WIRED_HEADPHONE);
+ speaker_on = adev->devices & AUDIO_DEVICE_OUT_SPEAKER;
+ main_mic_on = adev->devices & AUDIO_DEVICE_IN_BUILTIN_MIC;
+
+ reset_mixer_state(adev->ar);
+
+ if (speaker_on) {
+ audio_route_apply_path(adev->ar, "speaker");
+ ALOGE("Speaker Is Online");
+
+ ioctl(fm34_dev, DSP_CONTROL, END_RECORDING);
+ ioctl(fm34_dev, DSP_CONTROL, PLAYBACK);
+ ioctl(rt5631_dev, AUDIO_CAPTURE_MODE, OUTPUT_SOURCE_NORMAL);
+
+ }
+ if (headphone_on){
+ audio_route_apply_path(adev->ar, "headphone");
+ ALOGE("Headset Is Online");
+
+ ioctl(fm34_dev, DSP_CONTROL, END_RECORDING);
+ ioctl(fm34_dev, DSP_CONTROL, PLAYBACK);
+ ioctl(rt5631_dev, AUDIO_CAPTURE_MODE, OUTPUT_SOURCE_NORMAL);
+
+ }
+ if (main_mic_on) {
+
+ ALOGE("MIC Is Online");
+
+ ioctl(fm34_dev, DSP_CONTROL, INPUT_SOURCE_AGC);
+ ioctl(fm34_dev, DSP_CONTROL, START_RECORDING);
+ ioctl(rt5631_dev, AUDIO_CAPTURE_MODE, INPUT_SOURCE_AGC);
+
+ if (adev->orientation == ORIENTATION_LANDSCAPE)
+ audio_route_apply_path(adev->ar, "main-mic-left");
+ else
+ audio_route_apply_path(adev->ar, "main-mic-top");
+ }
+
+ update_mixer_state(adev->ar);
+
+ ALOGV("hp=%c speaker=%c main-mic=%c", headphone_on ? 'y' : 'n',
+ speaker_on ? 'y' : 'n', main_mic_on ? 'y' : 'n');
+
+ if(fm34_dev > 0)
+ close(fm34_dev);
+
+ if(rt5631_dev > 0)
+ close(rt5631_dev);
+}
+
+/* must be called with hw device and output stream mutexes locked */
+static void do_out_standby(struct stream_out *out)
+{
+ struct audio_device *adev = out->dev;
+
+ if (!out->standby) {
+ pcm_close(out->pcm);
+ out->pcm = NULL;
+ adev->active_out = NULL;
+ if (out->resampler) {
+ release_resampler(out->resampler);
+ out->resampler = NULL;
+ }
+ if (out->buffer) {
+ free(out->buffer);
+ out->buffer = NULL;
+ }
+ out->standby = true;
+ }
+}
+
+/* must be called with hw device and input stream mutexes locked */
+static void do_in_standby(struct stream_in *in)
+{
+ struct audio_device *adev = in->dev;
+
+ if (!in->standby) {
+ pcm_close(in->pcm);
+ in->pcm = NULL;
+ adev->active_in = NULL;
+ if (in->resampler) {
+ release_resampler(in->resampler);
+ in->resampler = NULL;
+ }
+ if (in->buffer) {
+ free(in->buffer);
+ in->buffer = NULL;
+ }
+ in->standby = true;
+ }
+}
+
+/* must be called with hw device and output stream mutexes locked */
+static int start_output_stream(struct stream_out *out)
+{
+ struct audio_device *adev = out->dev;
+ unsigned int device;
+ int ret;
+
+ /*
+ * Due to the lack of sample rate converters in the SoC,
+ * it greatly simplifies things to have only the main
+ * (speaker/headphone) PCM or the BC SCO PCM open at
+ * the same time.
+ */
+ if (adev->devices & AUDIO_DEVICE_OUT_ALL_SCO) {
+ device = PCM_DEVICE_SCO;
+ out->pcm_config = &pcm_config_sco;
+ } else {
+ device = PCM_DEVICE;
+ out->pcm_config = &pcm_config_out;
+ out->buffer_type = OUT_BUFFER_TYPE_UNKNOWN;
+ }
+
+ /*
+ * All open PCMs can only use a single group of rates at once:
+ * Group 1: 11.025, 22.05, 44.1
+ * Group 2: 8, 16, 32, 48
+ * Group 1 is used for digital audio playback since 44.1 is
+ * the most common rate, but group 2 is required for SCO.
+ */
+ if (adev->active_in) {
+ pthread_mutex_lock(&adev->active_in->lock);
+ if (((out->pcm_config->rate % 8000 == 0) &&
+ (adev->active_in->pcm_config->rate % 8000) != 0) ||
+ ((out->pcm_config->rate % 11025 == 0) &&
+ (adev->active_in->pcm_config->rate % 11025) != 0))
+ do_in_standby(adev->active_in);
+ pthread_mutex_unlock(&adev->active_in->lock);
+ }
+
+ out->pcm = pcm_open(PCM_CARD, device, PCM_OUT | PCM_NORESTART, out->pcm_config);
+
+ if (out->pcm && !pcm_is_ready(out->pcm)) {
+ ALOGE("pcm_open(out) failed: %s", pcm_get_error(out->pcm));
+ pcm_close(out->pcm);
+ return -ENOMEM;
+ }
+
+ /*
+ * If the stream rate differs from the PCM rate, we need to
+ * create a resampler.
+ */
+ if (out_get_sample_rate(&out->stream.common) != out->pcm_config->rate) {
+ ret = create_resampler(out_get_sample_rate(&out->stream.common),
+ out->pcm_config->rate,
+ out->pcm_config->channels,
+ RESAMPLER_QUALITY_DEFAULT,
+ NULL,
+ &out->resampler);
+ out->buffer_frames = (pcm_config_out.period_size * out->pcm_config->rate) /
+ out_get_sample_rate(&out->stream.common) + 1;
+
+ out->buffer = malloc(pcm_frames_to_bytes(out->pcm, out->buffer_frames));
+ }
+
+ adev->active_out = out;
+
+ return 0;
+}
+
+/* must be called with hw device and input stream mutexes locked */
+static int start_input_stream(struct stream_in *in)
+{
+ struct audio_device *adev = in->dev;
+ unsigned int device;
+ int ret;
+
+ /*
+ * Due to the lack of sample rate converters in the SoC,
+ * it greatly simplifies things to have only the main
+ * mic PCM or the BC SCO PCM open at the same time.
+ */
+ if (adev->devices & AUDIO_DEVICE_IN_ALL_SCO) {
+ device = PCM_DEVICE_SCO;
+ in->pcm_config = &pcm_config_sco;
+ } else {
+ device = PCM_DEVICE;
+ in->pcm_config = &pcm_config_in;
+ }
+
+ /*
+ * All open PCMs can only use a single group of rates at once:
+ * Group 1: 11.025, 22.05, 44.1
+ * Group 2: 8, 16, 32, 48
+ * Group 1 is used for digital audio playback since 44.1 is
+ * the most common rate, but group 2 is required for SCO.
+ */
+ if (adev->active_out) {
+ pthread_mutex_lock(&adev->active_out->lock);
+ if (((in->pcm_config->rate % 8000 == 0) &&
+ (adev->active_out->pcm_config->rate % 8000) != 0) ||
+ ((in->pcm_config->rate % 11025 == 0) &&
+ (adev->active_out->pcm_config->rate % 11025) != 0))
+ do_out_standby(adev->active_out);
+ pthread_mutex_unlock(&adev->active_out->lock);
+ }
+
+ in->pcm = pcm_open(PCM_CARD, device, PCM_IN, in->pcm_config);
+
+ if (in->pcm && !pcm_is_ready(in->pcm)) {
+ ALOGE("pcm_open(in) failed: %s", pcm_get_error(in->pcm));
+ pcm_close(in->pcm);
+ return -ENOMEM;
+ }
+
+ /*
+ * If the stream rate differs from the PCM rate, we need to
+ * create a resampler.
+ */
+ if (in_get_sample_rate(&in->stream.common) != in->pcm_config->rate) {
+ in->buf_provider.get_next_buffer = get_next_buffer;
+ in->buf_provider.release_buffer = release_buffer;
+
+ ret = create_resampler(in->pcm_config->rate,
+ in_get_sample_rate(&in->stream.common),
+ 1,
+ RESAMPLER_QUALITY_DEFAULT,
+ &in->buf_provider,
+ &in->resampler);
+ }
+ in->buffer_size = pcm_frames_to_bytes(in->pcm,
+ in->pcm_config->period_size);
+ in->buffer = malloc(in->buffer_size);
+
+ adev->active_in = in;
+
+ return 0;
+}
+
+static int get_next_buffer(struct resampler_buffer_provider *buffer_provider,
+ struct resampler_buffer* buffer)
+{
+ struct stream_in *in;
+
+ if (buffer_provider == NULL || buffer == NULL)
+ return -EINVAL;
+
+ in = (struct stream_in *)((char *)buffer_provider -
+ offsetof(struct stream_in, buf_provider));
+
+ if (in->pcm == NULL) {
+ buffer->raw = NULL;
+ buffer->frame_count = 0;
+ in->read_status = -ENODEV;
+ return -ENODEV;
+ }
+
+ if (in->frames_in == 0) {
+ in->read_status = pcm_read(in->pcm,
+ (void*)in->buffer,
+ in->buffer_size);
+ if (in->read_status != 0) {
+ ALOGE("get_next_buffer() pcm_read error %d", in->read_status);
+ buffer->raw = NULL;
+ buffer->frame_count = 0;
+ return in->read_status;
+ }
+ in->frames_in = in->pcm_config->period_size;
+ if (in->pcm_config->channels == 2) {
+ unsigned int i;
+
+ /* Discard right channel */
+ for (i = 1; i < in->frames_in; i++)
+ in->buffer[i] = in->buffer[i * 2];
+ }
+ }
+
+ buffer->frame_count = (buffer->frame_count > in->frames_in) ?
+ in->frames_in : buffer->frame_count;
+ buffer->i16 = in->buffer + (in->pcm_config->period_size - in->frames_in);
+
+ return in->read_status;
+
+}
+
+static void release_buffer(struct resampler_buffer_provider *buffer_provider,
+ struct resampler_buffer* buffer)
+{
+ struct stream_in *in;
+
+ if (buffer_provider == NULL || buffer == NULL)
+ return;
+
+ in = (struct stream_in *)((char *)buffer_provider -
+ offsetof(struct stream_in, buf_provider));
+
+ in->frames_in -= buffer->frame_count;
+}
+
+/* read_frames() reads frames from kernel driver, down samples to capture rate
+ * if necessary and output the number of frames requested to the buffer specified */
+static ssize_t read_frames(struct stream_in *in, void *buffer, ssize_t frames)
+{
+ ssize_t frames_wr = 0;
+
+ while (frames_wr < frames) {
+ size_t frames_rd = frames - frames_wr;
+ if (in->resampler != NULL) {
+ in->resampler->resample_from_provider(in->resampler,
+ (int16_t *)((char *)buffer +
+ frames_wr * audio_stream_frame_size(&in->stream.common)),
+ &frames_rd);
+ } else {
+ struct resampler_buffer buf = {
+ { raw : NULL, },
+ frame_count : frames_rd,
+ };
+ get_next_buffer(&in->buf_provider, &buf);
+ if (buf.raw != NULL) {
+ memcpy((char *)buffer +
+ frames_wr * audio_stream_frame_size(&in->stream.common),
+ buf.raw,
+ buf.frame_count * audio_stream_frame_size(&in->stream.common));
+ frames_rd = buf.frame_count;
+ }
+ release_buffer(&in->buf_provider, &buf);
+ }
+ /* in->read_status is updated by getNextBuffer() also called by
+ * in->resampler->resample_from_provider() */
+ if (in->read_status != 0)
+ return in->read_status;
+
+ frames_wr += frames_rd;
+ }
+ return frames_wr;
+}
+
+/* API functions */
+
+static uint32_t out_get_sample_rate(const struct audio_stream *stream)
+{
+ return pcm_config_out.rate;
+}
+
+static int out_set_sample_rate(struct audio_stream *stream, uint32_t rate)
+{
+ return -ENOSYS;
+}
+
+static size_t out_get_buffer_size(const struct audio_stream *stream)
+{
+ return pcm_config_out.period_size *
+ audio_stream_frame_size((struct audio_stream *)stream);
+}
+
+static uint32_t out_get_channels(const struct audio_stream *stream)
+{
+ return AUDIO_CHANNEL_OUT_STEREO;
+}
+
+static audio_format_t out_get_format(const struct audio_stream *stream)
+{
+ return AUDIO_FORMAT_PCM_16_BIT;
+}
+
+static int out_set_format(struct audio_stream *stream, audio_format_t format)
+{
+ return -ENOSYS;
+}
+
+static int out_standby(struct audio_stream *stream)
+{
+ struct stream_out *out = (struct stream_out *)stream;
+
+ pthread_mutex_lock(&out->dev->lock);
+ pthread_mutex_lock(&out->lock);
+ do_out_standby(out);
+ pthread_mutex_unlock(&out->lock);
+ pthread_mutex_unlock(&out->dev->lock);
+
+ return 0;
+}
+
+static int out_dump(const struct audio_stream *stream, int fd)
+{
+ return 0;
+}
+
+static int out_set_parameters(struct audio_stream *stream, const char *kvpairs)
+{
+ struct stream_out *out = (struct stream_out *)stream;
+ struct audio_device *adev = out->dev;
+ struct str_parms *parms;
+ char value[32];
+ int ret;
+ unsigned int val;
+
+ parms = str_parms_create_str(kvpairs);
+
+ ret = str_parms_get_str(parms, AUDIO_PARAMETER_STREAM_ROUTING,
+ value, sizeof(value));
+ pthread_mutex_lock(&adev->lock);
+ if (ret >= 0) {
+ val = atoi(value);
+ if (((adev->devices & AUDIO_DEVICE_OUT_ALL) != val) && (val != 0)) {
+ /*
+ * If SCO is turned on/off, we need to put audio into standby
+ * because SCO uses a different PCM.
+ */
+ if ((val & AUDIO_DEVICE_OUT_ALL_SCO) ^
+ (adev->devices & AUDIO_DEVICE_OUT_ALL_SCO)) {
+ pthread_mutex_lock(&out->lock);
+ do_out_standby(out);
+ pthread_mutex_unlock(&out->lock);
+ }
+
+ adev->devices &= ~AUDIO_DEVICE_OUT_ALL;
+ adev->devices |= val;
+ select_devices(adev);
+ }
+ }
+ pthread_mutex_unlock(&adev->lock);
+
+ str_parms_destroy(parms);
+ return ret;
+}
+
+static char * out_get_parameters(const struct audio_stream *stream, const char *keys)
+{
+ return strdup("");
+}
+
+static uint32_t out_get_latency(const struct audio_stream_out *stream)
+{
+ struct stream_out *out = (struct stream_out *)stream;
+ struct audio_device *adev = out->dev;
+ size_t period_count;
+
+ pthread_mutex_lock(&adev->lock);
+
+ if (adev->screen_off && !adev->active_in && !(adev->devices & AUDIO_DEVICE_OUT_ALL_SCO))
+ period_count = OUT_LONG_PERIOD_COUNT;
+ else
+ period_count = OUT_SHORT_PERIOD_COUNT;
+
+ pthread_mutex_unlock(&adev->lock);
+
+ return (pcm_config_out.period_size * period_count * 1000) / pcm_config_out.rate;
+}
+
+static int out_set_volume(struct audio_stream_out *stream, float left,
+ float right)
+{
+ return -ENOSYS;
+}
+
+static ssize_t out_write(struct audio_stream_out *stream, const void* buffer,
+ size_t bytes)
+{
+ int ret = 0;
+ struct stream_out *out = (struct stream_out *)stream;
+ struct audio_device *adev = out->dev;
+ size_t frame_size = audio_stream_frame_size(&out->stream.common);
+ int16_t *in_buffer = (int16_t *)buffer;
+ size_t in_frames = bytes / frame_size;
+ size_t out_frames;
+ int buffer_type;
+ int kernel_frames;
+ bool sco_on;
+
+ /*
+ * acquiring hw device mutex systematically is useful if a low
+ * priority thread is waiting on the output stream mutex - e.g.
+ * executing out_set_parameters() while holding the hw device
+ * mutex
+ */
+ pthread_mutex_lock(&adev->lock);
+ pthread_mutex_lock(&out->lock);
+ if (out->standby) {
+ ret = start_output_stream(out);
+ if (ret != 0) {
+ pthread_mutex_unlock(&adev->lock);
+ goto exit;
+ }
+ out->standby = false;
+ }
+ buffer_type = (adev->screen_off && !adev->active_in) ?
+ OUT_BUFFER_TYPE_LONG : OUT_BUFFER_TYPE_SHORT;
+ sco_on = (adev->devices & AUDIO_DEVICE_OUT_ALL_SCO);
+ pthread_mutex_unlock(&adev->lock);
+
+ /* detect changes in screen ON/OFF state and adapt buffer size
+ * if needed. Do not change buffer size when routed to SCO device. */
+ if (!sco_on && (buffer_type != out->buffer_type)) {
+ size_t period_count;
+
+ if (buffer_type == OUT_BUFFER_TYPE_LONG)
+ period_count = OUT_LONG_PERIOD_COUNT;
+ else
+ period_count = OUT_SHORT_PERIOD_COUNT;
+
+ out->write_threshold = out->pcm_config->period_size * period_count;
+ /* reset current threshold if exiting standby */
+ if (out->buffer_type == OUT_BUFFER_TYPE_UNKNOWN)
+ out->cur_write_threshold = out->write_threshold;
+ out->buffer_type = buffer_type;
+ }
+
+ /* Reduce number of channels, if necessary */
+ if (popcount(out_get_channels(&stream->common)) >
+ (int)out->pcm_config->channels) {
+ unsigned int i;
+
+ /* Discard right channel */
+ for (i = 1; i < in_frames; i++)
+ in_buffer[i] = in_buffer[i * 2];
+
+ /* The frame size is now half */
+ frame_size /= 2;
+ }
+
+ /* Change sample rate, if necessary */
+ if (out_get_sample_rate(&stream->common) != out->pcm_config->rate) {
+ out_frames = out->buffer_frames;
+ out->resampler->resample_from_input(out->resampler,
+ in_buffer, &in_frames,
+ out->buffer, &out_frames);
+ in_buffer = out->buffer;
+ } else {
+ out_frames = in_frames;
+ }
+
+ if (!sco_on) {
+ int total_sleep_time_us = 0;
+ size_t period_size = out->pcm_config->period_size;
+
+ /* do not allow more than out->cur_write_threshold frames in kernel
+ * pcm driver buffer */
+ do {
+ struct timespec time_stamp;
+ if (pcm_get_htimestamp(out->pcm,
+ (unsigned int *)&kernel_frames,
+ &time_stamp) < 0)
+ break;
+ kernel_frames = pcm_get_buffer_size(out->pcm) - kernel_frames;
+
+ if (kernel_frames > out->cur_write_threshold) {
+ int sleep_time_us =
+ (int)(((int64_t)(kernel_frames - out->cur_write_threshold)
+ * 1000000) / out->pcm_config->rate);
+ if (sleep_time_us < MIN_WRITE_SLEEP_US)
+ break;
+ total_sleep_time_us += sleep_time_us;
+ if (total_sleep_time_us > MAX_WRITE_SLEEP_US) {
+ ALOGW("out_write() limiting sleep time %d to %d",
+ total_sleep_time_us, MAX_WRITE_SLEEP_US);
+ sleep_time_us = MAX_WRITE_SLEEP_US -
+ (total_sleep_time_us - sleep_time_us);
+ }
+ usleep(sleep_time_us);
+ }
+
+ } while ((kernel_frames > out->cur_write_threshold) &&
+ (total_sleep_time_us <= MAX_WRITE_SLEEP_US));
+
+ /* do not allow abrupt changes on buffer size. Increasing/decreasing
+ * the threshold by steps of 1/4th of the buffer size keeps the write
+ * time within a reasonable range during transitions.
+ * Also reset current threshold just above current filling status when
+ * kernel buffer is really depleted to allow for smooth catching up with
+ * target threshold.
+ */
+ if (out->cur_write_threshold > out->write_threshold) {
+ out->cur_write_threshold -= period_size / 4;
+ if (out->cur_write_threshold < out->write_threshold) {
+ out->cur_write_threshold = out->write_threshold;
+ }
+ } else if (out->cur_write_threshold < out->write_threshold) {
+ out->cur_write_threshold += period_size / 4;
+ if (out->cur_write_threshold > out->write_threshold) {
+ out->cur_write_threshold = out->write_threshold;
+ }
+ } else if ((kernel_frames < out->write_threshold) &&
+ ((out->write_threshold - kernel_frames) >
+ (int)(period_size * OUT_SHORT_PERIOD_COUNT))) {
+ out->cur_write_threshold = (kernel_frames / period_size + 1) * period_size;
+ out->cur_write_threshold += period_size / 4;
+ }
+ }
+
+ ret = pcm_write(out->pcm, in_buffer, out_frames * frame_size);
+ if (ret == -EPIPE) {
+ /* In case of underrun, don't sleep since we want to catch up asap */
+ pthread_mutex_unlock(&out->lock);
+ return ret;
+ }
+
+exit:
+ pthread_mutex_unlock(&out->lock);
+
+ if (ret != 0) {
+ usleep(bytes * 1000000 / audio_stream_frame_size(&stream->common) /
+ out_get_sample_rate(&stream->common));
+ }
+
+ return bytes;
+}
+
+static int out_get_render_position(const struct audio_stream_out *stream,
+ uint32_t *dsp_frames)
+{
+ return -EINVAL;
+}
+
+static int out_add_audio_effect(const struct audio_stream *stream, effect_handle_t effect)
+{
+ return 0;
+}
+
+static int out_remove_audio_effect(const struct audio_stream *stream, effect_handle_t effect)
+{
+ return 0;
+}
+
+static int out_get_next_write_timestamp(const struct audio_stream_out *stream,
+ int64_t *timestamp)
+{
+ return -EINVAL;
+}
+
+/** audio_stream_in implementation **/
+static uint32_t in_get_sample_rate(const struct audio_stream *stream)
+{
+ struct stream_in *in = (struct stream_in *)stream;
+
+ return in->requested_rate;
+}
+
+static int in_set_sample_rate(struct audio_stream *stream, uint32_t rate)
+{
+ return 0;
+}
+
+static size_t in_get_buffer_size(const struct audio_stream *stream)
+{
+ struct stream_in *in = (struct stream_in *)stream;
+ size_t size;
+
+ /*
+ * take resampling into account and return the closest majoring
+ * multiple of 16 frames, as audioflinger expects audio buffers to
+ * be a multiple of 16 frames
+ */
+ size = (in->pcm_config->period_size * in_get_sample_rate(stream)) /
+ in->pcm_config->rate;
+ size = ((size + 15) / 16) * 16;
+
+ return size * audio_stream_frame_size((struct audio_stream *)stream);
+}
+
+static uint32_t in_get_channels(const struct audio_stream *stream)
+{
+ return AUDIO_CHANNEL_IN_MONO;
+}
+
+static audio_format_t in_get_format(const struct audio_stream *stream)
+{
+ return AUDIO_FORMAT_PCM_16_BIT;
+}
+
+static int in_set_format(struct audio_stream *stream, audio_format_t format)
+{
+ return -ENOSYS;
+}
+
+static int in_standby(struct audio_stream *stream)
+{
+ struct stream_in *in = (struct stream_in *)stream;
+
+ pthread_mutex_lock(&in->dev->lock);
+ pthread_mutex_lock(&in->lock);
+ do_in_standby(in);
+ pthread_mutex_unlock(&in->lock);
+ pthread_mutex_unlock(&in->dev->lock);
+
+ return 0;
+}
+
+static int in_dump(const struct audio_stream *stream, int fd)
+{
+ return 0;
+}
+
+static int in_set_parameters(struct audio_stream *stream, const char *kvpairs)
+{
+ struct stream_in *in = (struct stream_in *)stream;
+ struct audio_device *adev = in->dev;
+ struct str_parms *parms;
+ char value[32];
+ int ret;
+ unsigned int val;
+
+ parms = str_parms_create_str(kvpairs);
+
+ ret = str_parms_get_str(parms, AUDIO_PARAMETER_STREAM_ROUTING,
+ value, sizeof(value));
+ pthread_mutex_lock(&adev->lock);
+ if (ret >= 0) {
+ val = atoi(value);
+ if (((adev->devices & AUDIO_DEVICE_IN_ALL) != val) && (val != 0)) {
+ /*
+ * If SCO is turned on/off, we need to put audio into standby
+ * because SCO uses a different PCM.
+ */
+ if ((val & AUDIO_DEVICE_IN_ALL_SCO) ^
+ (adev->devices & AUDIO_DEVICE_IN_ALL_SCO)) {
+ pthread_mutex_lock(&in->lock);
+ do_in_standby(in);
+ pthread_mutex_unlock(&in->lock);
+ }
+
+ adev->devices &= ~AUDIO_DEVICE_IN_ALL;
+ adev->devices |= val;
+ select_devices(adev);
+ }
+ }
+ pthread_mutex_unlock(&adev->lock);
+
+ str_parms_destroy(parms);
+ return ret;
+}
+
+static char * in_get_parameters(const struct audio_stream *stream,
+ const char *keys)
+{
+ return strdup("");
+}
+
+static int in_set_gain(struct audio_stream_in *stream, float gain)
+{
+ return 0;
+}
+
+static ssize_t in_read(struct audio_stream_in *stream, void* buffer,
+ size_t bytes)
+{
+ int ret = 0;
+ struct stream_in *in = (struct stream_in *)stream;
+ struct audio_device *adev = in->dev;
+ size_t frames_rq = bytes / audio_stream_frame_size(&stream->common);
+
+ /*
+ * acquiring hw device mutex systematically is useful if a low
+ * priority thread is waiting on the input stream mutex - e.g.
+ * executing in_set_parameters() while holding the hw device
+ * mutex
+ */
+ pthread_mutex_lock(&adev->lock);
+ pthread_mutex_lock(&in->lock);
+ if (in->standby) {
+ ret = start_input_stream(in);
+ if (ret == 0)
+ in->standby = 0;
+ }
+ pthread_mutex_unlock(&adev->lock);
+
+ if (ret < 0)
+ goto exit;
+
+ /*if (in->num_preprocessors != 0) {
+ ret = process_frames(in, buffer, frames_rq);
+ } else */if (in->resampler != NULL) {
+ ret = read_frames(in, buffer, frames_rq);
+ } else if (in->pcm_config->channels == 2) {
+ /*
+ * If the PCM is stereo, capture twice as many frames and
+ * discard the right channel.
+ */
+ unsigned int i;
+ int16_t *in_buffer = (int16_t *)buffer;
+
+ ret = pcm_read(in->pcm, in->buffer, bytes * 2);
+
+ /* Discard right channel */
+ for (i = 0; i < frames_rq; i++)
+ in_buffer[i] = in->buffer[i * 2];
+ } else {
+ ret = pcm_read(in->pcm, buffer, bytes);
+ }
+
+ if (ret > 0)
+ ret = 0;
+
+ /*
+ * Instead of writing zeroes here, we could trust the hardware
+ * to always provide zeroes when muted.
+ */
+ if (ret == 0 && adev->mic_mute)
+ memset(buffer, 0, bytes);
+
+exit:
+ if (ret < 0)
+ usleep(bytes * 1000000 / audio_stream_frame_size(&stream->common) /
+ in_get_sample_rate(&stream->common));
+
+ pthread_mutex_unlock(&in->lock);
+ return bytes;
+}
+
+static uint32_t in_get_input_frames_lost(struct audio_stream_in *stream)
+{
+ return 0;
+}
+
+static int in_add_audio_effect(const struct audio_stream *stream,
+ effect_handle_t effect)
+{
+ return 0;
+}
+
+static int in_remove_audio_effect(const struct audio_stream *stream,
+ effect_handle_t effect)
+{
+ return 0;
+}
+
+
+static int adev_open_output_stream(struct audio_hw_device *dev,
+ audio_io_handle_t handle,
+ audio_devices_t devices,
+ audio_output_flags_t flags,
+ struct audio_config *config,
+ struct audio_stream_out **stream_out)
+{
+ struct audio_device *adev = (struct audio_device *)dev;
+ struct stream_out *out;
+ int ret;
+
+ out = (struct stream_out *)calloc(1, sizeof(struct stream_out));
+ if (!out)
+ return -ENOMEM;
+
+ out->stream.common.get_sample_rate = out_get_sample_rate;
+ out->stream.common.set_sample_rate = out_set_sample_rate;
+ out->stream.common.get_buffer_size = out_get_buffer_size;
+ out->stream.common.get_channels = out_get_channels;
+ out->stream.common.get_format = out_get_format;
+ out->stream.common.set_format = out_set_format;
+ out->stream.common.standby = out_standby;
+ out->stream.common.dump = out_dump;
+ out->stream.common.set_parameters = out_set_parameters;
+ out->stream.common.get_parameters = out_get_parameters;
+ out->stream.common.add_audio_effect = out_add_audio_effect;
+ out->stream.common.remove_audio_effect = out_remove_audio_effect;
+ out->stream.get_latency = out_get_latency;
+ out->stream.set_volume = out_set_volume;
+ out->stream.write = out_write;
+ out->stream.get_render_position = out_get_render_position;
+ out->stream.get_next_write_timestamp = out_get_next_write_timestamp;
+
+ out->dev = adev;
+
+ config->format = out_get_format(&out->stream.common);
+ config->channel_mask = out_get_channels(&out->stream.common);
+ config->sample_rate = out_get_sample_rate(&out->stream.common);
+
+ out->standby = true;
+
+ *stream_out = &out->stream;
+ return 0;
+
+err_open:
+ free(out);
+ *stream_out = NULL;
+ return ret;
+}
+
+static void adev_close_output_stream(struct audio_hw_device *dev,
+ struct audio_stream_out *stream)
+{
+ out_standby(&stream->common);
+ free(stream);
+}
+
+static int adev_set_parameters(struct audio_hw_device *dev, const char *kvpairs)
+{
+ struct audio_device *adev = (struct audio_device *)dev;
+ struct str_parms *parms;
+ char *str;
+ char value[32];
+ int ret;
+
+ parms = str_parms_create_str(kvpairs);
+ ret = str_parms_get_str(parms, "orientation", value, sizeof(value));
+ if (ret >= 0) {
+ int orientation;
+
+ if (strcmp(value, "landscape") == 0)
+ orientation = ORIENTATION_LANDSCAPE;
+ else if (strcmp(value, "portrait") == 0)
+ orientation = ORIENTATION_PORTRAIT;
+ else if (strcmp(value, "square") == 0)
+ orientation = ORIENTATION_SQUARE;
+ else
+ orientation = ORIENTATION_UNDEFINED;
+
+ pthread_mutex_lock(&adev->lock);
+ if (orientation != adev->orientation) {
+ adev->orientation = orientation;
+ /*
+ * Orientation changes can occur with the input device
+ * closed so we must call select_devices() here to set
+ * up the mixer. This is because select_devices() will
+ * not be called when the input device is opened if no
+ * other input parameter is changed.
+ */
+ select_devices(adev);
+ }
+ pthread_mutex_unlock(&adev->lock);
+ }
+
+ ret = str_parms_get_str(parms, "screen_state", value, sizeof(value));
+ if (ret >= 0) {
+ if (strcmp(value, AUDIO_PARAMETER_VALUE_ON) == 0)
+ adev->screen_off = false;
+ else
+ adev->screen_off = true;
+ }
+
+ str_parms_destroy(parms);
+ return ret;
+}
+
+static char * adev_get_parameters(const struct audio_hw_device *dev,
+ const char *keys)
+{
+ return strdup("");
+}
+
+static int adev_init_check(const struct audio_hw_device *dev)
+{
+ return 0;
+}
+
+static int adev_set_voice_volume(struct audio_hw_device *dev, float volume)
+{
+ return -ENOSYS;
+}
+
+static int adev_set_master_volume(struct audio_hw_device *dev, float volume)
+{
+ return -ENOSYS;
+}
+
+static int adev_set_mode(struct audio_hw_device *dev, audio_mode_t mode)
+{
+ return 0;
+}
+
+static int adev_set_mic_mute(struct audio_hw_device *dev, bool state)
+{
+ struct audio_device *adev = (struct audio_device *)dev;
+
+ adev->mic_mute = state;
+
+ return 0;
+}
+
+static int adev_get_mic_mute(const struct audio_hw_device *dev, bool *state)
+{
+ struct audio_device *adev = (struct audio_device *)dev;
+
+ *state = adev->mic_mute;
+
+ return 0;
+}
+
+static size_t adev_get_input_buffer_size(const struct audio_hw_device *dev,
+ const struct audio_config *config)
+{
+ size_t size;
+
+ /*
+ * take resampling into account and return the closest majoring
+ * multiple of 16 frames, as audioflinger expects audio buffers to
+ * be a multiple of 16 frames
+ */
+ size = (pcm_config_in.period_size * config->sample_rate) / pcm_config_in.rate;
+ size = ((size + 15) / 16) * 16;
+
+ return (size * popcount(config->channel_mask) *
+ audio_bytes_per_sample(config->format));
+}
+
+static int adev_open_input_stream(struct audio_hw_device *dev,
+ audio_io_handle_t handle,
+ audio_devices_t devices,
+ struct audio_config *config,
+ struct audio_stream_in **stream_in)
+{
+ struct audio_device *adev = (struct audio_device *)dev;
+ struct stream_in *in;
+ int ret;
+
+ *stream_in = NULL;
+
+ /* Respond with a request for mono if a different format is given. */
+ if (config->channel_mask != AUDIO_CHANNEL_IN_MONO) {
+ config->channel_mask = AUDIO_CHANNEL_IN_MONO;
+ return -EINVAL;
+ }
+
+ in = (struct stream_in *)calloc(1, sizeof(struct stream_in));
+ if (!in)
+ return -ENOMEM;
+
+ in->stream.common.get_sample_rate = in_get_sample_rate;
+ in->stream.common.set_sample_rate = in_set_sample_rate;
+ in->stream.common.get_buffer_size = in_get_buffer_size;
+ in->stream.common.get_channels = in_get_channels;
+ in->stream.common.get_format = in_get_format;
+ in->stream.common.set_format = in_set_format;
+ in->stream.common.standby = in_standby;
+ in->stream.common.dump = in_dump;
+ in->stream.common.set_parameters = in_set_parameters;
+ in->stream.common.get_parameters = in_get_parameters;
+ in->stream.common.add_audio_effect = in_add_audio_effect;
+ in->stream.common.remove_audio_effect = in_remove_audio_effect;
+ in->stream.set_gain = in_set_gain;
+ in->stream.read = in_read;
+ in->stream.get_input_frames_lost = in_get_input_frames_lost;
+
+ in->dev = adev;
+ in->standby = true;
+ in->requested_rate = config->sample_rate;
+ in->pcm_config = &pcm_config_in; /* default PCM config */
+
+ *stream_in = &in->stream;
+ return 0;
+}
+
+static void adev_close_input_stream(struct audio_hw_device *dev,
+ struct audio_stream_in *stream)
+{
+ struct stream_in *in = (struct stream_in *)stream;
+
+ in_standby(&stream->common);
+ free(stream);
+}
+
+static int adev_dump(const audio_hw_device_t *device, int fd)
+{
+ return 0;
+}
+
+static int adev_close(hw_device_t *device)
+{
+ struct audio_device *adev = (struct audio_device *)device;
+
+ audio_route_free(adev->ar);
+
+ free(device);
+ return 0;
+}
+
+static uint32_t adev_get_supported_devices(const struct audio_hw_device *dev)
+{
+ return (/* OUT */
+ AUDIO_DEVICE_OUT_SPEAKER |
+ AUDIO_DEVICE_OUT_WIRED_HEADSET |
+ AUDIO_DEVICE_OUT_WIRED_HEADPHONE |
+ AUDIO_DEVICE_OUT_ALL_SCO |
+ AUDIO_DEVICE_OUT_DEFAULT |
+ /* IN */
+ AUDIO_DEVICE_IN_BUILTIN_MIC |
+ AUDIO_DEVICE_IN_WIRED_HEADSET |
+ AUDIO_DEVICE_IN_BACK_MIC |
+ AUDIO_DEVICE_IN_ALL_SCO |
+ AUDIO_DEVICE_IN_DEFAULT);
+}
+
+static int adev_open(const hw_module_t* module, const char* name,
+ hw_device_t** device)
+{
+ struct audio_device *adev;
+ int ret;
+
+ if (strcmp(name, AUDIO_HARDWARE_INTERFACE) != 0)
+ return -EINVAL;
+
+ adev = calloc(1, sizeof(struct audio_device));
+ if (!adev)
+ return -ENOMEM;
+
+ adev->hw_device.common.tag = HARDWARE_DEVICE_TAG;
+ adev->hw_device.common.version = AUDIO_DEVICE_API_VERSION_1_0;
+ adev->hw_device.common.module = (struct hw_module_t *) module;
+ adev->hw_device.common.close = adev_close;
+
+ adev->hw_device.get_supported_devices = adev_get_supported_devices;
+ adev->hw_device.init_check = adev_init_check;
+ adev->hw_device.set_voice_volume = adev_set_voice_volume;
+ adev->hw_device.set_master_volume = adev_set_master_volume;
+ adev->hw_device.set_mode = adev_set_mode;
+ adev->hw_device.set_mic_mute = adev_set_mic_mute;
+ adev->hw_device.get_mic_mute = adev_get_mic_mute;
+ adev->hw_device.set_parameters = adev_set_parameters;
+ adev->hw_device.get_parameters = adev_get_parameters;
+ adev->hw_device.get_input_buffer_size = adev_get_input_buffer_size;
+ adev->hw_device.open_output_stream = adev_open_output_stream;
+ adev->hw_device.close_output_stream = adev_close_output_stream;
+ adev->hw_device.open_input_stream = adev_open_input_stream;
+ adev->hw_device.close_input_stream = adev_close_input_stream;
+ adev->hw_device.dump = adev_dump;
+
+ adev->ar = audio_route_init();
+ adev->orientation = ORIENTATION_UNDEFINED;
+
+ *device = &adev->hw_device.common;
+
+ return 0;
+}
+
+static struct hw_module_methods_t hal_module_methods = {
+ .open = adev_open,
+};
+
+struct audio_module HAL_MODULE_INFO_SYM = {
+ .common = {
+ .tag = HARDWARE_MODULE_TAG,
+ .module_api_version = AUDIO_MODULE_API_VERSION_0_1,
+ .hal_api_version = HARDWARE_HAL_API_VERSION,
+ .id = AUDIO_HARDWARE_MODULE_ID,
+ .name = "TF201 audio HW HAL",
+ .author = "The Android Open Source Project",
+ .methods = &hal_module_methods,
+ },
+};
diff --git a/audio/audio_route.c b/audio/audio_route.c
new file mode 100644
index 0000000..7a13de4
--- /dev/null
+++ b/audio/audio_route.c
@@ -0,0 +1,494 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ * Inspired by TinyHW, written by Mark Brown at Wolfson Micro
+ *
+ * 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 "audio_hw_primary"
+/*#define LOG_NDEBUG 0*/
+
+#include <errno.h>
+#include <expat.h>
+#include <stdbool.h>
+#include <stdio.h>
+
+#include <cutils/log.h>
+
+#include <tinyalsa/asoundlib.h>
+
+#define BUF_SIZE 1024
+#define MIXER_XML_PATH "/system/etc/mixer_paths.xml"
+#define INITIAL_MIXER_PATH_SIZE 8
+
+#define MIXER_CARD 1
+
+struct mixer_state {
+ struct mixer_ctl *ctl;
+ int old_value;
+ int new_value;
+ int reset_value;
+};
+
+struct mixer_setting {
+ struct mixer_ctl *ctl;
+ int value;
+};
+
+struct mixer_path {
+ char *name;
+ unsigned int size;
+ unsigned int length;
+ struct mixer_setting *setting;
+};
+
+struct audio_route {
+ struct mixer *mixer;
+ unsigned int num_mixer_ctls;
+ struct mixer_state *mixer_state;
+
+ unsigned int mixer_path_size;
+ unsigned int num_mixer_paths;
+ struct mixer_path *mixer_path;
+};
+
+struct config_parse_state {
+ struct audio_route *ar;
+ struct mixer_path *path;
+ int level;
+};
+
+/* path functions */
+
+static void path_free(struct audio_route *ar)
+{
+ unsigned int i;
+
+ for (i = 0; i < ar->num_mixer_paths; i++) {
+ if (ar->mixer_path[i].name)
+ free(ar->mixer_path[i].name);
+ if (ar->mixer_path[i].setting)
+ free(ar->mixer_path[i].setting);
+ }
+ free(ar->mixer_path);
+}
+
+static struct mixer_path *path_get_by_name(struct audio_route *ar,
+ const char *name)
+{
+ unsigned int i;
+
+ for (i = 0; i < ar->num_mixer_paths; i++)
+ if (strcmp(ar->mixer_path[i].name, name) == 0)
+ return &ar->mixer_path[i];
+
+ return NULL;
+}
+
+static struct mixer_path *path_create(struct audio_route *ar, const char *name)
+{
+ struct mixer_path *new_mixer_path = NULL;
+
+ if (path_get_by_name(ar, name)) {
+ ALOGE("Path name '%s' already exists", name);
+ return NULL;
+ }
+
+ /* check if we need to allocate more space for mixer paths */
+ if (ar->mixer_path_size <= ar->num_mixer_paths) {
+ if (ar->mixer_path_size == 0)
+ ar->mixer_path_size = INITIAL_MIXER_PATH_SIZE;
+ else
+ ar->mixer_path_size *= 2;
+
+ new_mixer_path = realloc(ar->mixer_path, ar->mixer_path_size *
+ sizeof(struct mixer_path));
+ if (new_mixer_path == NULL) {
+ ALOGE("Unable to allocate more paths");
+ return NULL;
+ } else {
+ ar->mixer_path = new_mixer_path;
+ }
+ }
+
+ /* initialise the new mixer path */
+ ar->mixer_path[ar->num_mixer_paths].name = strdup(name);
+ ar->mixer_path[ar->num_mixer_paths].size = 0;
+ ar->mixer_path[ar->num_mixer_paths].length = 0;
+ ar->mixer_path[ar->num_mixer_paths].setting = NULL;
+
+ /* return the mixer path just added, then increment number of them */
+ return &ar->mixer_path[ar->num_mixer_paths++];
+}
+
+static bool path_setting_exists(struct mixer_path *path,
+ struct mixer_setting *setting)
+{
+ unsigned int i;
+
+ for (i = 0; i < path->length; i++)
+ if (path->setting[i].ctl == setting->ctl)
+ return true;
+
+ return false;
+}
+
+static int path_add_setting(struct mixer_path *path,
+ struct mixer_setting *setting)
+{
+ struct mixer_setting *new_path_setting;
+
+ if (path_setting_exists(path, setting)) {
+ ALOGE("Duplicate path setting '%s'",
+ mixer_ctl_get_name(setting->ctl));
+ return -1;
+ }
+
+ /* check if we need to allocate more space for path settings */
+ if (path->size <= path->length) {
+ if (path->size == 0)
+ path->size = INITIAL_MIXER_PATH_SIZE;
+ else
+ path->size *= 2;
+
+ new_path_setting = realloc(path->setting,
+ path->size * sizeof(struct mixer_setting));
+ if (new_path_setting == NULL) {
+ ALOGE("Unable to allocate more path settings");
+ return -1;
+ } else {
+ path->setting = new_path_setting;
+ }
+ }
+
+ /* initialise the new path setting */
+ path->setting[path->length].ctl = setting->ctl;
+ path->setting[path->length].value = setting->value;
+ path->length++;
+
+ return 0;
+}
+
+static int path_add_path(struct mixer_path *path, struct mixer_path *sub_path)
+{
+ unsigned int i;
+
+ for (i = 0; i < sub_path->length; i++)
+ if (path_add_setting(path, &sub_path->setting[i]) < 0)
+ return -1;
+
+ return 0;
+}
+
+static void path_print(struct mixer_path *path)
+{
+ unsigned int i;
+
+ ALOGV("Path: %s, length: %d", path->name, path->length);
+ for (i = 0; i < path->length; i++)
+ ALOGV(" %d: %s -> %d", i, mixer_ctl_get_name(path->setting[i].ctl),
+ path->setting[i].value);
+}
+
+static int path_apply(struct audio_route *ar, struct mixer_path *path)
+{
+ unsigned int i;
+ unsigned int j;
+
+ for (i = 0; i < path->length; i++) {
+ struct mixer_ctl *ctl = path->setting[i].ctl;
+
+ /* locate the mixer ctl in the list */
+ for (j = 0; j < ar->num_mixer_ctls; j++) {
+ if (ar->mixer_state[j].ctl == ctl)
+ break;
+ }
+
+ /* apply the new value */
+ ar->mixer_state[j].new_value = path->setting[i].value;
+ }
+
+ return 0;
+}
+
+/* mixer helper function */
+static int mixer_enum_string_to_value(struct mixer_ctl *ctl, const char *string)
+{
+ unsigned int i;
+
+ /* Search the enum strings for a particular one */
+ for (i = 0; i < mixer_ctl_get_num_enums(ctl); i++) {
+ if (strcmp(mixer_ctl_get_enum_string(ctl, i), string) == 0)
+ break;
+ }
+
+ return i;
+}
+
+static void start_tag(void *data, const XML_Char *tag_name,
+ const XML_Char **attr)
+{
+ const XML_Char *attr_name = NULL;
+ const XML_Char *attr_value = NULL;
+ struct config_parse_state *state = data;
+ struct audio_route *ar = state->ar;
+ unsigned int i;
+ struct mixer_ctl *ctl;
+ int value;
+ struct mixer_setting mixer_setting;
+
+ /* Get name, type and value attributes (these may be empty) */
+ for (i = 0; attr[i]; i += 2) {
+ if (strcmp(attr[i], "name") == 0)
+ attr_name = attr[i + 1];
+ else if (strcmp(attr[i], "value") == 0)
+ attr_value = attr[i + 1];
+ }
+
+ /* Look at tags */
+ if (strcmp(tag_name, "path") == 0) {
+ if (attr_name == NULL) {
+ ALOGE("Unnamed path!");
+ } else {
+ if (state->level == 1) {
+ /* top level path: create and stash the path */
+ state->path = path_create(ar, (char *)attr_name);
+ } else {
+ /* nested path */
+ struct mixer_path *sub_path = path_get_by_name(ar, attr_name);
+ path_add_path(state->path, sub_path);
+ }
+ }
+ }
+
+ else if (strcmp(tag_name, "ctl") == 0) {
+ /* Obtain the mixer ctl and value */
+ ctl = mixer_get_ctl_by_name(ar->mixer, attr_name);
+ switch (mixer_ctl_get_type(ctl)) {
+ case MIXER_CTL_TYPE_BOOL:
+ case MIXER_CTL_TYPE_INT:
+ value = atoi((char *)attr_value);
+ break;
+ case MIXER_CTL_TYPE_ENUM:
+ value = mixer_enum_string_to_value(ctl, (char *)attr_value);
+ break;
+ default:
+ value = 0;
+ break;
+ }
+
+ if (state->level == 1) {
+ /* top level ctl (initial setting) */
+
+ /* locate the mixer ctl in the list */
+ for (i = 0; i < ar->num_mixer_ctls; i++) {
+ if (ar->mixer_state[i].ctl == ctl)
+ break;
+ }
+
+ /* apply the new value */
+ ar->mixer_state[i].new_value = value;
+ } else {
+ /* nested ctl (within a path) */
+ mixer_setting.ctl = ctl;
+ mixer_setting.value = value;
+ path_add_setting(state->path, &mixer_setting);
+ }
+ }
+
+ state->level++;
+}
+
+static void end_tag(void *data, const XML_Char *tag_name)
+{
+ struct config_parse_state *state = data;
+
+ state->level--;
+}
+
+static int alloc_mixer_state(struct audio_route *ar)
+{
+ unsigned int i;
+
+ ar->num_mixer_ctls = mixer_get_num_ctls(ar->mixer);
+ ar->mixer_state = malloc(ar->num_mixer_ctls * sizeof(struct mixer_state));
+ if (!ar->mixer_state)
+ return -1;
+
+ for (i = 0; i < ar->num_mixer_ctls; i++) {
+ ar->mixer_state[i].ctl = mixer_get_ctl(ar->mixer, i);
+ /* only get value 0, assume multiple ctl values are the same */
+ ar->mixer_state[i].old_value = mixer_ctl_get_value(ar->mixer_state[i].ctl, 0);
+ ar->mixer_state[i].new_value = ar->mixer_state[i].old_value;
+ }
+
+ return 0;
+}
+
+static void free_mixer_state(struct audio_route *ar)
+{
+ free(ar->mixer_state);
+ ar->mixer_state = NULL;
+}
+
+void update_mixer_state(struct audio_route *ar)
+{
+ unsigned int i;
+ unsigned int j;
+
+ for (i = 0; i < ar->num_mixer_ctls; i++) {
+ /* if the value has changed, update the mixer */
+ if (ar->mixer_state[i].old_value != ar->mixer_state[i].new_value) {
+ /* set all ctl values the same */
+ for (j = 0; j < mixer_ctl_get_num_values(ar->mixer_state[i].ctl); j++)
+ mixer_ctl_set_value(ar->mixer_state[i].ctl, j,
+ ar->mixer_state[i].new_value);
+ ar->mixer_state[i].old_value = ar->mixer_state[i].new_value;
+ }
+ }
+}
+
+/* saves the current state of the mixer, for resetting all controls */
+static void save_mixer_state(struct audio_route *ar)
+{
+ unsigned int i;
+
+ for (i = 0; i < ar->num_mixer_ctls; i++) {
+ /* only get value 0, assume multiple ctl values are the same */
+ ar->mixer_state[i].reset_value = mixer_ctl_get_value(ar->mixer_state[i].ctl, 0);
+ }
+}
+
+/* this resets all mixer settings to the saved values */
+void reset_mixer_state(struct audio_route *ar)
+{
+ unsigned int i;
+
+ /* load all of the saved values */
+ for (i = 0; i < ar->num_mixer_ctls; i++)
+ ar->mixer_state[i].new_value = ar->mixer_state[i].reset_value;
+}
+
+void audio_route_apply_path(struct audio_route *ar, const char *name)
+{
+ struct mixer_path *path;
+
+ if (!ar) {
+ ALOGE("invalid audio_route");
+ return;
+ }
+
+ path = path_get_by_name(ar, name);
+ if (!path) {
+ ALOGE("unable to find path '%s'", name);
+ return;
+ }
+
+ path_apply(ar, path);
+}
+
+struct audio_route *audio_route_init(void)
+{
+ struct config_parse_state state;
+ XML_Parser parser;
+ FILE *file;
+ int bytes_read;
+ void *buf;
+ int i;
+ struct mixer_path *path;
+ struct audio_route *ar;
+
+ ar = calloc(1, sizeof(struct audio_route));
+ if (!ar)
+ goto err_calloc;
+
+ ar->mixer = mixer_open(MIXER_CARD);
+ if (!ar->mixer) {
+ ALOGE("Unable to open the mixer, aborting.");
+ goto err_mixer_open;
+ }
+
+ ar->mixer_path = NULL;
+ ar->mixer_path_size = 0;
+ ar->num_mixer_paths = 0;
+
+ /* allocate space for and read current mixer settings */
+ if (alloc_mixer_state(ar) < 0)
+ goto err_mixer_state;
+
+ file = fopen(MIXER_XML_PATH, "r");
+ if (!file) {
+ ALOGE("Failed to open %s", MIXER_XML_PATH);
+ goto err_fopen;
+ }
+
+ parser = XML_ParserCreate(NULL);
+ if (!parser) {
+ ALOGE("Failed to create XML parser");
+ goto err_parser_create;
+ }
+
+ memset(&state, 0, sizeof(state));
+ state.ar = ar;
+ XML_SetUserData(parser, &state);
+ XML_SetElementHandler(parser, start_tag, end_tag);
+
+ for (;;) {
+ buf = XML_GetBuffer(parser, BUF_SIZE);
+ if (buf == NULL)
+ goto err_parse;
+
+ bytes_read = fread(buf, 1, BUF_SIZE, file);
+ if (bytes_read < 0)
+ goto err_parse;
+
+ if (XML_ParseBuffer(parser, bytes_read,
+ bytes_read == 0) == XML_STATUS_ERROR) {
+ ALOGE("Error in mixer xml (%s)", MIXER_XML_PATH);
+ goto err_parse;
+ }
+
+ if (bytes_read == 0)
+ break;
+ }
+
+ /* apply the initial mixer values, and save them so we can reset the
+ mixer to the original values */
+ update_mixer_state(ar);
+ save_mixer_state(ar);
+
+ XML_ParserFree(parser);
+ fclose(file);
+ return ar;
+
+err_parse:
+ XML_ParserFree(parser);
+err_parser_create:
+ fclose(file);
+err_fopen:
+ free_mixer_state(ar);
+err_mixer_state:
+ mixer_close(ar->mixer);
+err_mixer_open:
+ free(ar);
+ ar = NULL;
+err_calloc:
+ return NULL;
+}
+
+void audio_route_free(struct audio_route *ar)
+{
+ free_mixer_state(ar);
+ mixer_close(ar->mixer);
+ free(ar);
+}
diff --git a/audio/audio_route.h b/audio/audio_route.h
new file mode 100644
index 0000000..31355dd
--- /dev/null
+++ b/audio/audio_route.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * 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.
+ */
+
+#ifndef AUDIO_ROUTE_H
+#define AUDIO_ROUTE_H
+
+/* Initialises and frees the audio routes */
+struct audio_route *audio_route_init(void);
+void audio_route_free(struct audio_route *ar);
+
+/* Applies an audio route path by name */
+void audio_route_apply_path(struct audio_route *ar, const char *name);
+
+/* Resets the mixer back to its initial state */
+void reset_mixer_state(struct audio_route *ar);
+
+/* Updates the mixer with any changed values */
+void update_mixer_state(struct audio_route *ar);
+
+#endif
diff --git a/device_tf700t.mk b/device_tf700t.mk
index 5780a5b..19399a4 100644
--- a/device_tf700t.mk
+++ b/device_tf700t.mk
@@ -35,6 +35,7 @@ PRODUCT_COPY_FILES += \
$(LOCAL_PATH)/prebuilt/cpu.sh:system/bin/cpu.sh \
$(LOCAL_PATH)/prebuilt/vold.fstab:system/etc/vold.fstab \
$(LOCAL_PATH)/prebuilt/gpsconfig.xml:system/etc/gps/gpsconfig.xml
+ $(LOCAL_PATH)/prebuilt/mixer_paths.xml:system/etc/mixer_paths.xml
# Input device configuration files
PRODUCT_COPY_FILES += \
@@ -85,10 +86,14 @@ PRODUCT_PACKAGES += \
com.android.future.usb.accessory \
make_ext4fs \
setup_fs \
+ audio.primary.cardhu \
audio.a2dp.default \
audio.usb.default \
libtinyalsa \
libaudioutils \
+ tinymix \
+ tinyplay \
+ tinyrec \
libinvensense_mpl \
AutoParts_tfp \
blobpack_tfp \
diff --git a/prebuilt/mixer_paths.xml b/prebuilt/mixer_paths.xml
new file mode 100644
index 0000000..548db7f
--- /dev/null
+++ b/prebuilt/mixer_paths.xml
@@ -0,0 +1,38 @@
+<mixer>
+ <!-- These are the initial mixer settings -->
+ <ctl name="DMIC Mute Left" value="1" />
+ <ctl name="DMIC Mute Right " value="1" />
+ <ctl name="Int Spk Switch" value="0" />
+ <ctl name="DMIC Capture Switch" value="0" />
+ <ctl name="Int Mic Switch" value="0" />
+
+ <path name="speaker">
+ <ctl name="DMIC Mute Left" value="0" />
+ <ctl name="DMIC Mute Right" value="0" />
+ <ctl name="Int Spk Switch" value="1" />
+ </path>
+
+ <path name="headphone">
+ <ctl name="DMIC Mute Left" value="0" />
+ <ctl name="DMIC Mute Right" value="0" />
+ <ctl name="Int Spk Switch" value="0" />
+ <ctl name="Headphone Jack Switch" value="1" />
+ </path>
+
+ <path name="main-mic-top">
+ <ctl name="DMIC Capture Switch" value="1" />
+ <ctl name="DMIC Mute Left" value="0" />
+ <ctl name="DMIC Mute Right" value="0" />
+ <ctl name="Int Mic Switch" value="1" />
+ </path>
+
+ <path name="main-mic-left">
+ <ctl name="DMIC Capture Switch" value="1" />
+ <ctl name="DMIC Mute Left" value="0" />
+ <ctl name="DMIC Mute Right" value="0" />
+ <ctl name="Int Mic Switch" value="1" />
+ </path>
+
+ <path name="headset-mic">
+ </path>
+</mixer>
diff --git a/proprietary-files.txt b/proprietary-files.txt
index f5ed64e..a04c4e8 100644
--- a/proprietary-files.txt
+++ b/proprietary-files.txt
@@ -65,8 +65,6 @@ lib/egl/libGLESv1_CM_tegra.so
lib/egl/libGLESv2_perfhud.so
lib/egl/libGLESv2_tegra_impl.so
lib/egl/libGLESv2_tegra.so
-lib/hw/audio_policy.tegra.so
-lib/hw/audio.primary.tegra.so
lib/hw/camera.tegra.so
lib/hw/gps.tegra.so
lib/hw/gralloc.tegra.so