From fb8a507ecb7d8dfd435691c9c6a45bd9bc86daa7 Mon Sep 17 00:00:00 2001 From: Chandrashekhar Urkeri Date: Thu, 10 Oct 2019 01:55:22 +0000 Subject: AudioHAL:Implement primary Audio HAL library for j721e. This audio HAL implements audio HIDL Version 4.0.Android's audio Hardware Abstraction Layer (HAL) connects the higher-level, audio-specific framework APIs in android.media to the underlying audio driver and hardware. Audio policy configuration file format (XML) is used for describing your audio topology.This XML file enables defining the number and types of output and input stream profiles, devices usable for playback and capture, and audio attributes. The audio HAL mainly implementations the API's defined in the below header file. hardware/libhardware/include/hardware/audio.h The base implementation is taken from the J6 Audio HAL library. This audio hal generates the library for primary audio device i.e. audio.primary.j721e.so The current implementation will play and record at 48KHz For more info on Android audio hal please check below link. https://source.android.com/devices/audio Testing: Playback testing is done using the default Android music player with different audio formats. Record testing is done via test recorder app or the default Android Sound recorder app. TODO: 1.Implement the empty functions which handles changing of volume,audio effects,mute functions and debug dumps etc. 2.Multi channel audio playback. 3.Voice call handling. 4.BT voice call handling. 5.Changes required for CDD compliance. Acked-By: Misael Lopez Cruz Signed-off-by: Chandrashekhar Urkeri [corrected the mixer path ctrl names] Signed-off-by: Vishal Mahaveer --- audio/Android.mk | 17 + audio/audio_policy_configuration.xml | 18 + audio/primary/Android.mk | 48 ++ audio/primary/audio_hw.c | 1071 ++++++++++++++++++++++++++ audio/primary/audio_policy_configuration.xml | 34 + audio/primary/mixer_paths.xml | 13 + j721e.mk | 1 + 7 files changed, 1202 insertions(+) create mode 100644 audio/Android.mk create mode 100644 audio/audio_policy_configuration.xml create mode 100644 audio/primary/Android.mk create mode 100644 audio/primary/audio_hw.c create mode 100644 audio/primary/audio_policy_configuration.xml create mode 100644 audio/primary/mixer_paths.xml diff --git a/audio/Android.mk b/audio/Android.mk new file mode 100644 index 0000000..f5e4b26 --- /dev/null +++ b/audio/Android.mk @@ -0,0 +1,17 @@ +# Copyright (C) 2019 Texas Instruments +# +# 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 $(call all-makefiles-under,$(LOCAL_PATH)) diff --git a/audio/audio_policy_configuration.xml b/audio/audio_policy_configuration.xml new file mode 100644 index 0000000..576804e --- /dev/null +++ b/audio/audio_policy_configuration.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/audio/primary/Android.mk b/audio/primary/Android.mk new file mode 100644 index 0000000..551025c --- /dev/null +++ b/audio/primary/Android.mk @@ -0,0 +1,48 @@ +# Copyright (C) 2019 Texas Instruments +# +# 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) + +ifeq ($(findstring j721e, $(TARGET_BOARD_PLATFORM)),j721e) + +include $(CLEAR_VARS) + +LOCAL_MODULE := audio.primary.$(TARGET_BOARD_PLATFORM) + +LOCAL_MODULE_RELATIVE_PATH := hw +LOCAL_VENDOR_MODULE := true +LOCAL_SRC_FILES := audio_hw.c + +LOCAL_C_INCLUDES += \ + external/tinyalsa/include \ + system/media/audio_route/include \ + system/media/audio_utils/include \ + system/media/audio_effects/include + +LOCAL_HEADER_LIBRARIES += libhardware_headers + +LOCAL_CFLAGS += -Wno-implicit-function-declaration -Wno-unused-variable + +LOCAL_SHARED_LIBRARIES := \ + liblog \ + libcutils \ + libtinyalsa \ + libaudioroute \ + libaudioutils + +LOCAL_MODULE_TAGS := optional + +include $(BUILD_SHARED_LIBRARY) + +endif diff --git a/audio/primary/audio_hw.c b/audio/primary/audio_hw.c new file mode 100644 index 0000000..9c40f41 --- /dev/null +++ b/audio/primary/audio_hw.c @@ -0,0 +1,1071 @@ +/* + * Copyright (C) 2019 Texas Instruments + * + * 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 +//#define VERY_VERBOSE_LOGGING +#ifdef VERY_VERBOSE_LOGGING +#define ALOGVV ALOGV +#else +#define ALOGVV(a...) do { } while(0) +#endif + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#define UNUSED(x) (void)(x) + +/* yet another definition of ARRAY_SIZE macro) */ +#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0])) + +#define DEFAULT_AUDIO_FORMAT AUDIO_FORMAT_PCM_24_BIT_PACKED + +struct j7_audio_device { + struct audio_hw_device device; + struct j7_stream_in *in; + struct j7_stream_out *out; + struct audio_route *route; + pthread_mutex_t lock; + unsigned int card; + bool mic_mute; + audio_mode_t mode; +}; + +struct j7_stream_in { + struct audio_stream_in stream; + struct j7_audio_device *dev; + struct pcm_config config; + struct pcm *pcm; + struct resampler_buffer_provider buf_provider; + size_t frames_in; + size_t hw_frame_size; + unsigned int requested_rate; + unsigned int requested_channels; + unsigned int card; + unsigned int port; + audio_devices_t device; + int read_status; + pthread_mutex_t lock; + bool standby; +}; + +struct j7_stream_out { + struct audio_stream_out stream; + struct j7_audio_device *dev; + struct pcm_config config; + struct pcm *pcm; + struct timespec last; + pthread_mutex_t lock; + size_t hw_frame_size; + unsigned int requested_rate; + unsigned int requested_channels; + unsigned int card; + unsigned int port; + audio_devices_t device; + bool standby; + int64_t written; /* total frames written, not cleared when entering standby */ +}; + +static const char *supported_media_cards[] = { + "j721e-cpb-analog", +}; + +#define MAX_CARD_COUNT 1 +#define BYTES_PER_SAMPLE (3 * sizeof(int8_t)) //for 24 bit 3 bytes are required + +#define SUPPORTED_IN_DEVICES (AUDIO_DEVICE_IN_WIRED_HEADSET | \ + AUDIO_DEVICE_IN_DEFAULT) + +#define SUPPORTED_OUT_DEVICES (AUDIO_DEVICE_OUT_SPEAKER | \ + AUDIO_DEVICE_OUT_DEFAULT) + +#define CAPTURE_SAMPLE_RATE 48000 /* 48K Hz*/ +#define CAPTURE_PERIOD_SIZE 256 +#define CAPTURE_PERIOD_COUNT 4 +#define CAPTURE_BUFFER_SIZE (CAPTURE_PERIOD_SIZE * CAPTURE_PERIOD_COUNT) + +#define PLAYBACK_SAMPLE_RATE 48000 /* 48K Hz*/ +#define PLAYBACK_PERIOD_SIZE 256 +#define PLAYBACK_PERIOD_COUNT 4 +#define PLAYBACK_BUFFER_SIZE (PLAYBACK_PERIOD_SIZE * PLAYBACK_PERIOD_COUNT) + +#define MIXER_XML_PATH "/vendor/etc/mixer_paths.xml" + +struct pcm_config pcm_config_capture = { + .channels = 2,/* Stereo mode */ + .rate = CAPTURE_SAMPLE_RATE,/* 48K Hz*/ + .format = PCM_FORMAT_S24_3LE, + .period_size = CAPTURE_PERIOD_SIZE, + .period_count = CAPTURE_PERIOD_COUNT, + .start_threshold = 1, + .stop_threshold = CAPTURE_BUFFER_SIZE, +}; + +struct pcm_config pcm_config_playback = { + .channels = 2,/* Stereo mode */ + .rate = PLAYBACK_SAMPLE_RATE,/* 48K Hz*/ + .format = PCM_FORMAT_S24_3LE, + .period_size = PLAYBACK_PERIOD_SIZE, + .period_count = PLAYBACK_PERIOD_COUNT, + .start_threshold = PLAYBACK_BUFFER_SIZE / 2, + .stop_threshold = PLAYBACK_BUFFER_SIZE, + .avail_min = PLAYBACK_PERIOD_SIZE, +}; + +/* + * Recommended buffer sizes for the mandatory sampling frequencies. + * The buffer sizes were specifically chosen to avoid multiple + * resample_from_input() calls to consume an output buffer, which + * simplifies the HAL's write() function. + * The hardware natively supports 48kHz, but the rate of + * the playback and capture streams must be symmetric (they share the + * same FSYNC). + */ +static inline uint32_t output_buffer_size(uint32_t rate) +{ + uint32_t samples; + + if (rate == 48000){ + return PLAYBACK_PERIOD_SIZE; + } + else{ + /* + * 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 + */ + samples = (rate * PLAYBACK_PERIOD_SIZE) / PLAYBACK_SAMPLE_RATE; + samples = ((samples + 15) / 16) * 16; + ALOGE("output_buffer_size() samples=%d\n",samples); + } + return samples; +} + +static void do_out_standby(struct j7_stream_out *out); + +static size_t get_input_buffer_size(uint32_t sample_rate, int format, int channel_count) +{ + 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_capture.period_size * sample_rate) / pcm_config_capture.rate; + size = ((size + 15) / 16) * 16; + + return size * channel_count * audio_bytes_per_sample(format); +} +/* audio HAL functions */ + +static uint32_t out_get_sample_rate(const struct audio_stream *stream) +{ + const struct j7_stream_out *out = (const struct j7_stream_out *)stream; + + ALOGVV("out_get_sample_rate() stream=%p rate=%u", stream, out->requested_rate); + + return out->requested_rate; +} + +static int out_set_sample_rate(struct audio_stream *stream, uint32_t rate) +{ + UNUSED(stream); + UNUSED(rate); + ALOGVV("out_set_sample_rate() stream=%p rate=%u", stream, rate); + /* Only 48KHz is supported so we simply return 0 */ + return 0; +} + +static size_t out_get_buffer_size(const struct audio_stream *stream) +{ + const struct j7_stream_out *out = (const struct j7_stream_out *)stream; + uint32_t frames = output_buffer_size(out->requested_rate); + size_t bytes = frames * out->requested_channels * audio_bytes_per_sample(DEFAULT_AUDIO_FORMAT); + + ALOGE("out_get_buffer_size() stream=%p frames=%u bytes=%zu", stream, frames, bytes); + + return bytes; +} + +static audio_channel_mask_t out_get_channels(const struct audio_stream *stream) +{ + const struct j7_stream_out *out = (const struct j7_stream_out *)stream; + + ALOGVV("out_get_channels() stream=%p channels=%u", stream, out->requested_channels); + + return audio_channel_out_mask_from_count(out->requested_channels); +} + +static audio_format_t out_get_format(const struct audio_stream *stream) +{ + audio_format_t format = DEFAULT_AUDIO_FORMAT; + UNUSED(stream); + ALOGVV("out_get_format() stream=%p format=0x%08x (%zu bits/sample)", + stream, format, audio_bytes_per_sample(format) << 3); + return format; +} + +static int out_set_format(struct audio_stream *stream, audio_format_t format) +{ + UNUSED(stream); + ALOGVV("out_set_format() stream=%p format=0x%08x (%zu bits/sample)", + stream, format, audio_bytes_per_sample(format) << 3); + + if (format != DEFAULT_AUDIO_FORMAT) { + return -ENOSYS; + } else { + return 0; + } +} + +/* must be called with locks held */ +static void do_out_standby(struct j7_stream_out *out) +{ + struct j7_audio_device *adev = out->dev; + + if (!out->standby) { + ALOGV("do_out_standby() close card %u port %u", out->card, out->port); + pcm_close(out->pcm); + out->pcm = NULL; + out->standby = true; + } +} + +static int out_standby(struct audio_stream *stream) +{ + struct j7_stream_out *out = (struct j7_stream_out *)(stream); + struct j7_audio_device *adev = out->dev; + + ALOGV("out_standby() stream=%p", out); + pthread_mutex_lock(&adev->lock); + pthread_mutex_lock(&out->lock); + do_out_standby(out); + pthread_mutex_unlock(&out->lock); + pthread_mutex_unlock(&adev->lock); + + return 0; +} + +static int out_dump(const struct audio_stream *stream, int fd) +{ + UNUSED(stream); + UNUSED(fd); + /* As of now nothing to dump any debug data */ + return 0; +} + +static int out_set_parameters(struct audio_stream *stream, const char *kvpairs) +{ + struct j7_stream_out *out = (struct j7_stream_out *)(stream); + struct j7_audio_device *adev = out->dev; + struct str_parms *parms; + char value[32]; + int ret; + uint32_t val = 0; + + ALOGV("out_set_parameters() stream=%p parameter='%s'", out, kvpairs); + + parms = str_parms_create_str(kvpairs); + + ret = str_parms_get_str(parms, AUDIO_PARAMETER_STREAM_ROUTING, value, sizeof(value)); + if (ret >= 0) { + /* Here the value is properly checked and filled by the framework */ + val = atoi(value); + pthread_mutex_lock(&adev->lock); + pthread_mutex_lock(&out->lock); + if (val != 0) { + if ((out->device & AUDIO_DEVICE_OUT_ALL) != val) { + ALOGI("out_set_parameters() forcing standby"); + do_out_standby(out); + } + + /* set the active output device */ + out->device = val; + } + pthread_mutex_unlock(&out->lock); + pthread_mutex_unlock(&adev->lock); + } + str_parms_destroy(parms); + return 0; +} + +static char* out_get_parameters(const struct audio_stream *stream, const char *keys) +{ + UNUSED(stream); + UNUSED(keys); + /* Caller will be freeing this memory */ + return strdup(""); +} + +static uint32_t out_get_latency(const struct audio_stream_out *stream) +{ + const struct j7_stream_out *out = (const struct j7_stream_out *)(stream); + uint32_t frames = PLAYBACK_BUFFER_SIZE; + uint32_t latency = (frames * 1000) / PLAYBACK_SAMPLE_RATE; + + ALOGVV("out_get_latency() stream=%p latency=%u msecs", out, latency); + + return latency; +} + +static int out_set_volume(struct audio_stream_out *stream, float left, + float right) +{ + UNUSED(stream); + UNUSED(left); + UNUSED(right); + /* As of now we dont support this operation */ + return -ENOSYS; +} + +static ssize_t out_write(struct audio_stream_out *stream, const void* buffer, + size_t bytes) +{ + const struct audio_stream_out *s = (const struct audio_stream_out *)stream; + struct j7_stream_out *out = (struct j7_stream_out *)(stream); + struct j7_audio_device *adev = out->dev; + struct timespec now; + const size_t frame_size = audio_stream_out_frame_size(s); + size_t frames = bytes / frame_size; + size_t hw_frames; + uint32_t rate = out->requested_rate; + uint32_t write_usecs = frames * 1000000 / rate; + uint32_t diff_usecs; + const void *hw_buf; + void *in_buf; + int ret = 0; + + ALOGVV("out_write() stream=%p buffer=%p size=%zu/%u time=%u usecs", + out, buffer, frames, rate, write_usecs); + + pthread_mutex_lock(&adev->lock); + pthread_mutex_lock(&out->lock); + + if (out->standby) { + ALOGI("out_write() open card %u port %u", out->card, out->port); + + out->pcm = pcm_open(out->card, out->port, + PCM_OUT | PCM_MONOTONIC, + &out->config); + if (!pcm_is_ready(out->pcm)) { + ALOGE("out_write() failed to open pcm out: %s", pcm_get_error(out->pcm)); + pcm_close(out->pcm); + out->pcm = NULL; + ret = -ENODEV; + } + if (ret) { + usleep(write_usecs); /* limits the rate of error messages */ + pthread_mutex_unlock(&out->lock); + pthread_mutex_unlock(&adev->lock); + return ret; + } + + out->standby = false; + } + + pthread_mutex_unlock(&adev->lock); + + hw_frames = frames; + hw_buf = buffer; + + ret = pcm_write(out->pcm, hw_buf, hw_frames * out->hw_frame_size); + if (ret) { + ALOGE("out_write() failed to write audio data %d", ret); + usleep(write_usecs); /* limits the rate of error messages */ + } + + out->written += frames; + + pthread_mutex_unlock(&out->lock); + + return bytes; +} + +static int out_get_render_position(const struct audio_stream_out *stream, + uint32_t *dsp_frames) +{ + UNUSED(stream); + UNUSED(dsp_frames); + /* As of now we dont support this operation */ + return -ENOSYS; +} + +static int out_add_audio_effect(const struct audio_stream *stream, effect_handle_t effect) +{ + UNUSED(stream); + UNUSED(effect); + /* As of now we dont support this operation */ + return 0; +} + +static int out_remove_audio_effect(const struct audio_stream *stream, effect_handle_t effect) +{ + UNUSED(stream); + UNUSED(effect); + /* As of now we dont support this operation */ + return 0; +} + +static int out_get_next_write_timestamp(const struct audio_stream_out *stream, + int64_t *timestamp) +{ + UNUSED(stream); + UNUSED(timestamp); + /* As of now we dont support this operation */ + return -ENOSYS; +} + +static int out_get_presentation_position(const struct audio_stream_out *stream, + uint64_t *frames, struct timespec *timestamp) +{ + struct j7_stream_out *out = (struct j7_stream_out *)(stream); + struct j7_audio_device *adev = out->dev; + int64_t signed_frames = -1; + uint32_t avail; + int ret = -1; + + pthread_mutex_lock(&out->lock); + + if (out->pcm && + (pcm_get_htimestamp(out->pcm, &avail, timestamp) == 0)) { + signed_frames = out->written - pcm_get_buffer_size(out->pcm) + avail; + } + + /* It would be unusual for this value to be negative, but check just in case ... */ + if (signed_frames >= 0) { + *frames = signed_frames; + ret = 0; + } + + pthread_mutex_unlock(&out->lock); + + return ret; +} + +/** audio_stream_in implementation **/ +static uint32_t in_get_sample_rate(const struct audio_stream *stream) +{ + const struct j7_stream_in *in = (const struct j7_stream_in *)(stream); + + ALOGVV("in_get_sample_rate() stream=%p rate=%u", stream, in->requested_rate); + + return in->requested_rate; +} + +static int in_set_sample_rate(struct audio_stream *stream, uint32_t rate) +{ + ALOGV("in_set_sample_rate() stream=%p rate=%u", stream, rate); + /* Only 48KHz is supported so we simply return 0 */ + return 0; +} + +static size_t in_get_buffer_size(const struct audio_stream *stream) +{ + const struct j7_stream_in *in = (const struct j7_stream_in *)(stream); + + size_t bytes = get_input_buffer_size(in->requested_rate, + DEFAULT_AUDIO_FORMAT, + in->requested_channels); + + ALOGE("in_get_buffer_size() stream=%p bytes=%zu", in, bytes); + + return bytes; +} + +static audio_channel_mask_t in_get_channels(const struct audio_stream *stream) +{ + const struct j7_stream_in *in = (const struct j7_stream_in *)(stream); + audio_channel_mask_t channels = audio_channel_in_mask_from_count(in->requested_channels); + + ALOGVV("in_get_channels() stream=%p channels=%u", in, in->requested_channels); + + return channels; +} + +static audio_format_t in_get_format(const struct audio_stream *stream) +{ + audio_format_t format = DEFAULT_AUDIO_FORMAT; + + UNUSED(stream); + ALOGVV("in_set_format() stream=%p format=0x%08x (%zu bits/sample)", + stream, format, audio_bytes_per_sample(format) << 3); + + return format; +} + +static int in_set_format(struct audio_stream *stream, audio_format_t format) +{ + UNUSED(stream); + ALOGV("in_set_format() stream=%p format=0x%08x (%zu bits/sample)", + stream, format, audio_bytes_per_sample(format) << 3); + + if (format != DEFAULT_AUDIO_FORMAT) { + return -ENOSYS; + } else { + return 0; + } +} + +/* must be called with locks held */ +static void do_in_standby(struct j7_stream_in *in) +{ + struct j7_audio_device *adev = in->dev; + + if (!in->standby) { + ALOGI("do_in_standby() close card %u port %u", in->card, in->port); + pcm_close(in->pcm); + in->pcm = NULL; + in->standby = true; + } +} + +static int in_standby(struct audio_stream *stream) +{ + struct j7_stream_in *in = (struct j7_stream_in *)(stream); + struct j7_audio_device *adev = in->dev; + + ALOGV("in_standby() stream=%p", in); + pthread_mutex_lock(&adev->lock); + pthread_mutex_lock(&in->lock); + do_in_standby(in); + pthread_mutex_unlock(&in->lock); + pthread_mutex_unlock(&adev->lock); + + return 0; +} + +static int in_dump(const struct audio_stream *stream, int fd) +{ + UNUSED(stream); + UNUSED(fd); + /* As of now nothing to dump anu debug data */ + return 0; +} + +static int in_set_parameters(struct audio_stream *stream, const char *kvpairs) +{ + struct j7_stream_in *in = (struct j7_stream_in *)(stream); + struct j7_audio_device *adev = in->dev; + struct str_parms *parms; + char value[32]; + int ret; + uint32_t val = 0; + + ALOGV("in_set_parameters() stream=%p parameter='%s'", stream, kvpairs); + + parms = str_parms_create_str(kvpairs); + + /* Nothing to do for AUDIO_PARAMETER_STREAM_INPUT_SOURCE, so it's ignored */ + + ret = str_parms_get_str(parms, AUDIO_PARAMETER_STREAM_ROUTING, value, sizeof(value)); + if (ret >= 0) { + /* Here the value is properly checked and filled by the framework */ + val = atoi(value); + pthread_mutex_lock(&adev->lock); + pthread_mutex_lock(&in->lock); + if (val != 0) { + if ((in->device & AUDIO_DEVICE_IN_ALL) != val) + do_in_standby(in); + + /* set the active input device */ + in->device = val; + } + pthread_mutex_unlock(&in->lock); + pthread_mutex_unlock(&adev->lock); + } + str_parms_destroy(parms); + return 0; +} + +static char * in_get_parameters(const struct audio_stream *stream, + const char *keys) +{ + UNUSED(stream); + UNUSED(keys); + /* As of now we dont support this operation */ + return strdup(""); +} + +static int in_set_gain(struct audio_stream_in *stream, float gain) +{ + UNUSED(stream); + UNUSED(gain); + /* As of now we dont support this operation */ + return 0; +} + +static ssize_t in_read(struct audio_stream_in *stream, void* buffer, + size_t bytes) +{ + const struct audio_stream_in *s = (const struct audio_stream_in *)stream; + struct j7_stream_in *in = (struct j7_stream_in *)(stream); + struct j7_audio_device *adev = in->dev; + const size_t frame_size = audio_stream_in_frame_size(stream); + const size_t frames = bytes / frame_size; + uint32_t rate = in_get_sample_rate(&stream->common); + uint32_t read_usecs = frames * 1000000 / rate; + int ret; + + ALOGVV("in_read() stream=%p buffer=%p size=%zu/%u time=%u usecs", + stream, buffer, frames, rate, read_usecs); + + pthread_mutex_lock(&adev->lock); + pthread_mutex_lock(&in->lock); + + if (in->standby) { + ALOGE("in_read() open card %u port %u", in->card, in->port); + in->pcm = pcm_open(in->card, in->port, + PCM_IN | PCM_MONOTONIC, + &in->config); + if (!pcm_is_ready(in->pcm)) { + ALOGE("in_read() failed to open pcm in: %s", pcm_get_error(in->pcm)); + pcm_close(in->pcm); + in->pcm = NULL; + usleep(read_usecs); /* limits the rate of error messages */ + pthread_mutex_unlock(&in->lock); + pthread_mutex_unlock(&adev->lock); + return -ENODEV; + } + in->standby = false; + } + + pthread_mutex_unlock(&adev->lock); + ret = pcm_read(in->pcm, buffer, bytes); + + if (ret < 0) { + ALOGE("in_read() failed to read audio data %d", ret); + usleep(read_usecs); /* limits the rate of error messages */ + memset(buffer, 0, bytes); + } else if (adev->mic_mute) { + memset(buffer, 0, bytes); + } + + pthread_mutex_unlock(&in->lock); + + return bytes; +} + +static uint32_t in_get_input_frames_lost(struct audio_stream_in *stream) +{ + UNUSED(stream); + ALOGVV("in_get_input_frames_lost() stream=%p frames=%u", stream, 0); + /* As of now we dont support this operation */ + return 0; +} + +static int in_add_audio_effect(const struct audio_stream *stream, effect_handle_t effect) +{ + UNUSED(stream); + UNUSED(effect); + /* As of now we dont support this operation */ + return 0; +} + +static int in_remove_audio_effect(const struct audio_stream *stream, effect_handle_t effect) +{ + UNUSED(stream); + UNUSED(effect); + /* As of now we dont support this operation */ + 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, + const char *address) +{ + struct j7_audio_device *adev = (struct j7_audio_device *)dev; + struct j7_stream_out *out; + int buffer_size; + int ret; + + UNUSED(handle); + UNUSED(devices); + UNUSED(address); + + out = (struct j7_stream_out *)calloc(1, sizeof(struct j7_stream_out)); + if (!out) + return -ENOMEM; + + ALOGV("adev_open_output_stream() stream=%p rate=%u channels=%u " + "format=0x%08x flags=0x%08x", + out, config->sample_rate, popcount(config->channel_mask), + config->format, flags); + + pthread_mutex_init(&out->lock, NULL); + + 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->stream.get_presentation_position = out_get_presentation_position; + + out->dev = adev; + out->standby = true; + out->config = pcm_config_playback; + out->requested_rate = config->sample_rate; + out->requested_channels = popcount(config->channel_mask); + out->hw_frame_size = out->config.channels * BYTES_PER_SAMPLE; + out->written = 0; + out->card = adev->card; + out->port = 0; + out->device = devices; + adev->out = out; + *stream_out = &out->stream; + + return 0; +} + +static void adev_close_output_stream(struct audio_hw_device *dev, + struct audio_stream_out *stream) +{ + struct j7_audio_device *adev = (struct j7_audio_device *)dev; + struct j7_stream_out *out = (struct j7_stream_out *)(stream); + + ALOGV("adev_close_output_stream() stream=%p", out); + + out_standby(&stream->common); + + free(out); + adev->out = NULL; +} + +static int adev_set_parameters(struct audio_hw_device *dev, const char *kvpairs) +{ + UNUSED(dev); + UNUSED(kvpairs); + + ALOGV("adev_set_parameters() parameter='%s'", kvpairs); + /* As of now we dont support this operation */ + return 0; +} + +static char * adev_get_parameters(const struct audio_hw_device *dev, + const char *keys) +{ + UNUSED(dev); + UNUSED(keys); + /* As of now we dont support this operation */ + return strdup("");; +} + +static int adev_init_check(const struct audio_hw_device *dev) +{ + UNUSED(dev); + /* As of now we dont support this operation */ + return 0; +} + +static int adev_set_voice_volume(struct audio_hw_device *dev, float volume) +{ + UNUSED(dev); + UNUSED(volume); + /* As of now we dont support this operation */ + return 0; +} + +static int adev_set_master_volume(struct audio_hw_device *dev, float volume) +{ + UNUSED(dev); + UNUSED(volume); + /* As of now we dont support this operation */ + return -ENOSYS; +} + +static int adev_get_master_volume(struct audio_hw_device *dev, float *volume) +{ + UNUSED(dev); + UNUSED(volume); + /* As of now we dont support this operation */ + return -ENOSYS; +} + +static int adev_set_master_mute(struct audio_hw_device *dev, bool muted) +{ + UNUSED(dev); + UNUSED(muted); + /* As of now we dont support this operation */ + return -ENOSYS; +} + +static int adev_get_master_mute(struct audio_hw_device *dev, bool *muted) +{ + UNUSED(dev); + UNUSED(muted); + /* As of now we dont support this operation */ + return -ENOSYS; +} + +static int adev_set_mode(struct audio_hw_device *dev, audio_mode_t mode) +{ + struct j7_audio_device *adev = (struct j7_audio_device *)dev; + int ret = 0; + + ALOGV("adev_set_mode() mode=0x%08x", mode); + + pthread_mutex_lock(&adev->lock); + + if (adev->mode == mode) { + ALOGV("adev_set_mode() already in mode=0x%08x", mode); + goto out; + } + + adev->mode = mode; + +out: + pthread_mutex_unlock(&adev->lock); + + return ret; +} + +static int adev_set_mic_mute(struct audio_hw_device *dev, bool state) +{ + struct j7_audio_device *adev = (struct j7_audio_device *)dev; + + ALOGV("adev_set_mic_mute() state=%s", state ? "mute" : "unmute"); + adev->mic_mute = state; + + return 0; +} + +static int adev_get_mic_mute(const struct audio_hw_device *dev, bool *state) +{ + const struct j7_audio_device *adev = (const struct j7_audio_device *)dev; + + *state = adev->mic_mute; + ALOGV("adev_get_mic_mute() state=%s", *state ? "mute" : "unmute"); + + return 0; +} + +static size_t adev_get_input_buffer_size(const struct audio_hw_device *dev, + const struct audio_config *config) +{ + UNUSED(dev); + + size_t bytes = get_input_buffer_size(config->sample_rate, + config->format, + popcount(config->channel_mask)); + + ALOGVV("adev_in_get_buffer_size() bytes=%zu", bytes); + + return bytes; +} + +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, + audio_input_flags_t flags, + const char *address, + audio_source_t source) +{ + struct j7_audio_device *adev = (struct j7_audio_device *)dev; + struct j7_stream_in *in; + int buffer_size; + int ret; + + UNUSED(handle); + UNUSED(flags); + UNUSED(address); + UNUSED(source); + + in = (struct j7_stream_in *)calloc(1, sizeof(struct j7_stream_in)); + if (!in) + return -ENOMEM; + + ALOGE("adev_open_input_stream() stream=%p rate=%u channels=%u format=0x%08x", + in, config->sample_rate, popcount(config->channel_mask), config->format); + + pthread_mutex_init(&in->lock, NULL); + + 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->config = pcm_config_capture; + in->requested_rate = config->sample_rate; + in->requested_channels = popcount(config->channel_mask); + in->hw_frame_size = in->config.channels * BYTES_PER_SAMPLE; + in->card = adev->card; + in->port = 1; + in->device = devices; + adev->in = in; + *stream_in = &in->stream; + + return 0; +} + +static void adev_close_input_stream(struct audio_hw_device *dev, + struct audio_stream_in *stream) +{ + struct j7_audio_device *adev = (struct j7_audio_device *)dev; + struct j7_stream_in *in = (struct j7_stream_in *)(stream); + + ALOGE("adev_close_input_stream() stream=%p", stream); + + in_standby(&stream->common); + + free(in); + adev->in = NULL; +} + +static int adev_dump(const audio_hw_device_t *device, int fd) +{ + UNUSED(device); + UNUSED(fd); + /* As of now nothing to dump any debug data */ + return 0; +} + +static int adev_close(hw_device_t *device) +{ + struct j7_audio_device *adev = (struct j7_audio_device *)device; + + ALOGI("adev_close()"); + + audio_route_free(adev->route); + + free(device); + + return 0; +} + +static int adev_open(const hw_module_t* module, const char* name, + hw_device_t** device) +{ + struct j7_audio_device *adev; + + ALOGI("adev_open() %s", name); + + if (strcmp(name, AUDIO_HARDWARE_INTERFACE) != 0) + return -EINVAL; + + adev = (struct j7_audio_device*)calloc(1, sizeof(struct j7_audio_device)); + if (!adev) + return -ENOMEM; + + pthread_mutex_init(&adev->lock, NULL); + + adev->device.common.tag = HARDWARE_DEVICE_TAG; + adev->device.common.version = AUDIO_DEVICE_API_VERSION_2_0; + adev->device.common.module = (struct hw_module_t *) module; + adev->device.common.close = adev_close; + adev->device.init_check = adev_init_check; + adev->device.set_voice_volume = adev_set_voice_volume; + adev->device.set_master_volume = adev_set_master_volume; + adev->device.get_master_volume = adev_get_master_volume; + adev->device.set_master_mute = adev_set_master_mute; + adev->device.get_master_mute = adev_get_master_mute; + adev->device.set_mode = adev_set_mode; + adev->device.set_mic_mute = adev_set_mic_mute; + adev->device.get_mic_mute = adev_get_mic_mute; + adev->device.set_parameters = adev_set_parameters; + adev->device.get_parameters = adev_get_parameters; + adev->device.get_input_buffer_size = adev_get_input_buffer_size; + adev->device.open_output_stream = adev_open_output_stream; + adev->device.close_output_stream = adev_close_output_stream; + adev->device.open_input_stream = adev_open_input_stream; + adev->device.close_input_stream = adev_close_input_stream; + adev->device.dump = adev_dump; + adev->card = 0; + + ALOGI("adev_open() media card is hw:%d", adev->card); + + adev->mic_mute = false; + + adev->mode = AUDIO_MODE_NORMAL; + + adev->route = audio_route_init(adev->card, MIXER_XML_PATH); + if (!adev->route) { + ALOGE("adev_open() unable to initialize audio routes"); + goto err1; + } + + *device = &adev->device.common; + + return 0; + + audio_route_free(adev->route); + err1: + free(adev); + return -ENODEV; +} + +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 = "Jacinto7 Audio HAL", + .author = "Texas Instruments Inc.", + .methods = &hal_module_methods, + }, +}; diff --git a/audio/primary/audio_policy_configuration.xml b/audio/primary/audio_policy_configuration.xml new file mode 100644 index 0000000..a4fc066 --- /dev/null +++ b/audio/primary/audio_policy_configuration.xml @@ -0,0 +1,34 @@ + + + + + Speaker + Built-In Mic + + + Speaker + + + + + + + + + + + + + + + + + + + + + + diff --git a/audio/primary/mixer_paths.xml b/audio/primary/mixer_paths.xml new file mode 100644 index 0000000..3f37eea --- /dev/null +++ b/audio/primary/mixer_paths.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/j721e.mk b/j721e.mk index cb3ff33..7324f59 100644 --- a/j721e.mk +++ b/j721e.mk @@ -16,3 +16,4 @@ PRODUCT_PACKAGES += \ hwcomposer.j721e \ + audio.primary.j721e \ -- cgit v1.2.3-54-g00ecf