From 3ee55c65a2ac8d018f91d1b8ba48f15272cc146c Mon Sep 17 00:00:00 2001 From: Mingming Yin Date: Mon, 4 Aug 2014 14:23:35 -0700 Subject: [PATCH] hal: Support for FLAC 24 bit format in offload path - Add flac in supported codecs list - Set FLAC codec specific parameters - Set fragment size based on bit width and sampling rate for flac - Configure backend to 24/16 bit based on the current usecases running Change-Id: If013078e277fd3053fba6489345803f8f58bd86d --- hal/Android.mk | 4 + hal/audio_extn/audio_extn.h | 10 ++ hal/audio_hw.c | 63 +++++++++++- hal/audio_hw.h | 3 + hal/msm8916/platform.c | 7 ++ hal/msm8960/platform.c | 9 ++ hal/msm8974/platform.c | 195 ++++++++++++++++++++++++++++++++++++ hal/platform_api.h | 5 + 8 files changed, 291 insertions(+), 5 deletions(-) diff --git a/hal/Android.mk b/hal/Android.mk index 3d6559e9..d6f07fa5 100644 --- a/hal/Android.mk +++ b/hal/Android.mk @@ -137,6 +137,10 @@ ifeq ($(strip $(AUDIO_FEATURE_ENABLED_MULTIPLE_TUNNEL)), true) LOCAL_CFLAGS += -DMULTIPLE_OFFLOAD_ENABLED endif +ifeq ($(strip $(AUDIO_FEATURE_ENABLED_EXTN_FLAC_DECODER)),true) + LOCAL_CFLAGS += -DQTI_FLAC_DECODER +endif + LOCAL_SHARED_LIBRARIES := \ liblog \ libcutils \ diff --git a/hal/audio_extn/audio_extn.h b/hal/audio_extn/audio_extn.h index a8580285..7ce1c3a7 100644 --- a/hal/audio_extn/audio_extn.h +++ b/hal/audio_extn/audio_extn.h @@ -51,6 +51,16 @@ #define AUDIO_DEVICE_IN_FM_RX_A2DP (AUDIO_DEVICE_BIT_IN | 0x10000) #endif +#ifndef QTI_FLAC_DECODER +#define AUDIO_FORMAT_FLAC 0x19000000UL +#define AUDIO_OFFLOAD_CODEC_FLAC_MIN_BLK_SIZE "music_offload_flac_min_blk_size" +#define AUDIO_OFFLOAD_CODEC_FLAC_MAX_BLK_SIZE "music_offload_flac_max_blk_size" +#define AUDIO_OFFLOAD_CODEC_FLAC_MIN_FRAME_SIZE "music_offload_flac_min_frame_size" +#define AUDIO_OFFLOAD_CODEC_FLAC_MAX_FRAME_SIZE "music_offload_flac_max_frame_size" +#define PCM_OUTPUT_BIT_WIDTH (CODEC_BACKEND_DEFAULT_BIT_WIDTH) +#else +#define PCM_OUTPUT_BIT_WIDTH (config->offload_info.bit_width) +#endif #define MAX_LENGTH_MIXER_CONTROL_IN_INT (128) diff --git a/hal/audio_hw.c b/hal/audio_hw.c index 0faf8ddd..87ea0d35 100644 --- a/hal/audio_hw.c +++ b/hal/audio_hw.c @@ -125,7 +125,7 @@ const char * const use_case_table[AUDIO_USECASE_MAX] = { [USECASE_AUDIO_HFP_SCO] = "hfp-sco", [USECASE_AUDIO_HFP_SCO_WB] = "hfp-sco-wb", [USECASE_VOICE_CALL] = "voice-call", - + [USECASE_VOICE2_CALL] = "voice2-call", [USECASE_VOLTE_CALL] = "volte-call", [USECASE_QCHAT_CALL] = "qchat-call", @@ -209,7 +209,8 @@ static bool is_supported_format(audio_format_t format) if (format == AUDIO_FORMAT_MP3 || format == AUDIO_FORMAT_AAC || format == AUDIO_FORMAT_PCM_16_BIT_OFFLOAD || - format == AUDIO_FORMAT_PCM_24_BIT_OFFLOAD) + format == AUDIO_FORMAT_PCM_24_BIT_OFFLOAD || + format == AUDIO_FORMAT_FLAC) return true; return false; @@ -230,6 +231,9 @@ static int get_snd_codec_id(audio_format_t format) case AUDIO_FORMAT_PCM_24_BIT_OFFLOAD: id = SND_AUDIOCODEC_PCM; break; + case AUDIO_FORMAT_FLAC: + id = SND_AUDIOCODEC_FLAC; + break; default: ALOGE("%s: Unsupported audio format :%x", __func__, format); } @@ -447,6 +451,15 @@ static void check_usecases_codec_backend(struct audio_device *adev, * because of the limitation that both the devices cannot be enabled * at the same time as they share the same backend. */ + /* + * This call is to check if we need to force routing for a particular stream + * If there is a backend configuration change for the device when a + * new stream starts, then ADM needs to be closed and re-opened with the new + * configuraion. This call check if we need to re-route all the streams + * associated with the backend. Touch tone + 24 bit playback. + */ + bool force_routing = platform_check_and_set_codec_backend_cfg(adev, uc_info); + /* Disable all the usecases on the shared backend other than the specified usecase */ for (i = 0; i < AUDIO_USECASE_MAX; i++) @@ -456,7 +469,7 @@ static void check_usecases_codec_backend(struct audio_device *adev, usecase = node_to_item(node, struct audio_usecase, list); if (usecase->type != PCM_CAPTURE && usecase != uc_info && - usecase->out_snd_device != snd_device && + (usecase->out_snd_device != snd_device || force_routing) && usecase->devices & AUDIO_DEVICE_OUT_ALL_CODEC_BACKEND) { ALOGV("%s: Usecase (%s) is active on (%s) - disabling ..", __func__, use_case_table[usecase->id], @@ -1501,6 +1514,29 @@ static int parse_compress_metadata(struct stream_out *out, struct str_parms *par out->send_new_metadata = 1; } + if (out->format == AUDIO_FORMAT_FLAC) { + ret = str_parms_get_str(parms, AUDIO_OFFLOAD_CODEC_FLAC_MIN_BLK_SIZE, value, sizeof(value)); + if (ret >= 0) { + out->compr_config.codec->options.flac_dec.min_blk_size = atoi(value); + out->send_new_metadata = 1; + } + ret = str_parms_get_str(parms, AUDIO_OFFLOAD_CODEC_FLAC_MAX_BLK_SIZE, value, sizeof(value)); + if (ret >= 0) { + out->compr_config.codec->options.flac_dec.max_blk_size = atoi(value); + out->send_new_metadata = 1; + } + ret = str_parms_get_str(parms, AUDIO_OFFLOAD_CODEC_FLAC_MIN_FRAME_SIZE, value, sizeof(value)); + if (ret >= 0) { + out->compr_config.codec->options.flac_dec.min_frame_size = atoi(value); + out->send_new_metadata = 1; + } + ret = str_parms_get_str(parms, AUDIO_OFFLOAD_CODEC_FLAC_MAX_FRAME_SIZE, value, sizeof(value)); + if (ret >= 0) { + out->compr_config.codec->options.flac_dec.max_frame_size = atoi(value); + out->send_new_metadata = 1; + } + } + ret = str_parms_get_str(parms, AUDIO_OFFLOAD_CODEC_SAMPLE_RATE, value, sizeof(value)); if(ret >= 0) is_meta_data_params = true; @@ -2335,6 +2371,7 @@ static int adev_open_output_stream(struct audio_hw_device *dev, out->channel_mask = AUDIO_CHANNEL_OUT_STEREO; out->supported_channel_masks[0] = AUDIO_CHANNEL_OUT_STEREO; out->handle = handle; + out->bit_width = CODEC_BACKEND_DEFAULT_BIT_WIDTH; /* Init use case and pcm_config */ if ((out->flags == AUDIO_OUTPUT_FLAG_DIRECT) && @@ -2409,6 +2446,7 @@ static int adev_open_output_stream(struct audio_hw_device *dev, out->stream.resume = out_resume; out->stream.drain = out_drain; out->stream.flush = out_flush; + out->bit_width = CODEC_BACKEND_DEFAULT_BIT_WIDTH; if (audio_extn_is_dolby_format(config->offload_info.format)) out->compr_config.codec->id = @@ -2432,13 +2470,26 @@ static int adev_open_output_stream(struct audio_hw_device *dev, out->compr_config.codec->ch_in = popcount(config->channel_mask); out->compr_config.codec->ch_out = out->compr_config.codec->ch_in; - out->compr_config.codec->format = SND_AUDIOSTREAMFORMAT_RAW; + out->bit_width = PCM_OUTPUT_BIT_WIDTH; + if (config->offload_info.format == AUDIO_FORMAT_AAC) + out->compr_config.codec->format = SND_AUDIOSTREAMFORMAT_RAW; if (config->offload_info.format == AUDIO_FORMAT_PCM_16_BIT_OFFLOAD) out->compr_config.codec->format = SNDRV_PCM_FORMAT_S16_LE; - else if(config->offload_info.format == AUDIO_FORMAT_PCM_24_BIT_OFFLOAD) + if(config->offload_info.format == AUDIO_FORMAT_PCM_24_BIT_OFFLOAD) out->compr_config.codec->format = SNDRV_PCM_FORMAT_S24_LE; + if (out->bit_width == 24) { + out->compr_config.codec->format = SNDRV_PCM_FORMAT_S24_LE; + } + + if (out->bit_width == 24 && !platform_check_24_bit_support()) { + ALOGW("24 bit support is not enabled, using 16 bit backend"); + out->compr_config.codec->format = SNDRV_PCM_FORMAT_S16_LE; + } + + out->compr_config.codec->options.flac_dec.sample_size = out->bit_width; + if (flags & AUDIO_OUTPUT_FLAG_NON_BLOCKING) out->non_blocking = 1; @@ -3005,6 +3056,8 @@ static int adev_open(const hw_module_t *module, const char *name, adev->bluetooth_nrec = true; adev->acdb_settings = TTY_MODE_OFF; /* adev->cur_hdmi_channels = 0; by calloc() */ + adev->cur_codec_backend_samplerate = CODEC_BACKEND_DEFAULT_SAMPLE_RATE; + adev->cur_codec_backend_bit_width = CODEC_BACKEND_DEFAULT_BIT_WIDTH; adev->snd_dev_ref_cnt = calloc(SND_DEVICE_MAX, sizeof(int)); voice_init(adev); list_init(&adev->usecase_list); diff --git a/hal/audio_hw.h b/hal/audio_hw.h index e537377a..f1ceedc5 100644 --- a/hal/audio_hw.h +++ b/hal/audio_hw.h @@ -188,6 +188,7 @@ struct stream_out { void *offload_cookie; struct compr_gapless_mdata gapless_mdata; int send_new_metadata; + unsigned int bit_width; struct audio_device *dev; }; @@ -272,6 +273,8 @@ struct audio_device { bool bt_wb_speech_enabled; int snd_card; + unsigned int cur_codec_backend_samplerate; + unsigned int cur_codec_backend_bit_width; void *platform; unsigned int offload_usecases_state; void *visualizer_lib; diff --git a/hal/msm8916/platform.c b/hal/msm8916/platform.c index b27c8ca4..e372254f 100644 --- a/hal/msm8916/platform.c +++ b/hal/msm8916/platform.c @@ -2041,3 +2041,10 @@ uint32_t platform_get_pcm_offload_buffer_size(audio_offload_info_t* info) return fragment_size; } +bool platform_check_24_bit_support() { + return false; +} + +bool platform_check_and_set_codec_backend_cfg(struct audio_device* adev, struct audio_usecase *usecase) { + return false; +} diff --git a/hal/msm8960/platform.c b/hal/msm8960/platform.c index 29d62157..2400cd8c 100644 --- a/hal/msm8960/platform.c +++ b/hal/msm8960/platform.c @@ -950,3 +950,12 @@ bool platform_listen_usecase_needs_event(audio_usecase_t uc_id) { return false; } + +bool platform_check_24_bit_support() { + return false; +} + +bool platform_check_and_set_codec_backend_cfg(struct audio_device* adev, struct audio_usecase *usecase) { + return false; +} + diff --git a/hal/msm8974/platform.c b/hal/msm8974/platform.c index 9bfb8012..3f621640 100644 --- a/hal/msm8974/platform.c +++ b/hal/msm8974/platform.c @@ -2154,6 +2154,14 @@ uint32_t platform_get_compress_offload_buffer_size(audio_offload_info_t* info) fragment_size = atoi(value) * 1024; } + // For FLAC use max size since it is loss less, and has sampling rates + // upto 192kHZ + if (info != NULL && !info->has_video && + info->format == AUDIO_FORMAT_FLAC) { + fragment_size = MAX_COMPRESS_OFFLOAD_FRAGMENT_SIZE; + ALOGV("FLAC fragment size %d", fragment_size); + } + if (info != NULL && info->has_video && info->is_streaming) { fragment_size = COMPRESS_OFFLOAD_FRAGMENT_SIZE_FOR_AV_STREAMING; ALOGV("%s: offload fragment size reduced for AV streaming to %d", @@ -2213,3 +2221,190 @@ uint32_t platform_get_pcm_offload_buffer_size(audio_offload_info_t* info) return fragment_size; } +bool platform_check_24_bit_support() { + + char value[PROPERTY_VALUE_MAX] = {0}; + property_get("audio.offload.24bit.enable", value, "0"); + if (atoi(value)) { + ALOGW("Property audio.offload.24bit.enable is set"); + return true; + } + return false; +} + +int platform_set_codec_backend_cfg(struct audio_device* adev, + unsigned int bit_width, unsigned int sample_rate) +{ + ALOGV("platform_set_codec_backend_cfg bw %d, sr %d", bit_width, sample_rate); + + int ret = 0; + if (bit_width != adev->cur_codec_backend_bit_width) { + const char * mixer_ctl_name = "SLIM_0_RX Format"; + struct mixer_ctl *ctl; + ctl = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name); + if (!ctl) { + ALOGE("%s: Could not get ctl for mixer command - %s", + __func__, mixer_ctl_name); + return -EINVAL; + } + + if (bit_width == 24) { + mixer_ctl_set_enum_by_string(ctl, "S24_LE"); + } else { + mixer_ctl_set_enum_by_string(ctl, "S16_LE"); + sample_rate = CODEC_BACKEND_DEFAULT_SAMPLE_RATE; + } + adev->cur_codec_backend_bit_width = bit_width; + ALOGE("Backend bit width is set to %d ", bit_width); + } + + if ((adev->cur_codec_backend_bit_width == CODEC_BACKEND_DEFAULT_BIT_WIDTH && + adev->cur_codec_backend_samplerate != CODEC_BACKEND_DEFAULT_SAMPLE_RATE) || + (adev->cur_codec_backend_samplerate < sample_rate)) { + + char *rate_str = NULL; + const char * mixer_ctl_name = "SLIM_0_RX SampleRate"; + struct mixer_ctl *ctl; + + switch (sample_rate) { + case 8000: + rate_str = "KHZ_8"; + break; + case 11025: + rate_str = "KHZ_11_025"; + break; + case 16000: + rate_str = "KHZ_16"; + break; + case 22050: + rate_str = "KHZ_22_05"; + break; + case 32000: + rate_str = "KHZ_32"; + break; + case 44100: + rate_str = "KHZ_44_1"; + break; + case 48000: + rate_str = "KHZ_48"; + break; + case 64000: + rate_str = "KHZ_64"; + break; + case 88200: + rate_str = "KHZ_88_2"; + break; + case 96000: + rate_str = "KHZ_96"; + break; + case 176400: + rate_str = "KHZ_176_4"; + break; + case 192000: + rate_str = "KHZ_192"; + break; + default: + rate_str = "KHZ_48"; + break; + } + + ctl = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name); + if(!ctl) { + ALOGE("%s: Could not get ctl for mixer command - %s", + __func__, mixer_ctl_name); + return -EINVAL; + } + + ALOGV("Set sample rate as rate_str = %s", rate_str); + mixer_ctl_set_enum_by_string(ctl, rate_str); + adev->cur_codec_backend_samplerate = sample_rate; + } + + return ret; +} + +bool platform_check_codec_backend_cfg(struct audio_device* adev, + struct audio_usecase* usecase, + unsigned int* new_bit_width, + unsigned int* new_sample_rate) +{ + bool backend_change = false; + struct listnode *node; + struct stream_out *out = NULL; + + // For voice calls use default configuration + // force routing is not required here, caller will do it anyway + if (adev->mode == AUDIO_MODE_IN_CALL || + adev->mode == AUDIO_MODE_IN_COMMUNICATION) { + ALOGW("%s:Use default bw and sr for voice/voip calls ",__func__); + *new_bit_width = CODEC_BACKEND_DEFAULT_BIT_WIDTH; + *new_sample_rate = CODEC_BACKEND_DEFAULT_SAMPLE_RATE; + backend_change = true; + } + + + if (!backend_change) { + // go through all the offload usecases, and + // find the max bit width and samplerate + list_for_each(node, &adev->usecase_list) { + struct audio_usecase *curr_usecase; + curr_usecase = node_to_item(node, struct audio_usecase, list); + if (curr_usecase->id == USECASE_AUDIO_PLAYBACK_OFFLOAD) { + struct stream_out *out = + (struct stream_out*) curr_usecase->stream.out; + if (out != NULL ) { + ALOGV("Offload playback running bw %d sr %d", + out->bit_width, out->sample_rate); + if (*new_bit_width < out->bit_width) { + *new_bit_width = out->bit_width; + } + if (*new_sample_rate < out->sample_rate) { + *new_sample_rate = out->sample_rate; + } + } + } + } + } + + // Force routing if the expected bitwdith or samplerate + // is not same as current backend comfiguration + if ((*new_bit_width != adev->cur_codec_backend_bit_width) || + (*new_sample_rate != adev->cur_codec_backend_samplerate)) { + backend_change = true; + ALOGW("Codec backend needs to be updated"); + } + + return backend_change; +} + +bool platform_check_and_set_codec_backend_cfg(struct audio_device* adev, struct audio_usecase *usecase) +{ + // check if 24bit configuration is enabled first + if (!platform_check_24_bit_support()) { + ALOGW("24bit not enable, no need to check for backend change"); + return false; + } + + ALOGV("platform_check_and_set_codec_backend_cfg usecase = %d",usecase->id ); + + unsigned int new_bit_width, old_bit_width; + unsigned int new_sample_rate, old_sample_rate; + + new_bit_width = old_bit_width = adev->cur_codec_backend_bit_width; + new_sample_rate = old_sample_rate = adev->cur_codec_backend_samplerate; + + ALOGW("Codec backend bitwidth %d, samplerate %d", old_bit_width, old_sample_rate); + if (platform_check_codec_backend_cfg(adev, usecase, + &new_bit_width, &new_sample_rate)) { + platform_set_codec_backend_cfg(adev, new_bit_width, new_sample_rate); + } + + if (old_bit_width != adev->cur_codec_backend_bit_width || + old_sample_rate != adev->cur_codec_backend_samplerate) { + ALOGW("New codec backend bit width %d, sample rate %d", + adev->cur_codec_backend_bit_width, adev->cur_codec_backend_samplerate); + return true; + } + + return false; +} diff --git a/hal/platform_api.h b/hal/platform_api.h index 6c640730..e09b070f 100644 --- a/hal/platform_api.h +++ b/hal/platform_api.h @@ -21,6 +21,9 @@ #define AUDIO_PLATFORM_API_H #include +#define CODEC_BACKEND_DEFAULT_BIT_WIDTH 16 +#define CODEC_BACKEND_DEFAULT_SAMPLE_RATE 48000 + void *platform_init(struct audio_device *adev); void platform_deinit(void *platform); const char *platform_get_snd_device_name(snd_device_t snd_device); @@ -80,4 +83,6 @@ struct audio_offload_info_t; uint32_t platform_get_compress_offload_buffer_size(audio_offload_info_t* info); uint32_t platform_get_pcm_offload_buffer_size(audio_offload_info_t* info); +bool platform_check_and_set_codec_backend_cfg(struct audio_device* adev, struct audio_usecase *usecase); +bool platform_check_24_bit_support(); #endif // AUDIO_PLATFORM_API_H