| /* |
| * Copyright (C) 2011 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 <sys/time.h> |
| #include <stdlib.h> |
| |
| #include <cutils/log.h> |
| #include <cutils/str_parms.h> |
| |
| #include <hardware/hardware.h> |
| #include <system/audio.h> |
| #include <hardware/audio.h> |
| |
| #include <tinyalsa/asoundlib.h> |
| #include <speex/speex_resampler.h> |
| |
| #include "ril_interface.h" |
| |
| /* Mixer control names */ |
| #define MIXER_DL1_MEDIA_PLAYBACK_VOLUME "DL1 Media Playback Volume" |
| #define MIXER_DL1_VOICE_PLAYBACK_VOLUME "DL1 Voice Playback Volume" |
| #define MIXER_DL2_MEDIA_PLAYBACK_VOLUME "DL2 Media Playback Volume" |
| #define MIXER_DL2_VOICE_PLAYBACK_VOLUME "DL2 Voice Playback Volume" |
| #define MIXER_SDT_DL_VOLUME "SDT DL Volume" |
| |
| #define MIXER_HEADSET_PLAYBACK_VOLUME "Headset Playback Volume" |
| #define MIXER_HANDSFREE_PLAYBACK_VOLUME "Handsfree Playback Volume" |
| #define MIXER_EARPHONE_PLAYBACK_VOLUME "Earphone Playback Volume" |
| #define MIXER_BT_UL_VOLUME "BT UL Volume" |
| |
| #define MIXER_DL1_MIXER_MULTIMEDIA "DL1 Mixer Multimedia" |
| #define MIXER_DL1_MIXER_VOICE "DL1 Mixer Voice" |
| #define MIXER_DL2_MIXER_MULTIMEDIA "DL2 Mixer Multimedia" |
| #define MIXER_DL2_MIXER_VOICE "DL2 Mixer Voice" |
| #define MIXER_SIDETONE_MIXER_PLAYBACK "Sidetone Mixer Playback" |
| #define MIXER_DL1_PDM_SWITCH "DL1 PDM Switch" |
| #define MIXER_DL1_BT_VX_SWITCH "DL1 BT_VX Switch" |
| #define MIXER_VOICE_CAPTURE_MIXER_CAPTURE "Voice Capture Mixer Capture" |
| |
| #define MIXER_HS_LEFT_PLAYBACK "HS Left Playback" |
| #define MIXER_HS_RIGHT_PLAYBACK "HS Right Playback" |
| #define MIXER_HF_LEFT_PLAYBACK "HF Left Playback" |
| #define MIXER_HF_RIGHT_PLAYBACK "HF Right Playback" |
| #define MIXER_EARPHONE_ENABLE_SWITCH "Earphone Enable Switch" |
| |
| #define MIXER_ANALOG_LEFT_CAPTURE_ROUTE "Analog Left Capture Route" |
| #define MIXER_ANALOG_RIGHT_CAPTURE_ROUTE "Analog Right Capture Route" |
| #define MIXER_CAPTURE_PREAMPLIFIER_VOLUME "Capture Preamplifier Volume" |
| #define MIXER_CAPTURE_VOLUME "Capture Volume" |
| #define MIXER_AMIC_UL_VOLUME "AMIC UL Volume" |
| #define MIXER_AUDUL_VOICE_UL_VOLUME "AUDUL Voice UL Volume" |
| #define MIXER_MUX_VX0 "MUX_VX0" |
| #define MIXER_MUX_VX1 "MUX_VX1" |
| #define MIXER_MUX_UL10 "MUX_UL10" |
| #define MIXER_MUX_UL11 "MUX_UL11" |
| |
| /* Mixer control gain and route values */ |
| #define MIXER_ABE_GAIN_0DB 120 |
| #define MIXER_CODEC_VOLUME_MAX 15 |
| #define MIXER_PLAYBACK_HS_DAC "HS DAC" |
| #define MIXER_PLAYBACK_HF_DAC "HF DAC" |
| #define MIXER_MAIN_MIC "Main Mic" |
| #define MIXER_SUB_MIC "Sub Mic" |
| #define MIXER_HS_MIC "Headset Mic" |
| #define MIXER_AMIC0 "AMic0" |
| #define MIXER_AMIC1 "AMic1" |
| #define MIXER_BT_LEFT "BT Left" |
| #define MIXER_BT_RIGHT "BT Right" |
| |
| /* ALSA ports for OMAP4 */ |
| #define PORT_MM 0 |
| #define PORT_MM2_UL 1 |
| #define PORT_VX 2 |
| #define PORT_TONES 3 |
| #define PORT_VIBRA 4 |
| #define PORT_MODEM 5 |
| #define PORT_MM_LP 6 |
| |
| #define RESAMPLER_BUFFER_SIZE 8192 |
| |
| #define DEFAULT_OUT_SAMPLING_RATE 44100 |
| |
| struct pcm_config pcm_config_mm = { |
| .channels = 2, |
| .rate = 48000, |
| .period_size = 1024, |
| .period_count = 4, |
| .format = PCM_FORMAT_S16_LE, |
| }; |
| |
| struct pcm_config pcm_config_vx = { |
| .channels = 1, |
| .rate = 8000, |
| .period_size = 160, |
| .period_count = 2, |
| .format = PCM_FORMAT_S16_LE, |
| }; |
| |
| #define MIN(x, y) ((x) > (y) ? (y) : (x)) |
| |
| struct route_setting |
| { |
| char *ctl_name; |
| int intval; |
| char *strval; |
| }; |
| |
| /* These are values that never change */ |
| struct route_setting defaults[] = { |
| /* general */ |
| { |
| .ctl_name = MIXER_DL1_MEDIA_PLAYBACK_VOLUME, |
| .intval = MIXER_ABE_GAIN_0DB, |
| }, |
| { |
| .ctl_name = MIXER_DL2_MEDIA_PLAYBACK_VOLUME, |
| .intval = MIXER_ABE_GAIN_0DB, |
| }, |
| { |
| .ctl_name = MIXER_DL1_VOICE_PLAYBACK_VOLUME, |
| .intval = MIXER_ABE_GAIN_0DB, |
| }, |
| { |
| .ctl_name = MIXER_DL2_VOICE_PLAYBACK_VOLUME, |
| .intval = MIXER_ABE_GAIN_0DB, |
| }, |
| { |
| .ctl_name = MIXER_SDT_DL_VOLUME, |
| .intval = MIXER_ABE_GAIN_0DB, |
| }, |
| { |
| .ctl_name = MIXER_HEADSET_PLAYBACK_VOLUME, |
| .intval = 13, |
| }, |
| { |
| .ctl_name = MIXER_EARPHONE_PLAYBACK_VOLUME, |
| .intval = 15, |
| }, |
| { |
| .ctl_name = MIXER_HANDSFREE_PLAYBACK_VOLUME, |
| .intval = 26, /* max for no distortion */ |
| }, |
| { |
| .ctl_name = MIXER_AUDUL_VOICE_UL_VOLUME, |
| .intval = MIXER_ABE_GAIN_0DB, |
| }, |
| { |
| .ctl_name = MIXER_CAPTURE_PREAMPLIFIER_VOLUME, |
| .intval = 1, |
| }, |
| { |
| .ctl_name = MIXER_CAPTURE_VOLUME, |
| .intval = 4, |
| }, |
| |
| /* speaker */ |
| { |
| .ctl_name = MIXER_HF_LEFT_PLAYBACK, |
| .strval = MIXER_PLAYBACK_HF_DAC, |
| }, |
| { |
| .ctl_name = MIXER_HF_RIGHT_PLAYBACK, |
| .strval = MIXER_PLAYBACK_HF_DAC, |
| }, |
| |
| /* headset */ |
| { |
| .ctl_name = MIXER_SIDETONE_MIXER_PLAYBACK, |
| .intval = 1, |
| }, |
| { |
| .ctl_name = MIXER_DL1_PDM_SWITCH, |
| .intval = 1, |
| }, |
| { |
| .ctl_name = MIXER_HS_LEFT_PLAYBACK, |
| .strval = MIXER_PLAYBACK_HS_DAC, |
| }, |
| { |
| .ctl_name = MIXER_HS_RIGHT_PLAYBACK, |
| .strval = MIXER_PLAYBACK_HS_DAC, |
| }, |
| |
| /* bt */ |
| { |
| .ctl_name = MIXER_BT_UL_VOLUME, |
| .intval = MIXER_ABE_GAIN_0DB, |
| }, |
| { |
| .ctl_name = NULL, |
| }, |
| }; |
| |
| /* MM UL front-end paths */ |
| struct route_setting mm_ul2_bt[] = { |
| { |
| .ctl_name = MIXER_MUX_UL10, |
| .strval = MIXER_BT_LEFT, |
| }, |
| { |
| .ctl_name = MIXER_MUX_UL11, |
| .strval = MIXER_BT_RIGHT, |
| }, |
| { |
| .ctl_name = NULL, |
| }, |
| }; |
| |
| struct route_setting mm_ul2_amic[] = { |
| { |
| .ctl_name = MIXER_MUX_UL10, |
| .strval = MIXER_AMIC0, |
| }, |
| { |
| .ctl_name = MIXER_MUX_UL11, |
| .strval = MIXER_AMIC1, |
| }, |
| { |
| .ctl_name = NULL, |
| }, |
| }; |
| |
| /* VX UL front-end paths */ |
| struct route_setting vx_ul_amic_left[] = { |
| { |
| .ctl_name = MIXER_MUX_VX0, |
| .strval = MIXER_AMIC0, |
| }, |
| { |
| .ctl_name = MIXER_MUX_VX1, |
| .strval = MIXER_AMIC1, |
| }, |
| { |
| .ctl_name = MIXER_VOICE_CAPTURE_MIXER_CAPTURE, |
| .intval = 1, |
| }, |
| { |
| .ctl_name = NULL, |
| }, |
| }; |
| |
| struct route_setting vx_ul_amic_right[] = { |
| { |
| .ctl_name = MIXER_MUX_VX0, |
| .strval = MIXER_AMIC1, |
| }, |
| { |
| .ctl_name = MIXER_MUX_VX1, |
| .strval = MIXER_AMIC0, |
| }, |
| { |
| .ctl_name = MIXER_VOICE_CAPTURE_MIXER_CAPTURE, |
| .intval = 1, |
| }, |
| { |
| .ctl_name = NULL, |
| }, |
| }; |
| |
| struct route_setting vx_ul_bt[] = { |
| { |
| .ctl_name = MIXER_MUX_VX0, |
| .strval = MIXER_BT_LEFT, |
| }, |
| { |
| .ctl_name = MIXER_MUX_VX1, |
| .strval = MIXER_BT_RIGHT, |
| }, |
| { |
| .ctl_name = MIXER_VOICE_CAPTURE_MIXER_CAPTURE, |
| .intval = 1, |
| }, |
| { |
| .ctl_name = NULL, |
| }, |
| }; |
| |
| struct mixer_ctls |
| { |
| struct mixer_ctl *mm_dl1; |
| struct mixer_ctl *mm_dl2; |
| struct mixer_ctl *vx_dl1; |
| struct mixer_ctl *vx_dl2; |
| struct mixer_ctl *earpiece_enable; |
| struct mixer_ctl *dl1_headset; |
| struct mixer_ctl *dl1_bt; |
| struct mixer_ctl *left_capture; |
| struct mixer_ctl *right_capture; |
| }; |
| |
| struct tuna_audio_device { |
| struct audio_hw_device hw_device; |
| |
| pthread_mutex_t lock; |
| struct mixer *mixer; |
| struct mixer_ctls mixer_ctls; |
| int mode; |
| int devices; |
| struct pcm *pcm_modem_dl; |
| struct pcm *pcm_modem_ul; |
| int in_call; |
| float voice_volume; |
| struct tuna_stream_in *active_input; |
| /* RIL */ |
| struct ril_handle ril; |
| }; |
| |
| struct tuna_stream_out { |
| struct audio_stream_out stream; |
| |
| pthread_mutex_t lock; |
| struct pcm_config config; |
| struct pcm *pcm; |
| int device; |
| SpeexResamplerState *speex; |
| char *buffer; |
| int standby; |
| |
| struct tuna_audio_device *dev; |
| }; |
| |
| struct tuna_stream_in { |
| struct audio_stream_in stream; |
| |
| pthread_mutex_t lock; |
| struct pcm_config config; |
| struct pcm *pcm; |
| int device; |
| SpeexResamplerState *speex; |
| char *buffer; |
| size_t frames_in; |
| unsigned int requested_rate; |
| int port; |
| int standby; |
| |
| struct tuna_audio_device *dev; |
| }; |
| |
| static void select_output_device(struct tuna_audio_device *adev); |
| static void select_input_device(struct tuna_audio_device *adev); |
| static int adev_set_voice_volume(struct audio_hw_device *dev, float volume); |
| |
| /* The enable flag when 0 makes the assumption that enums are disabled by |
| * "Off" and integers/booleans by 0 */ |
| static int set_route_by_array(struct mixer *mixer, struct route_setting *route, |
| int enable) |
| { |
| struct mixer_ctl *ctl; |
| unsigned int i, j; |
| |
| /* Go through the route array and set each value */ |
| i = 0; |
| while (route[i].ctl_name) { |
| ctl = mixer_get_ctl_by_name(mixer, route[i].ctl_name); |
| if (!ctl) |
| return -EINVAL; |
| |
| if (route[i].strval) { |
| if (enable) |
| mixer_ctl_set_enum_by_string(ctl, route[i].strval); |
| else |
| mixer_ctl_set_enum_by_string(ctl, "Off"); |
| } else { |
| /* This ensures multiple (i.e. stereo) values are set jointly */ |
| for (j = 0; j < mixer_ctl_get_num_values(ctl); j++) { |
| if (enable) |
| mixer_ctl_set_value(ctl, j, route[i].intval); |
| else |
| mixer_ctl_set_value(ctl, j, 0); |
| } |
| } |
| i++; |
| } |
| |
| return 0; |
| } |
| |
| static int start_call(struct tuna_audio_device *adev) |
| { |
| /* Open modem PCM channels */ |
| if (adev->pcm_modem_dl == NULL) { |
| adev->pcm_modem_dl = pcm_open(0, PORT_MODEM, PCM_OUT, &pcm_config_vx); |
| if (!pcm_is_ready(adev->pcm_modem_dl)) { |
| LOGE("cannot open PCM modem DL stream: %s", pcm_get_error(adev->pcm_modem_dl)); |
| goto err_open_dl; |
| } |
| } |
| |
| if (adev->pcm_modem_ul == NULL) { |
| adev->pcm_modem_ul = pcm_open(0, PORT_MODEM, PCM_IN, &pcm_config_vx); |
| if (!pcm_is_ready(adev->pcm_modem_ul)) { |
| LOGE("cannot open PCM modem UL stream: %s", pcm_get_error(adev->pcm_modem_ul)); |
| goto err_open_ul; |
| } |
| } |
| |
| ril_set_call_clock_sync(&adev->ril, SOUND_CLOCK_START); |
| ril_set_call_audio_path(&adev->ril, SOUND_AUDIO_PATH_HANDSET); |
| |
| pcm_start(adev->pcm_modem_dl); |
| pcm_start(adev->pcm_modem_ul); |
| |
| return 0; |
| |
| err_open_dl: |
| pcm_close(adev->pcm_modem_dl); |
| adev->pcm_modem_dl = NULL; |
| err_open_ul: |
| pcm_close(adev->pcm_modem_ul); |
| adev->pcm_modem_ul = NULL; |
| |
| return -ENOMEM; |
| } |
| |
| static void end_call(struct tuna_audio_device *adev) |
| { |
| pcm_stop(adev->pcm_modem_dl); |
| pcm_stop(adev->pcm_modem_ul); |
| pcm_close(adev->pcm_modem_dl); |
| pcm_close(adev->pcm_modem_ul); |
| adev->pcm_modem_dl = NULL; |
| adev->pcm_modem_ul = NULL; |
| } |
| |
| static void select_mode(struct tuna_audio_device *adev) |
| { |
| if (adev->mode == AUDIO_MODE_IN_CALL) { |
| if (!adev->in_call) { |
| select_output_device(adev); |
| start_call(adev); |
| adev_set_voice_volume(&adev->hw_device, adev->voice_volume); |
| adev->in_call = 1; |
| } |
| } else { |
| if (adev->in_call) { |
| adev->in_call = 0; |
| end_call(adev); |
| select_output_device(adev); |
| select_input_device(adev); |
| } |
| } |
| } |
| |
| static void select_output_device(struct tuna_audio_device *adev) |
| { |
| int headset_on; |
| int speaker_on; |
| int earpiece_on; |
| int bt_on; |
| int dl1_on; |
| |
| /* tear down call stream before changing route, |
| otherwise microphone does not function */ |
| if (adev->in_call) |
| end_call(adev); |
| |
| headset_on = adev->devices & |
| (AUDIO_DEVICE_OUT_WIRED_HEADSET | AUDIO_DEVICE_OUT_WIRED_HEADPHONE); |
| speaker_on = adev->devices & AUDIO_DEVICE_OUT_SPEAKER; |
| earpiece_on = adev->devices & AUDIO_DEVICE_OUT_EARPIECE; |
| bt_on = adev->devices & AUDIO_DEVICE_OUT_ALL_SCO; |
| dl1_on = headset_on | earpiece_on | bt_on; |
| |
| /* Select front end */ |
| mixer_ctl_set_value(adev->mixer_ctls.mm_dl2, 0, speaker_on); |
| mixer_ctl_set_value(adev->mixer_ctls.vx_dl2, 0, |
| speaker_on && (adev->mode == AUDIO_MODE_IN_CALL)); |
| mixer_ctl_set_value(adev->mixer_ctls.mm_dl1, 0, dl1_on); |
| mixer_ctl_set_value(adev->mixer_ctls.vx_dl1, 0, |
| dl1_on && (adev->mode == AUDIO_MODE_IN_CALL)); |
| /* Select back end */ |
| mixer_ctl_set_value(adev->mixer_ctls.dl1_headset, 0, headset_on | earpiece_on); |
| mixer_ctl_set_value(adev->mixer_ctls.dl1_bt, 0, bt_on); |
| mixer_ctl_set_value(adev->mixer_ctls.earpiece_enable, 0, earpiece_on); |
| |
| /* Special case: select input path if in a call, otherwise |
| in_set_parameters is used to update the input route |
| todo: use sub mic for handsfree case */ |
| if (adev->mode == AUDIO_MODE_IN_CALL) { |
| if (bt_on) |
| set_route_by_array(adev->mixer, vx_ul_bt, bt_on); |
| else { |
| if (headset_on || earpiece_on) |
| set_route_by_array(adev->mixer, vx_ul_amic_left, 1); |
| else if (speaker_on) |
| set_route_by_array(adev->mixer, vx_ul_amic_right, 1); |
| else |
| set_route_by_array(adev->mixer, vx_ul_amic_left, 0); |
| |
| mixer_ctl_set_enum_by_string(adev->mixer_ctls.left_capture, |
| earpiece_on ? MIXER_MAIN_MIC : |
| (headset_on ? MIXER_HS_MIC : "Off")); |
| mixer_ctl_set_enum_by_string(adev->mixer_ctls.right_capture, |
| speaker_on ? MIXER_SUB_MIC : "Off"); |
| } |
| } |
| if (adev->in_call) |
| start_call(adev); |
| } |
| |
| static void select_input_device(struct tuna_audio_device *adev) |
| { |
| int headset_on; |
| int main_mic_on; |
| int sub_mic_on = 0; /* not routing to sub-mic for now */ |
| int bt_on; |
| int anlg_mic_on; |
| int port; |
| |
| headset_on = adev->devices & AUDIO_DEVICE_IN_WIRED_HEADSET; |
| main_mic_on = adev->devices & AUDIO_DEVICE_IN_BUILTIN_MIC; |
| bt_on = adev->devices & AUDIO_DEVICE_IN_ALL_SCO; |
| anlg_mic_on = headset_on | main_mic_on | sub_mic_on; |
| |
| /* PORT_MM2_UL is only used when not in call and active input uses it. */ |
| port = PORT_VX; |
| if ((adev->mode != AUDIO_MODE_IN_CALL) && (adev->active_input != 0)) |
| port = adev->active_input->port; |
| |
| /* tear down call stream before changing route, |
| * otherwise microphone does not function |
| */ |
| if (adev->in_call) |
| end_call(adev); |
| |
| /* TODO: check how capture is possible during voice calls or if |
| * both use cases are mutually exclusive. |
| */ |
| if (bt_on) { |
| set_route_by_array(adev->mixer, mm_ul2_bt, (port != PORT_VX)); |
| set_route_by_array(adev->mixer, vx_ul_bt, (port == PORT_VX)); |
| } else { |
| /* Select front end */ |
| set_route_by_array(adev->mixer, mm_ul2_amic, |
| anlg_mic_on && (port != PORT_VX)); |
| set_route_by_array(adev->mixer, vx_ul_amic_left, |
| anlg_mic_on && (port == PORT_VX)); |
| |
| /* Select back end */ |
| if (headset_on) |
| mixer_ctl_set_enum_by_string(adev->mixer_ctls.left_capture, |
| MIXER_HS_MIC); |
| else |
| mixer_ctl_set_enum_by_string(adev->mixer_ctls.left_capture, |
| main_mic_on ? MIXER_MAIN_MIC : "Off"); |
| /* TODO: set up sub mic for BACK_MIC when gpio for sub_mic is enabled */ |
| } |
| |
| if (adev->in_call) |
| start_call(adev); |
| } |
| |
| static int start_output_stream(struct tuna_stream_out *out) |
| { |
| struct tuna_audio_device *adev = out->dev; |
| |
| pthread_mutex_lock(&adev->lock); |
| adev->devices &= ~AUDIO_DEVICE_OUT_ALL; |
| adev->devices |= out->device; |
| select_output_device(adev); |
| pthread_mutex_unlock(&adev->lock); |
| |
| out->pcm = pcm_open(0, PORT_MM, PCM_OUT, &out->config); |
| if (!pcm_is_ready(out->pcm)) { |
| LOGE("cannot open pcm_out driver: %s", pcm_get_error(out->pcm)); |
| pcm_close(out->pcm); |
| return -ENOMEM; |
| } |
| |
| return 0; |
| } |
| |
| static int check_input_parameters(uint32_t sample_rate, int format, int channel_count) |
| { |
| if (format != AUDIO_FORMAT_PCM_16_BIT) |
| return -EINVAL; |
| |
| if ((channel_count < 1) || (channel_count > 2)) |
| return -EINVAL; |
| |
| switch(sample_rate) { |
| case 8000: |
| case 11025: |
| case 16000: |
| case 22050: |
| case 24000: |
| case 32000: |
| case 44100: |
| case 48000: |
| break; |
| default: |
| return -EINVAL; |
| } |
| |
| return 0; |
| } |
| |
| static size_t get_input_buffer_size(uint32_t sample_rate, int format, int channel_count) |
| { |
| size_t size; |
| size_t device_rate; |
| |
| if (check_input_parameters(sample_rate, format, channel_count) != 0) |
| return 0; |
| |
| switch (sample_rate) { |
| case 8000: |
| size = pcm_config_vx.period_size; |
| device_rate = 8000; |
| break; |
| |
| case 11025: |
| case 16000: |
| size = pcm_config_vx.period_size * 2; |
| device_rate = 16000; |
| break; |
| |
| case 22050: |
| case 24000: |
| case 32000: |
| case 44100: |
| case 48000: |
| size = pcm_config_mm.period_size; |
| device_rate = 48000; |
| break; |
| |
| default: |
| return 0; |
| } |
| |
| size = (((size * sample_rate) / device_rate + 15) / 16) * 16; |
| |
| return size * channel_count * sizeof(short); |
| } |
| |
| static uint32_t out_get_sample_rate(const struct audio_stream *stream) |
| { |
| return DEFAULT_OUT_SAMPLING_RATE; |
| } |
| |
| static int out_set_sample_rate(struct audio_stream *stream, uint32_t rate) |
| { |
| return 0; |
| } |
| |
| static size_t out_get_buffer_size(const struct audio_stream *stream) |
| { |
| struct tuna_stream_out *out = (struct tuna_stream_out *)stream; |
| |
| /* 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_t size = (out->config.period_size * DEFAULT_OUT_SAMPLING_RATE) / |
| out->config.rate; |
| size = ((size + 15) / 16) * 16; |
| return 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 int out_get_format(const struct audio_stream *stream) |
| { |
| return AUDIO_FORMAT_PCM_16_BIT; |
| } |
| |
| static int out_set_format(struct audio_stream *stream, int format) |
| { |
| return 0; |
| } |
| |
| static int out_standby(struct audio_stream *stream) |
| { |
| struct tuna_stream_out *out = (struct tuna_stream_out *)stream; |
| |
| pthread_mutex_lock(&out->lock); |
| if (!out->standby) { |
| pcm_close(out->pcm); |
| out->pcm = NULL; |
| out->standby = 1; |
| } |
| pthread_mutex_unlock(&out->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 tuna_stream_out *out = (struct tuna_stream_out *)stream; |
| struct tuna_audio_device *adev = out->dev; |
| struct str_parms *parms; |
| char *str; |
| char value[32]; |
| int ret, val = 0; |
| |
| parms = str_parms_create_str(kvpairs); |
| |
| ret = str_parms_get_str(parms, AUDIO_PARAMETER_STREAM_ROUTING, value, sizeof(value)); |
| if (ret >= 0) { |
| val = atoi(value); |
| pthread_mutex_lock(&out->lock); |
| if ((out->device != val) && (val != 0)) { |
| out->device = val; |
| pthread_mutex_unlock(&out->lock); |
| pthread_mutex_lock(&adev->lock); |
| if (adev->mode == AUDIO_MODE_IN_CALL) { |
| adev->devices &= ~AUDIO_DEVICE_OUT_ALL; |
| adev->devices |= out->device; |
| select_output_device(adev); |
| pthread_mutex_unlock(&adev->lock); |
| } else { |
| pthread_mutex_unlock(&adev->lock); |
| out_standby(stream); |
| } |
| } else |
| pthread_mutex_unlock(&out->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 tuna_stream_out *out = (struct tuna_stream_out *)stream; |
| |
| return (out->config.period_size * out->config.period_count * 1000) / |
| out->config.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; |
| struct tuna_stream_out *out = (struct tuna_stream_out *)stream; |
| struct tuna_audio_device *adev = out->dev; |
| spx_uint32_t in_frames = bytes / 4; /* todo */ |
| spx_uint32_t out_frames = RESAMPLER_BUFFER_SIZE / 4; |
| unsigned int total_bytes; |
| unsigned int max_bytes; |
| unsigned int remaining_bytes; |
| unsigned int pos; |
| |
| pthread_mutex_lock(&out->lock); |
| if (out->standby) { |
| ret = start_output_stream(out); |
| if (ret == 0) |
| out->standby = 0; |
| } |
| speex_resampler_process_interleaved_int(out->speex, buffer, &in_frames, |
| (spx_int16_t *)out->buffer, |
| &out_frames); |
| |
| total_bytes = out_frames * 4; |
| max_bytes = pcm_get_buffer_size(out->pcm); |
| remaining_bytes = total_bytes; |
| for (pos = 0; pos < total_bytes; pos += max_bytes) { |
| int bytes_to_write = MIN(max_bytes, remaining_bytes); |
| |
| ret = pcm_write(out->pcm, (void *)(out->buffer + pos), bytes_to_write); |
| |
| if (ret != 0) { |
| usleep(bytes * 1000000 / audio_stream_frame_size(&stream->common) / |
| out_get_sample_rate(&stream->common)); |
| pthread_mutex_unlock(&out->lock); |
| return bytes; |
| } |
| |
| remaining_bytes -= bytes_to_write; |
| } |
| |
| pthread_mutex_unlock(&out->lock); |
| 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; |
| } |
| |
| /** audio_stream_in implementation **/ |
| static int start_input_stream(struct tuna_stream_in *in) |
| { |
| int ret = 0; |
| struct tuna_audio_device *adev = in->dev; |
| |
| pthread_mutex_lock(&adev->lock); |
| adev->devices &= ~AUDIO_DEVICE_IN_ALL; |
| adev->devices |= in->device; |
| adev->active_input = in; |
| select_input_device(adev); |
| pthread_mutex_unlock(&adev->lock); |
| |
| /* this assumes routing is done previously */ |
| in->pcm = pcm_open(0, in->port, PCM_IN, &in->config); |
| if (!pcm_is_ready(in->pcm)) { |
| LOGE("cannot open pcm_in driver: %s", pcm_get_error(in->pcm)); |
| pcm_close(in->pcm); |
| return -ENOMEM; |
| } |
| /* if no supported sample rate is available, use the resampler */ |
| if (in->speex) { |
| speex_resampler_reset_mem(in->speex); |
| in->frames_in = 0; |
| } |
| return 0; |
| } |
| |
| static uint32_t in_get_sample_rate(const struct audio_stream *stream) |
| { |
| struct tuna_stream_in *in = (struct tuna_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 tuna_stream_in *in = (struct tuna_stream_in *)stream; |
| |
| return get_input_buffer_size(in->requested_rate, |
| AUDIO_FORMAT_PCM_16_BIT, |
| in->config.channels); |
| } |
| |
| static uint32_t in_get_channels(const struct audio_stream *stream) |
| { |
| struct tuna_stream_in *in = (struct tuna_stream_in *)stream; |
| |
| if (in->config.channels == 1) { |
| return AUDIO_CHANNEL_IN_MONO; |
| } else { |
| return AUDIO_CHANNEL_IN_STEREO; |
| } |
| } |
| |
| static int in_get_format(const struct audio_stream *stream) |
| { |
| return AUDIO_FORMAT_PCM_16_BIT; |
| } |
| |
| static int in_set_format(struct audio_stream *stream, int format) |
| { |
| return 0; |
| } |
| |
| static int in_standby(struct audio_stream *stream) |
| { |
| struct tuna_stream_in *in = (struct tuna_stream_in *)stream; |
| struct tuna_audio_device *adev = in->dev; |
| |
| pthread_mutex_lock(&in->lock); |
| if (!in->standby) { |
| pcm_close(in->pcm); |
| in->pcm = NULL; |
| adev->active_input = 0; |
| pthread_mutex_lock(&adev->lock); |
| adev->devices &= ~AUDIO_DEVICE_IN_ALL; |
| adev->active_input = 0; |
| select_input_device(adev); |
| pthread_mutex_unlock(&adev->lock); |
| in->standby = 1; |
| } |
| pthread_mutex_unlock(&in->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 tuna_stream_in *in = (struct tuna_stream_in *)stream; |
| struct tuna_audio_device *adev = in->dev; |
| struct str_parms *parms; |
| char *str; |
| char value[32]; |
| int ret, val = 0; |
| |
| parms = str_parms_create_str(kvpairs); |
| |
| ret = str_parms_get_str(parms, AUDIO_PARAMETER_STREAM_ROUTING, value, sizeof(value)); |
| if (ret >= 0) { |
| val = atoi(value); |
| pthread_mutex_lock(&in->lock); |
| if ((in->device != val) && (val != 0)) { |
| in->device = val; |
| pthread_mutex_unlock(&in->lock); |
| in_standby(stream); |
| } else |
| pthread_mutex_unlock(&in->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 tuna_stream_in *in = (struct tuna_stream_in *)stream; |
| struct tuna_audio_device *adev = in->dev; |
| |
| pthread_mutex_lock(&in->lock); |
| if (in->standby) { |
| ret = start_input_stream(in); |
| if (ret == 0) |
| in->standby = 0; |
| } |
| |
| if (ret < 0) |
| goto exit; |
| |
| if (in->speex) { |
| size_t frame_size = audio_stream_frame_size(&in->stream.common); |
| size_t frames_rq = bytes / frame_size; |
| size_t frames_wr = 0; |
| |
| while (frames_wr < frames_rq) { |
| size_t frames_in; |
| size_t frames_out; |
| |
| if (in->frames_in == 0) { |
| ret = pcm_read(in->pcm, in->buffer, in->config.period_size * frame_size); |
| if (ret != 0) |
| break; |
| in->frames_in = in->config.period_size; |
| } |
| |
| frames_out = frames_rq - frames_wr; |
| frames_in = in->frames_in; |
| if (in->config.channels == 1) { |
| speex_resampler_process_int( |
| in->speex, |
| 0, |
| (short *)((char *)in->buffer + |
| (in->config.period_size - in->frames_in) * frame_size), |
| &frames_in, |
| (short *)((char *)buffer + frames_wr * frame_size), |
| &frames_out); |
| } else { |
| speex_resampler_process_interleaved_int( |
| in->speex, |
| (short *)((char *)in->buffer + |
| (in->config.period_size - in->frames_in) * frame_size), |
| &frames_in, |
| (short *)((char *)buffer + frames_wr * frame_size), |
| &frames_out); |
| } |
| frames_wr += frames_out; |
| in->frames_in -= frames_in; |
| } |
| } else { |
| ret = pcm_read(in->pcm, buffer, 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, |
| uint32_t devices, int *format, |
| uint32_t *channels, uint32_t *sample_rate, |
| struct audio_stream_out **stream_out) |
| { |
| struct tuna_audio_device *ladev = (struct tuna_audio_device *)dev; |
| struct tuna_stream_out *out; |
| int ret; |
| |
| out = (struct tuna_stream_out *)calloc(1, sizeof(struct tuna_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->config = pcm_config_mm; |
| |
| out->speex = speex_resampler_init(2, DEFAULT_OUT_SAMPLING_RATE, 48000, |
| SPEEX_RESAMPLER_QUALITY_DEFAULT, &ret); |
| speex_resampler_reset_mem(out->speex); |
| out->buffer = malloc(RESAMPLER_BUFFER_SIZE); /* todo: allow for reallocing */ |
| |
| out->device = devices; |
| out->dev = ladev; |
| out->standby = !!start_output_stream(out); |
| |
| *format = out_get_format(&out->stream.common); |
| *channels = out_get_channels(&out->stream.common); |
| *sample_rate = out_get_sample_rate(&out->stream.common); |
| |
| *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) |
| { |
| struct tuna_stream_out *out = (struct tuna_stream_out *)stream; |
| |
| out_standby(&stream->common); |
| if (out->buffer) |
| free(out->buffer); |
| if (out->speex) |
| speex_resampler_destroy(out->speex); |
| free(stream); |
| } |
| |
| static int adev_set_parameters(struct audio_hw_device *dev, const char *kvpairs) |
| { |
| return -ENOSYS; |
| } |
| |
| 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) |
| { |
| struct tuna_audio_device *adev = (struct tuna_audio_device *)dev; |
| |
| adev->voice_volume = volume; |
| |
| if (adev->mode == AUDIO_MODE_IN_CALL) |
| ril_set_call_volume(&adev->ril, SOUND_TYPE_VOICE, volume); |
| |
| return 0; |
| } |
| |
| 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, int mode) |
| { |
| struct tuna_audio_device *adev = (struct tuna_audio_device *)dev; |
| |
| pthread_mutex_lock(&adev->lock); |
| if (adev->mode != mode) { |
| adev->mode = mode; |
| select_mode(adev); |
| } |
| pthread_mutex_unlock(&adev->lock); |
| |
| return 0; |
| } |
| |
| static int adev_set_mic_mute(struct audio_hw_device *dev, bool state) |
| { |
| return -ENOSYS; |
| } |
| |
| static int adev_get_mic_mute(const struct audio_hw_device *dev, bool *state) |
| { |
| return -ENOSYS; |
| } |
| |
| static size_t adev_get_input_buffer_size(const struct audio_hw_device *dev, |
| uint32_t sample_rate, int format, |
| int channel_count) |
| { |
| size_t size; |
| |
| if (check_input_parameters(sample_rate, format, channel_count) != 0) |
| return 0; |
| |
| return get_input_buffer_size(sample_rate, format, channel_count); |
| } |
| |
| static int adev_open_input_stream(struct audio_hw_device *dev, uint32_t devices, |
| int *format, uint32_t *channel_mask, |
| uint32_t *sample_rate, |
| audio_in_acoustics_t acoustics, |
| struct audio_stream_in **stream_in) |
| { |
| struct tuna_audio_device *ladev = (struct tuna_audio_device *)dev; |
| struct tuna_stream_in *in; |
| int ret; |
| int channel_count = popcount(*channel_mask); |
| |
| if (check_input_parameters(*sample_rate, *format, channel_count) != 0) |
| return -EINVAL; |
| |
| in = (struct tuna_stream_in *)calloc(1, sizeof(struct tuna_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->requested_rate = *sample_rate; |
| |
| if (in->requested_rate <= 8000) { |
| in->port = PORT_VX; |
| memcpy(&in->config, &pcm_config_vx, sizeof(pcm_config_vx)); |
| in->config.rate = 8000; |
| } else if (in->requested_rate <= 16000) { |
| in->port = PORT_VX; /* use voice uplink */ |
| memcpy(&in->config, &pcm_config_vx, sizeof(pcm_config_vx)); |
| in->config.rate = 16000; |
| in->config.period_size *= 2; |
| } else { |
| in->port = PORT_MM2_UL; /* use multimedia uplink 2 */ |
| memcpy(&in->config, &pcm_config_mm, sizeof(pcm_config_mm)); |
| } |
| in->config.channels = channel_count; |
| |
| if (in->requested_rate != in->config.rate) { |
| in->speex = speex_resampler_init(in->config.channels, in->config.rate, |
| in->requested_rate, |
| SPEEX_RESAMPLER_QUALITY_DEFAULT, |
| &ret); |
| if (ret != RESAMPLER_ERR_SUCCESS) { |
| ret = -EINVAL; |
| goto err; |
| } |
| in->buffer = malloc(in->config.period_size * |
| audio_stream_frame_size(&in->stream.common)); |
| if (!in->buffer) { |
| ret = -ENOMEM; |
| goto err; |
| } |
| } |
| |
| in->dev = ladev; |
| in->standby = 1; |
| in->device = devices; |
| |
| *stream_in = &in->stream; |
| return 0; |
| |
| err: |
| if (in->speex) |
| speex_resampler_destroy(in->speex); |
| |
| free(in); |
| *stream_in = NULL; |
| return ret; |
| } |
| |
| static void adev_close_input_stream(struct audio_hw_device *dev, |
| struct audio_stream_in *stream) |
| { |
| struct tuna_stream_in *in = (struct tuna_stream_in *)stream; |
| |
| in_standby(&stream->common); |
| |
| if (in->speex) { |
| free(in->buffer); |
| speex_resampler_destroy(in->speex); |
| } |
| |
| free(stream); |
| return; |
| } |
| |
| static int adev_dump(const audio_hw_device_t *device, int fd) |
| { |
| return 0; |
| } |
| |
| static int adev_close(hw_device_t *device) |
| { |
| struct tuna_audio_device *adev = (struct tuna_audio_device *)device; |
| |
| /* RIL */ |
| ril_close(&adev->ril); |
| |
| mixer_close(adev->mixer); |
| free(device); |
| return 0; |
| } |
| |
| static uint32_t adev_get_supported_devices(const struct audio_hw_device *dev) |
| { |
| return (/* OUT */ |
| AUDIO_DEVICE_OUT_EARPIECE | |
| AUDIO_DEVICE_OUT_SPEAKER | |
| AUDIO_DEVICE_OUT_WIRED_HEADSET | |
| AUDIO_DEVICE_OUT_WIRED_HEADPHONE | |
| AUDIO_DEVICE_OUT_AUX_DIGITAL | |
| AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET | |
| AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET | |
| AUDIO_DEVICE_OUT_ALL_SCO | |
| AUDIO_DEVICE_OUT_DEFAULT | |
| /* IN */ |
| AUDIO_DEVICE_IN_COMMUNICATION | |
| AUDIO_DEVICE_IN_AMBIENT | |
| AUDIO_DEVICE_IN_BUILTIN_MIC | |
| AUDIO_DEVICE_IN_WIRED_HEADSET | |
| AUDIO_DEVICE_IN_AUX_DIGITAL | |
| 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 tuna_audio_device *adev; |
| int ret; |
| |
| if (strcmp(name, AUDIO_HARDWARE_INTERFACE) != 0) |
| return -EINVAL; |
| |
| adev = calloc(1, sizeof(struct tuna_audio_device)); |
| if (!adev) |
| return -ENOMEM; |
| |
| adev->hw_device.common.tag = HARDWARE_DEVICE_TAG; |
| adev->hw_device.common.version = 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->mixer = mixer_open(0); |
| if (!adev->mixer) { |
| free(adev); |
| LOGE("Unable to open the mixer, aborting."); |
| return -EINVAL; |
| } |
| |
| adev->mixer_ctls.mm_dl1 = mixer_get_ctl_by_name(adev->mixer, |
| MIXER_DL1_MIXER_MULTIMEDIA); |
| adev->mixer_ctls.vx_dl1 = mixer_get_ctl_by_name(adev->mixer, |
| MIXER_DL1_MIXER_VOICE); |
| adev->mixer_ctls.mm_dl2 = mixer_get_ctl_by_name(adev->mixer, |
| MIXER_DL2_MIXER_MULTIMEDIA); |
| adev->mixer_ctls.vx_dl2 = mixer_get_ctl_by_name(adev->mixer, |
| MIXER_DL2_MIXER_VOICE); |
| adev->mixer_ctls.dl1_headset = mixer_get_ctl_by_name(adev->mixer, |
| MIXER_DL1_PDM_SWITCH); |
| adev->mixer_ctls.dl1_bt = mixer_get_ctl_by_name(adev->mixer, |
| MIXER_DL1_BT_VX_SWITCH); |
| adev->mixer_ctls.earpiece_enable = mixer_get_ctl_by_name(adev->mixer, |
| MIXER_EARPHONE_ENABLE_SWITCH); |
| adev->mixer_ctls.left_capture = mixer_get_ctl_by_name(adev->mixer, |
| MIXER_ANALOG_LEFT_CAPTURE_ROUTE); |
| adev->mixer_ctls.right_capture = mixer_get_ctl_by_name(adev->mixer, |
| MIXER_ANALOG_RIGHT_CAPTURE_ROUTE); |
| |
| if (!adev->mixer_ctls.mm_dl1 || !adev->mixer_ctls.vx_dl1 || |
| !adev->mixer_ctls.mm_dl2 || !adev->mixer_ctls.vx_dl2 || |
| !adev->mixer_ctls.dl1_headset || !adev->mixer_ctls.dl1_bt || |
| !adev->mixer_ctls.earpiece_enable || !adev->mixer_ctls.left_capture || |
| !adev->mixer_ctls.right_capture) { |
| mixer_close(adev->mixer); |
| free(adev); |
| LOGE("Unable to locate all mixer controls, aborting."); |
| return -EINVAL; |
| } |
| |
| /* Set the default route before the PCM stream is opened */ |
| pthread_mutex_lock(&adev->lock); |
| set_route_by_array(adev->mixer, defaults, 1); |
| adev->mode = AUDIO_MODE_NORMAL; |
| adev->devices = AUDIO_DEVICE_OUT_SPEAKER | AUDIO_DEVICE_IN_BUILTIN_MIC; |
| select_output_device(adev); |
| |
| adev->pcm_modem_dl = NULL; |
| adev->pcm_modem_ul = NULL; |
| adev->voice_volume = 1.0f; |
| |
| /* RIL */ |
| ril_open(&adev->ril); |
| pthread_mutex_unlock(&adev->lock); |
| |
| *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, |
| .version_major = 1, |
| .version_minor = 0, |
| .id = AUDIO_HARDWARE_MODULE_ID, |
| .name = "Tuna audio HW HAL", |
| .author = "The Android Open Source Project", |
| .methods = &hal_module_methods, |
| }, |
| }; |