Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 80a8cfb8 authored by Pradnya Chaphekar's avatar Pradnya Chaphekar Committed by Haynes Mathew George
Browse files

hal: Audio HAL / APM changes for HDMI passthrough

-Add support for HDMI passthrough for DD/DDP contents to HAL and
 APM.
-Add mixer control to set volume on volume module on passthrough
 COPP. The mixer control  takes zero volume for mute and unity
 volume for max volume.
-DDP content requires sample rate to be four times native sample
 rate. HAL sample rate is changed to four times native rate from
 start output stream. During rapid pause/resume offload thread
 calls standby when there is no active track and no activity for
 over a second. On resume start output steam is called and
 sample rate is converted to four times its current value.
 This results in session to be started with invalid sampling
 rate and playback failures.
-Add HDMI passthrough support for JOC format.
 Expose DDP/JOC as pass-through supported format if sink
 supports either DD or DDP. This is to allow support for
 pass-through convert based on edid data.
-Deep buffer music stream does not switch back to HDMI after
 ringtone playback ends. Ringtone, alarm, notification etc are
 played on speaker if HDMI pass-through is enabled. The
 decision is taken based on stream or strategy. The decision
 to change the device from HDMI to speaker is taken based on
 the stream type. When a patricular stream ends use the
 reference count instead of stream type.
-DAP is not turned on when switching from passthrough stream to
 deep buffer stream.DAP bypass call from HAL to DAP HAL expects
 integer pointer. Address of a bool variable is passed into the
 function. Corrupt value is set on driver instead of turn on/off
 causing random failures in DAP on/DAP bypass. Use same data type
 and use enumeration to make the function call readable.

CRs-Fixed: 761339
Change-Id: Ided9439ec5e87233a1fec3ff9f50a1e9ba4cb788
parent 8ec50479
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -167,6 +167,10 @@ ifneq ($(strip $(DOLBY_DDP)),true)
endif
endif

ifeq ($(strip $(AUDIO_FEATURE_ENABLED_HDMI_PASSTHROUGH)),true)
    LOCAL_CFLAGS += -DHDMI_PASSTHROUGH_ENABLED
endif

LOCAL_SHARED_LIBRARIES := \
	liblog \
	libcutils \
+28 −1
Original line number Diff line number Diff line
@@ -270,7 +270,6 @@ void audio_extn_dolby_set_license(struct audio_device *adev);
void audio_extn_dolby_set_endpoint(struct audio_device *adev);
#endif


#if defined(DS1_DOLBY_DDP_ENABLED) || defined(DS2_DOLBY_DAP_ENABLED)
bool audio_extn_is_dolby_format(audio_format_t format);
int audio_extn_dolby_get_snd_codec_id(struct audio_device *adev,
@@ -288,6 +287,34 @@ int audio_extn_dolby_get_snd_codec_id(struct audio_device *adev,
void audio_extn_ddp_set_parameters(struct audio_device *adev,
                                   struct str_parms *parms);
void audio_extn_dolby_send_ddp_endp_params(struct audio_device *adev);

#endif

#ifndef HDMI_PASSTHROUGH_ENABLED
#define audio_extn_dolby_update_passt_formats(adev, out)                   (0)
#define audio_extn_dolby_update_passt_stream_configuration(adev, out)      (0)
#define audio_extn_dolby_is_passt_convert_supported(adev, out)             (0)
#define audio_extn_dolby_is_passt_supported(adev, out)                     (0)
#define audio_extn_dolby_is_passthrough_stream(flags)                      (0)
#define audio_extn_dolby_set_hdmi_config(adev, out)                        (0)
#define audio_extn_dolby_get_passt_buffer_size(info)                       (0)
#define audio_extn_dolby_set_passt_volume(out, mute)                       (0)
#define audio_extn_dolby_set_passt_latency(out, latency)                   (0)
#else
int audio_extn_dolby_update_passt_formats(struct audio_device *adev,
                                          struct stream_out *out);
bool audio_extn_dolby_is_passt_convert_supported(struct audio_device *adev,
                                                 struct stream_out *out);
bool audio_extn_dolby_is_passt_supported(struct audio_device *adev,
                                         struct stream_out *out);
void audio_extn_dolby_update_passt_stream_configuration(struct audio_device *adev,
                                                 struct stream_out *out);
bool audio_extn_dolby_is_passthrough_stream(int flags);
int audio_extn_dolby_set_hdmi_config(struct audio_device *adev,
                                     struct stream_out *out);
int audio_extn_dolby_get_passt_buffer_size(audio_offload_info_t* info);
int audio_extn_dolby_set_passt_volume(struct stream_out *out, int mute);
int audio_extn_dolby_set_passt_latency(struct stream_out *out, int latency);
#endif

#ifndef HFP_ENABLED
+112 −0
Original line number Diff line number Diff line
@@ -408,6 +408,118 @@ bool audio_extn_is_dolby_format(audio_format_t format)
}
#endif /* DS1_DOLBY_DDP_ENABLED || DS2_DOLBY_DAP_ENABLED */

#ifdef HDMI_PASSTHROUGH_ENABLED
int audio_extn_dolby_update_passt_formats(struct audio_device *adev,
                                          struct stream_out *out) {
    int32_t i = 0, ret = -ENOSYS;

    if (platform_is_edid_supported_format(adev->platform, AUDIO_FORMAT_AC3) ||
        platform_is_edid_supported_format(adev->platform, AUDIO_FORMAT_E_AC3)) {
        out->supported_formats[i++] = AUDIO_FORMAT_AC3;
        out->supported_formats[i++] = AUDIO_FORMAT_E_AC3;
        /* Reciever must support JOC and advertise, otherwise JOC is treated as DDP */
        out->supported_formats[i++] = AUDIO_FORMAT_E_AC3_JOC;
        ret = 0;
    }
    ALOGV("%s: ret = %d", __func__, ret);
    return ret;
}

bool audio_extn_dolby_is_passt_convert_supported(struct audio_device *adev,
                                                 struct stream_out *out) {

    bool convert = false;
    switch (out->format) {
    case AUDIO_FORMAT_E_AC3:
    case AUDIO_FORMAT_E_AC3_JOC:
        if (!platform_is_edid_supported_format(adev->platform,
            AUDIO_FORMAT_E_AC3)) {
            ALOGV("%s:PASSTHROUGH_CONVERT supported", __func__);
            convert = true;
        }
        break;
    default:
        ALOGE("%s: PASSTHROUGH_CONVERT not supported for format 0x%x",
              __func__, out->format);
        break;
    }
    ALOGE("%s: convert %d", __func__, convert);
    return convert;
}

bool audio_extn_dolby_is_passt_supported(struct audio_device *adev,
                                         struct stream_out *out) {
    bool passt = false;
    switch (out->format) {
    case AUDIO_FORMAT_E_AC3:
        if (platform_is_edid_supported_format(adev->platform, out->format)) {
            ALOGV("%s:PASSTHROUGH supported for format %x",
                   __func__, out->format);
            passt = true;
        }
        break;
    case AUDIO_FORMAT_AC3:
        if (platform_is_edid_supported_format(adev->platform, AUDIO_FORMAT_AC3)
            || platform_is_edid_supported_format(adev->platform,
            AUDIO_FORMAT_E_AC3)) {
            ALOGV("%s:PASSTHROUGH supported for format %x",
                   __func__, out->format);
            passt = true;
        }
        break;
    case AUDIO_FORMAT_E_AC3_JOC:
         /* Check for DDP capability in edid for JOC contents.*/
         if (platform_is_edid_supported_format(adev->platform,
             AUDIO_FORMAT_E_AC3)) {
             ALOGV("%s:PASSTHROUGH supported for format %x",
                   __func__, out->format);
             passt = true;
         }
    default:
        ALOGV("%s:Passthrough not supported", __func__);
    }
    return passt;
}

void audio_extn_dolby_update_passt_stream_configuration(
        struct audio_device *adev, struct stream_out *out) {
    if (audio_extn_dolby_is_passt_supported(adev, out)) {
        ALOGV("%s:PASSTHROUGH", __func__);
        out->compr_config.codec->compr_passthr = PASSTHROUGH;
    } else if (audio_extn_dolby_is_passt_convert_supported(adev, out)){
        ALOGV("%s:PASSTHROUGH CONVERT", __func__);
        out->compr_config.codec->compr_passthr = PASSTHROUGH_CONVERT;
    } else {
        ALOGV("%s:NO PASSTHROUGH", __func__);
        out->compr_config.codec->compr_passthr = LEGACY_PCM;
    }
}

bool audio_extn_dolby_is_passthrough_stream(int flags) {

    if (flags & AUDIO_OUTPUT_FLAG_COMPRESS_PASSTHROUGH)
        return true;
    return false;
}

int audio_extn_dolby_set_hdmi_config(struct audio_device *adev,
                                                    struct stream_out *out) {
    return platform_set_hdmi_config(out);
}

int audio_extn_dolby_get_passt_buffer_size(audio_offload_info_t* info) {
    return platform_get_compress_passthrough_buffer_size(info);
}

int audio_extn_dolby_set_passt_volume(struct stream_out *out,  int mute) {
    return platform_set_device_params(out, DEVICE_PARAM_MUTE_ID, mute);
}

int audio_extn_dolby_set_passt_latency(struct stream_out *out, int latency) {
    return platform_set_device_params(out, DEVICE_PARAM_LATENCY_ID, latency);
}
#endif /* HDMI_PASSTHROUGH_ENABLED */

#ifdef DS1_DOLBY_DAP_ENABLED
void audio_extn_dolby_set_endpoint(struct audio_device *adev)
{
+11 −3
Original line number Diff line number Diff line
@@ -64,12 +64,14 @@ const struct string_to_enum s_flag_name_to_enum_table[] = {
    STRING_TO_ENUM(AUDIO_OUTPUT_FLAG_DEEP_BUFFER),
    STRING_TO_ENUM(AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD),
    STRING_TO_ENUM(AUDIO_OUTPUT_FLAG_NON_BLOCKING),
    STRING_TO_ENUM(AUDIO_OUTPUT_FLAG_HW_AV_SYNC),
#ifdef INCALL_MUSIC_ENABLED
    STRING_TO_ENUM(AUDIO_OUTPUT_FLAG_INCALL_MUSIC),
#endif
#ifdef COMPRESS_VOIP_ENABLED
    STRING_TO_ENUM(AUDIO_OUTPUT_FLAG_VOIP_RX),
#endif
    STRING_TO_ENUM(AUDIO_OUTPUT_FLAG_COMPRESS_PASSTHROUGH),
};

const struct string_to_enum s_format_name_to_enum_table[] = {
@@ -98,6 +100,7 @@ const struct string_to_enum s_format_name_to_enum_table[] = {
    STRING_TO_ENUM(AUDIO_FORMAT_PCM_16_BIT_OFFLOAD),
    STRING_TO_ENUM(AUDIO_FORMAT_PCM_24_BIT_OFFLOAD),
    STRING_TO_ENUM(AUDIO_FORMAT_FLAC),
    STRING_TO_ENUM(AUDIO_FORMAT_E_AC3_JOC),
#endif
};

@@ -404,6 +407,7 @@ static bool set_output_cfg(struct streams_output_cfg *so_info,
        ss_info = node_to_item(node_i, struct stream_sample_rate, list);
        if ((sample_rate <= ss_info->sample_rate) &&
            (bit_width == so_info->app_type_cfg.bit_width)) {

            app_type_cfg->app_type = so_info->app_type_cfg.app_type;
            app_type_cfg->sample_rate = ss_info->sample_rate;
            app_type_cfg->bit_width = so_info->app_type_cfg.bit_width;
@@ -509,7 +513,7 @@ int audio_extn_utils_send_app_type_cfg(struct audio_usecase *usecase)
        (usecase->id != USECASE_AUDIO_PLAYBACK_LOW_LATENCY) &&
        (usecase->id != USECASE_AUDIO_PLAYBACK_MULTI_CH) &&
        (!is_offload_usecase(usecase->id))) {
        ALOGV("%s: a playback path where app type cfg is not required", __func__);
        ALOGV("%s: a playback path where app type cfg is not required %d", __func__, usecase->id);
        rc = 0;
        goto exit_send_app_type_cfg;
    }
@@ -548,8 +552,12 @@ int audio_extn_utils_send_app_type_cfg(struct audio_usecase *usecase)

    app_type_cfg[len++] = out->app_type_cfg.app_type;
    app_type_cfg[len++] = acdb_dev_id;
    if (((out->format == AUDIO_FORMAT_E_AC3) ||
        (out->format == AUDIO_FORMAT_E_AC3_JOC)) &&
        (out->flags  & AUDIO_OUTPUT_FLAG_COMPRESS_PASSTHROUGH))
        app_type_cfg[len++] = sample_rate * 4;
    else
        app_type_cfg[len++] = sample_rate;

    mixer_ctl_set_array(ctl, app_type_cfg, len);
    ALOGI("%s app_type %d, acdb_dev_id %d, sample_rate %d",
           __func__, out->app_type_cfg.app_type, acdb_dev_id, sample_rate);
+107 −31
Original line number Diff line number Diff line
@@ -1277,8 +1277,9 @@ static bool allow_hdmi_channel_config(struct audio_device *adev)
                break;
            } else if (is_offload_usecase(usecase->id) &&
                       audio_channel_count_from_out_mask(usecase->stream.out->channel_mask) > 2) {
                ALOGD("%s: multi-channel(%x) compress offload playback is active, "
                      "no change in HDMI channels", __func__, usecase->stream.out->channel_mask);
                ALOGD("%s:multi-channel(%x) compress offload playback is active"
                      ", no change in HDMI channels", __func__,
                      usecase->stream.out->channel_mask);
                ret = false;
                break;
            }
@@ -1309,6 +1310,7 @@ static int check_and_set_hdmi_channels(struct audio_device *adev,
        return 0;
    }

    /*TODO: CHECK for passthrough don't set channel map for passthrough*/
    platform_set_hdmi_channels(adev->platform, channels);
    platform_set_edid_channels_configuration(adev->platform, channels);
    adev->cur_hdmi_channels = channels;
@@ -1356,7 +1358,8 @@ static int stop_output_stream(struct stream_out *out)
        return -EINVAL;
    }

    if (is_offload_usecase(out->usecase)) {
    if (is_offload_usecase(out->usecase) &&
        !(audio_extn_dolby_is_passthrough_stream(out->flags))) {
        if (adev->visualizer_stop_output != NULL)
            adev->visualizer_stop_output(out->handle, out->pcm_device_id);
        if (adev->offload_effects_stop_output != NULL)
@@ -1372,6 +1375,15 @@ static int stop_output_stream(struct stream_out *out)
    list_remove(&uc_info->list);
    free(uc_info);

    if (is_offload_usecase(out->usecase) &&
        (out->devices & AUDIO_DEVICE_OUT_AUX_DIGITAL) &&
        (audio_extn_dolby_is_passthrough_stream(out->flags))) {
        ALOGV("Disable passthrough , reset mixer to pcm");
        /* NO_PASSTHROUGH */
        out->compr_config.codec->compr_passthr = 0;
        audio_extn_dolby_set_hdmi_config(adev, out);
        audio_extn_dolby_set_dap_bypass(adev, DAP_STATE_ON);
    }
    /* Must be called after removing the usecase from list */
    if (out->devices & AUDIO_DEVICE_OUT_AUX_DIGITAL)
        check_and_set_hdmi_channels(adev, DEFAULT_HDMI_OUT_CHANNELS);
@@ -1409,7 +1421,7 @@ int start_output_stream(struct stream_out *out)
        ALOGE("%s: Invalid PCM device id(%d) for the usecase(%d)",
              __func__, out->pcm_device_id, out->usecase);
        ret = -EINVAL;
        goto error_config;
        goto error_open;
    }

    uc_info = (struct audio_usecase *)calloc(1, sizeof(struct audio_usecase));
@@ -1425,9 +1437,17 @@ int start_output_stream(struct stream_out *out)
    uc_info->devices = out->devices;
    uc_info->in_snd_device = SND_DEVICE_NONE;
    uc_info->out_snd_device = SND_DEVICE_NONE;

    /* This must be called before adding this usecase to the list */
    if (out->devices & AUDIO_DEVICE_OUT_AUX_DIGITAL) {
        if (is_offload_usecase(out->usecase)) {
            if (audio_extn_dolby_is_passthrough_stream(out->flags)) {
                ret = audio_extn_dolby_set_dap_bypass(adev, DAP_STATE_BYPASS);
                if(ret != 0) {
                    goto error_open;
                }
                audio_extn_dolby_update_passt_stream_configuration(adev, out);
            }
        }
        property_get("audio.use.hdmi.sink.cap", prop_value, NULL);
        if (!strncmp("true", prop_value, 4)) {
            sink_channels = platform_edid_get_max_channels(out->dev->platform);
@@ -1435,13 +1455,17 @@ int start_output_stream(struct stream_out *out)
                   __func__, sink_channels);
            check_and_set_hdmi_channels(adev, sink_channels);
        } else {
            if (is_offload_usecase(out->usecase))
                check_and_set_hdmi_channels(adev, out->compr_config.codec->ch_in);
            else
            if (is_offload_usecase(out->usecase)) {
                unsigned int ch_count =  out->compr_config.codec->ch_in;
                if (audio_extn_dolby_is_passthrough_stream(out->flags))
                    /* backend channel config for passthrough stream is stereo */
                    ch_count = 2;
                check_and_set_hdmi_channels(adev, ch_count);
            } else
                check_and_set_hdmi_channels(adev, out->config.channels);
        }
        audio_extn_dolby_set_hdmi_config(adev, out);
    }

    list_add_tail(&adev->usecase_list, &uc_info->list);

    select_devices(adev, out->usecase);
@@ -1498,12 +1522,13 @@ int start_output_stream(struct stream_out *out)
        if (audio_extn_is_dolby_format(out->format))
            audio_extn_dolby_send_ddp_endp_params(adev);
#endif

        if (!(audio_extn_dolby_is_passthrough_stream(out->flags))) {
            if (adev->visualizer_start_output != NULL)
                adev->visualizer_start_output(out->handle, out->pcm_device_id);
            if (adev->offload_effects_start_output != NULL)
                adev->offload_effects_start_output(out->handle, out->pcm_device_id);
        }
    }
    ALOGV("%s: exit", __func__);
    return 0;
error_open:
@@ -1891,6 +1916,28 @@ static char* out_get_parameters(const struct audio_stream *stream, const char *k
            str = strdup(keys);
        }
    }

    ret = str_parms_get_str(query, AUDIO_PARAMETER_STREAM_SUP_FORMATS, value, sizeof(value));
    if (ret >= 0) {
        value[0] = '\0';
        i = 0;
        first = true;
        while (out->supported_formats[i] != 0) {
            for (j = 0; j < ARRAY_SIZE(out_formats_name_to_enum_table); j++) {
                if (out_formats_name_to_enum_table[j].value == out->supported_formats[i]) {
                    if (!first) {
                        strcat(value, "|");
                    }
                    strlcat(value, out_formats_name_to_enum_table[j].name, sizeof(value));
                    first = false;
                    break;
                }
            }
            i++;
        }
        str_parms_add_str(reply, AUDIO_PARAMETER_STREAM_SUP_FORMATS, value);
        str = str_parms_to_str(reply);
    }
    str_parms_destroy(query);
    str_parms_destroy(reply);
    ALOGV("%s: exit: returns - %s", __func__, str);
@@ -1919,6 +1966,14 @@ static int out_set_volume(struct audio_stream_out *stream, float left,
        out->muted = (left == 0.0f);
        return 0;
    } else if (is_offload_usecase(out->usecase)) {
        if (audio_extn_dolby_is_passthrough_stream(out->flags)) {
            /*
             * Set mute or umute on HDMI passthrough stream.
             * Only take left channel into account.
             * Mute is 0 and unmute 1
             */
            audio_extn_dolby_set_passt_volume(out, (left == 0.0f));
        } else {
            char mixer_ctl_name[128];
            struct audio_device *adev = out->dev;
            struct mixer_ctl *ctl;
@@ -1938,6 +1993,7 @@ static int out_set_volume(struct audio_stream_out *stream, float left,
            mixer_ctl_set_array(ctl, volume, sizeof(volume)/sizeof(volume[0]));
            return 0;
        }
    }

    return -ENOSYS;
}
@@ -2633,6 +2689,18 @@ static int adev_open_output_stream(struct audio_hw_device *dev,
            ret = -EINVAL;
            goto error_open;
        }

        if ((out->devices & AUDIO_DEVICE_OUT_AUX_DIGITAL) &&
            ((audio_extn_dolby_is_passthrough_stream(out->flags)))) {
            ALOGV("read and update_pass through formats");
            ret = audio_extn_dolby_update_passt_formats(adev, out);
            if(ret != 0) {
                goto error_open;
            }
            if(config->offload_info.format == 0)
                config->offload_info.format = out->supported_formats[0];
        }

        if (!is_supported_format(config->offload_info.format) &&
                !audio_extn_is_dolby_format(config->offload_info.format)) {
            ALOGE("%s: Unsupported audio format", __func__);
@@ -2675,6 +2743,9 @@ static int adev_open_output_stream(struct audio_hw_device *dev,
        if (audio_is_offload_pcm(config->offload_info.format)) {
            out->compr_config.fragment_size =
               platform_get_pcm_offload_buffer_size(&config->offload_info);
        } else if (audio_extn_dolby_is_passthrough_stream(out->flags)) {
            out->compr_config.fragment_size =
               audio_extn_dolby_get_passt_buffer_size(&config->offload_info);
        } else {
            out->compr_config.fragment_size =
               platform_get_compress_offload_buffer_size(&config->offload_info);
@@ -2688,6 +2759,8 @@ static int adev_open_output_stream(struct audio_hw_device *dev,
                audio_channel_count_from_out_mask(config->channel_mask);
        out->compr_config.codec->ch_out = out->compr_config.codec->ch_in;
        out->bit_width = PCM_OUTPUT_BIT_WIDTH;
        /*TODO: Do we need to change it for passthrough */
        out->compr_config.codec->format = SND_AUDIOSTREAMFORMAT_RAW;

        if (config->offload_info.format == AUDIO_FORMAT_AAC)
            out->compr_config.codec->format = SND_AUDIOSTREAMFORMAT_RAW;
@@ -2761,6 +2834,9 @@ static int adev_open_output_stream(struct audio_hw_device *dev,

    ALOGV("%s devices %d,flags %x, format %x, out->sample_rate %d, out->bit_width %d",
           __func__, devices, flags, format, out->sample_rate, out->bit_width);
    /* TODO remove this hardcoding and check why width is zero*/
    if (out->bit_width == 0)
        out->bit_width = 16;
    audio_extn_utils_update_stream_app_type_cfg(adev->platform,
                                                &adev->streams_output_cfg_list,
                                                devices, flags, format, out->sample_rate,
Loading