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

Commit d84fd6a7 authored by Ashish Jain's avatar Ashish Jain Committed by Gerrit - the friendly Code Review server
Browse files

hal: Refactor HDMI backend configuration

-In current design HDMI backend configuration does not validate
incoming configuration against what is supported by the
connected sink ( using edid). This results in an incorrect
configuration of the HDMI backend, sometimes leading to no audio.
-Move HDMI backend configuration to
platform_check_and_set_codec_backend_cfg, this design ensures
that HDMI backend configuration happens with valid and only
supported parameters (sample rate, channels and bit width)
by the connected sink.
-Remove usleep from keep_alive and move to conditional timed wait,
this ensures to break from the keep_alive loop quickly, instead
of waiting for the complete usleep duration.

CRs-Fixed: 1039831
Change-Id: Id46ae76575f1b8169370cee817b505b97af8fe01
parent 02a31b98
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -763,6 +763,7 @@ void audio_extn_set_parameters(struct audio_device *adev,
   audio_extn_pm_set_parameters(parms);
   audio_extn_source_track_set_parameters(adev, parms);
   audio_extn_fbsp_set_parameters(parms);
   audio_extn_keep_alive_set_parameters(adev, parms);
   check_and_set_hdmi_connection_status(parms);
   if (adev->offload_effects_set_parameters != NULL)
       adev->offload_effects_set_parameters(parms);
+60 −10
Original line number Diff line number Diff line
@@ -38,7 +38,7 @@

#define SILENCE_MIXER_PATH "silence-playback hdmi"
#define SILENCE_DEV_ID 32           /* index into machine driver */
#define SILENCE_INTERVAL_US 2000000
#define SILENCE_INTERVAL 2 /*In secs*/

typedef enum {
    STATE_DEINIT = -1,
@@ -52,7 +52,9 @@ typedef enum {

typedef struct {
    pthread_mutex_t lock;
    pthread_mutex_t sleep_lock;
    pthread_cond_t  cond;
    pthread_cond_t  wake_up_cond;
    pthread_t thread;
    state_t state;
    struct listnode cmd_list;
@@ -88,6 +90,8 @@ void audio_extn_keep_alive_init(struct audio_device *adev)
    ka.pcm = NULL;
    pthread_mutex_init(&ka.lock, (const pthread_mutexattr_t *) NULL);
    pthread_cond_init(&ka.cond, (const pthread_condattr_t *) NULL);
    pthread_cond_init(&ka.wake_up_cond, (const pthread_condattr_t *) NULL);
    pthread_mutex_init(&ka.sleep_lock, (const pthread_mutexattr_t *) NULL);
    list_init(&ka.cmd_list);
    if (pthread_create(&ka.thread,  (const pthread_attr_t *) NULL,
                       keep_alive_loop, NULL) < 0) {
@@ -143,6 +147,27 @@ static int open_silence_stream()
    return 0;
}


static int set_mixer_control(struct mixer *mixer,
                             const char * mixer_ctl_name,
                             const char *mixer_val)
{
    struct mixer_ctl *ctl;
    if ((mixer == NULL) || (mixer_ctl_name == NULL) || (mixer_val == NULL)) {
       ALOGE("%s: Invalid input", __func__);
       return -EINVAL;
    }
    ALOGD("setting mixer ctl %s with value %s", mixer_ctl_name, mixer_val);
    ctl = mixer_get_ctl_by_name(mixer, mixer_ctl_name);
    if (!ctl) {
        ALOGE("%s: could not get ctl for mixer cmd - %s",
              __func__, mixer_ctl_name);
        return -EINVAL;
    }

    return mixer_ctl_set_enum_by_string(ctl, mixer_val);
}

/* must be called with adev lock held */
void audio_extn_keep_alive_start()
{
@@ -151,18 +176,20 @@ void audio_extn_keep_alive_start()
    int app_type_cfg[MAX_LENGTH_MIXER_CONTROL_IN_INT], len = 0, rc;
    struct mixer_ctl *ctl;
    int acdb_dev_id, snd_device;
    struct listnode *node;
    struct audio_usecase *usecase;
    int32_t sample_rate = DEFAULT_OUTPUT_SAMPLING_RATE;

    pthread_mutex_lock(&ka.lock);

    if (ka.state == STATE_DEINIT) {
        ALOGE(" %s : Invalid state ",__func__);
        return;
        goto exit;
    }

    if (audio_extn_passthru_is_active()) {
        ALOGE(" %s : Pass through is already active", __func__);
        return;
        goto exit;
    }

    if (ka.state == STATE_ACTIVE) {
@@ -170,6 +197,14 @@ void audio_extn_keep_alive_start()
        goto exit;
    }

    /* Dont start keep_alive if any other PCM session is routed to HDMI*/
    list_for_each(node, &adev->usecase_list) {
         usecase = node_to_item(node, struct audio_usecase, list);
         if (usecase->type == PCM_PLAYBACK &&
                 usecase->devices & AUDIO_DEVICE_OUT_AUX_DIGITAL)
             goto exit;
    }

    ka.done = false;

    /*configure app type */
@@ -202,9 +237,15 @@ void audio_extn_keep_alive_start()
          platform_get_default_app_type(adev->platform),
          acdb_dev_id, sample_rate);
    mixer_ctl_set_array(ctl, app_type_cfg, len);
    /*Configure HDMI Backend with default values, this as well
     *helps reconfigure HDMI backend after passthrough
     */
    set_mixer_control(adev->mixer, "HDMI RX Format", "LPCM");
    set_mixer_control(adev->mixer, "HDMI_RX SampleRate", "KHZ_48");
    set_mixer_control(adev->mixer, "HDMI_RX Channels", "Two");

    /*send calibration*/
    struct audio_usecase *usecase = calloc(1, sizeof(struct audio_usecase));
    usecase = calloc(1, sizeof(struct audio_usecase));
    usecase->type = PCM_PLAYBACK;
    usecase->out_snd_device = SND_DEVICE_OUT_HDMI;

@@ -232,13 +273,13 @@ void audio_extn_keep_alive_stop()

    pthread_mutex_lock(&ka.lock);

    if (ka.state == STATE_DEINIT)
        return;

    if (ka.state == STATE_IDLE)
    if ((ka.state == STATE_DEINIT) || (ka.state == STATE_IDLE))
        goto exit;

    pthread_mutex_lock(&ka.sleep_lock);
    ka.done = true;
    pthread_cond_signal(&ka.wake_up_cond);
    pthread_mutex_unlock(&ka.sleep_lock);
    while (ka.state != STATE_IDLE) {
        pthread_cond_wait(&ka.cond, &ka.lock);
    }
@@ -290,6 +331,7 @@ static void * keep_alive_loop(void * context __unused)
    struct listnode *item;
    uint8_t * silence = NULL;
    int32_t bytes = 0;
    struct timespec ts;

    while (true) {
        pthread_mutex_lock(&ka.lock);
@@ -328,9 +370,17 @@ static void * keep_alive_loop(void * context __unused)
             * Just something to keep the connection alive is sufficient.
             * Hence a short burst of silence periodically.
             */
            usleep(SILENCE_INTERVAL_US);
        }
            pthread_mutex_lock(&ka.sleep_lock);
            clock_gettime(CLOCK_REALTIME, &ts);
            ts.tv_sec += SILENCE_INTERVAL;
            ts.tv_nsec = 0;

            if (!ka.done)
              pthread_cond_timedwait(&ka.wake_up_cond,
                            &ka.sleep_lock, &ts);

            pthread_mutex_unlock(&ka.sleep_lock);
        }
        pthread_mutex_lock(&ka.lock);
        ka.state = STATE_IDLE;
        pthread_cond_signal(&ka.cond);
+8 −5
Original line number Diff line number Diff line
@@ -82,8 +82,14 @@ bool audio_extn_passthru_is_supported_format(audio_format_t format)
 */
bool audio_extn_passthru_should_drop_data(struct stream_out * out)
{

    if (out->usecase != USECASE_AUDIO_PLAYBACK_OFFLOAD) {
    /*Drop data only
     *stream is routed to HDMI and
     *stream has PCM format or
     *if a compress offload (DSP decode) session
     */
    if ((out->devices & AUDIO_DEVICE_OUT_AUX_DIGITAL) &&
        (((out->format & AUDIO_FORMAT_MAIN_MASK) == AUDIO_FORMAT_PCM) ||
        ((out->compr_config.codec != NULL) && (out->compr_config.codec->compr_passthr == LEGACY_PCM)))) {
        if (android_atomic_acquire_load(&compress_passthru_active) > 0) {
            ALOGI("drop data as pass thru is active");
            return true;
@@ -112,9 +118,6 @@ void audio_extn_passthru_on_start(struct stream_out * out)
    ALOGV("inc pass thru count to notify other streams");
    android_atomic_inc(&compress_passthru_active);

    ALOGV("keep_alive_stop");
    audio_extn_keep_alive_stop();

    while (true) {
        /* find max period time among active playback use cases */
        list_for_each(node, &adev->usecase_list) {
+19 −204
Original line number Diff line number Diff line
@@ -892,8 +892,9 @@ static void check_usecases_codec_backend(struct audio_device *adev,
        if (usecase->type != PCM_CAPTURE &&
            usecase != uc_info &&
            (usecase->out_snd_device != snd_device || force_routing) &&
            (usecase->devices & AUDIO_DEVICE_OUT_ALL_CODEC_BACKEND ||
            force_restart_session) &&
            ((usecase->devices & AUDIO_DEVICE_OUT_ALL_CODEC_BACKEND) ||
             (usecase->devices & AUDIO_DEVICE_OUT_AUX_DIGITAL) ||
             (force_restart_session)) &&
            platform_check_backends_match(snd_device, usecase->out_snd_device)) {
                ALOGD("%s:becf: check_usecases (%s) is active on (%s) - disabling ..",
                    __func__, use_case_table[usecase->id],
@@ -1802,193 +1803,6 @@ static int destroy_offload_callback_thread(struct stream_out *out)
    return 0;
}

static bool allow_hdmi_channel_config(struct audio_device *adev,
                                      bool enable_passthru)
{
    struct listnode *node;
    struct audio_usecase *usecase;
    bool ret = true;

    if (enable_passthru && !audio_extn_passthru_is_enabled()) {
        ret = false;
        goto exit;
    }

    if (audio_extn_passthru_is_active()) {
        ALOGI("%s: Compress audio passthrough is active,"
              "no HDMI config change allowed", __func__);
        ret = false;
        goto exit;
    }

    list_for_each(node, &adev->usecase_list) {
        usecase = node_to_item(node, struct audio_usecase, list);
        if (usecase->devices & AUDIO_DEVICE_OUT_AUX_DIGITAL) {
            /*
             * If voice call is already existing, do not proceed further to avoid
             * disabling/enabling both RX and TX devices, CSD calls, etc.
             * Once the voice call done, the HDMI channels can be configured to
             * max channels of remaining use cases.
             */
            if (usecase->id == USECASE_VOICE_CALL) {
                ALOGV("%s: voice call is active, no change in HDMI channels",
                      __func__);
                ret = false;
                break;
            } else if (usecase->id == USECASE_AUDIO_PLAYBACK_MULTI_CH) {
                if (!enable_passthru) {
                    ALOGV("%s: multi channel playback is active, "
                          "no change in HDMI channels", __func__);
                    ret = false;
                    break;
                }
            } else if (is_offload_usecase(usecase->id) &&
                       audio_channel_count_from_out_mask(usecase->stream.out->channel_mask) > 2) {
                if (!enable_passthru) {
                    ALOGD("%s:multi-channel(%x) compress offload playback is active"
                        ", no change in HDMI channels", __func__,
                        usecase->stream.out->channel_mask);
                    ret = false;
                    break;
                }
            }
        }
    }
    ALOGV("allow hdmi config %d", ret);
exit:
    return ret;
}

static int check_and_set_hdmi_config(struct audio_device *adev,
                                     uint32_t channels,
                                     uint32_t sample_rate,
                                     audio_format_t format,
                                     bool enable_passthru)
{
    struct listnode *node;
    struct audio_usecase *usecase;
    int32_t factor = 1;
    bool config = false;

    ALOGV("%s channels %d sample_rate %d format:%x enable_passthru:%d",
         __func__, channels, sample_rate, format, enable_passthru);

    if (channels != adev->cur_hdmi_channels) {
        ALOGV("channel does not match current hdmi channels");
        config = true;
    }

    if (sample_rate != adev->cur_hdmi_sample_rate) {
        ALOGV("sample rate does not match current hdmi sample rate");
        config = true;
    }

    if (format != adev->cur_hdmi_format) {
        ALOGV("format does not match current hdmi format");
        config = true;
    }

    /* TBD - add check for bit width */
    if (!config) {
        ALOGV("No need to config hdmi");
        return 0;
    }

    if (enable_passthru &&
        (format == AUDIO_FORMAT_E_AC3)) {
        ALOGV("factor 4 for E_AC3 passthru");
        factor = 4;
    }

    platform_set_hdmi_config(adev->platform, channels, factor * sample_rate,
                             enable_passthru);
    adev->cur_hdmi_channels = channels;
    adev->cur_hdmi_format = format;
    adev->cur_hdmi_sample_rate = sample_rate;

    /*
     * Deroute all the playback streams routed to HDMI so that
     * the back end is deactivated. Note that backend will not
     * be deactivated if any one stream is connected to it.
     */
    list_for_each(node, &adev->usecase_list) {
        usecase = node_to_item(node, struct audio_usecase, list);
        if (usecase->type == PCM_PLAYBACK &&
                usecase->devices & AUDIO_DEVICE_OUT_AUX_DIGITAL) {
            disable_audio_route(adev, usecase);
        }
    }

    bool was_active = audio_extn_keep_alive_is_active();
    if (was_active)
        audio_extn_keep_alive_stop();

    /*
     * Enable all the streams disabled above. Now the HDMI backend
     * will be activated with new channel configuration
     */
    list_for_each(node, &adev->usecase_list) {
        usecase = node_to_item(node, struct audio_usecase, list);
        if (usecase->type == PCM_PLAYBACK &&
                usecase->devices & AUDIO_DEVICE_OUT_AUX_DIGITAL) {
            enable_audio_route(adev, usecase);
        }
    }

    if (was_active)
        audio_extn_keep_alive_start();

    return 0;
}

/* called with out lock taken */
static int check_and_set_hdmi_backend(struct stream_out *out)
{
    struct audio_device *adev = out->dev;
    int ret;
    bool enable_passthru = false;

    if (!(out->devices & AUDIO_DEVICE_OUT_AUX_DIGITAL))
        return -1;

    ALOGV("%s usecase %s out->format:%x out->bit_width:%d", __func__, use_case_table[out->usecase],out->format,out->bit_width);

    if (is_offload_usecase(out->usecase) &&
        audio_extn_passthru_is_passthrough_stream(out)) {
        enable_passthru = true;
        ALOGV("%s : enable_passthru is set to true", __func__);
    }

    /* Check if change in HDMI channel config is allowed */
    if (!allow_hdmi_channel_config(adev, enable_passthru)) {
        return -EPERM;
    }

    if (out->usecase == USECASE_AUDIO_PLAYBACK_OFFLOAD) {
        uint32_t channels;
        ALOGV("Offload usecase, enable passthru %d", enable_passthru);

        if (enable_passthru) {
            audio_extn_passthru_on_start(out);
            audio_extn_passthru_update_stream_configuration(adev, out);
        }

        /* For pass through case, the backend should be configured as stereo */
        channels = enable_passthru ? DEFAULT_HDMI_OUT_CHANNELS :
                                     out->compr_config.codec->ch_in;

        ret = check_and_set_hdmi_config(adev, channels,
                                        out->sample_rate, out->format,
                                        enable_passthru);
    } else
        ret = check_and_set_hdmi_config(adev, out->config.channels,
                                        out->config.rate,
                                        out->format,
                                        false);
    return ret;
}


static int stop_output_stream(struct stream_out *out)
{
    int ret = 0;
@@ -2029,17 +1843,14 @@ static int stop_output_stream(struct stream_out *out)
        ALOGV("Disable passthrough , reset mixer to pcm");
        /* NO_PASSTHROUGH */
        out->compr_config.codec->compr_passthr = 0;

        audio_extn_passthru_on_stop(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_config(adev, DEFAULT_HDMI_OUT_CHANNELS,
                                  DEFAULT_HDMI_OUT_SAMPLE_RATE,
                                  DEFAULT_HDMI_OUT_FORMAT,
                                  false);
        audio_extn_keep_alive_start();

    ALOGV("%s: exit: status(%d)", __func__, ret);
    return ret;
}
@@ -2081,12 +1892,6 @@ int start_output_stream(struct stream_out *out)
        goto error_config;
    }

    /* This must be called before adding this usecase to the list */
    if (out->devices & AUDIO_DEVICE_OUT_AUX_DIGITAL) {
        /* This call can fail if compress pass thru is already active */
        check_and_set_hdmi_backend(out);
    }

    uc_info->id = out->usecase;
    uc_info->type = PCM_PLAYBACK;
    uc_info->stream.out = out;
@@ -2098,6 +1903,16 @@ int start_output_stream(struct stream_out *out)
    audio_extn_perf_lock_acquire(&adev->perf_lock_handle, 0,
                                 adev->perf_lock_opts,
                                 adev->perf_lock_opts_size);

    if (out->devices & AUDIO_DEVICE_OUT_AUX_DIGITAL) {
        audio_extn_keep_alive_stop();
        if (audio_extn_passthru_is_enabled() &&
            audio_extn_passthru_is_passthrough_stream(out)) {
            audio_extn_passthru_on_start(out);
            audio_extn_passthru_update_stream_configuration(adev, out);
        }
    }

    select_devices(adev, out->usecase);

    ALOGV("%s: Opening PCM device card_id(%d) device_id(%d) format(%#x)",
@@ -2791,9 +2606,10 @@ static ssize_t out_write(struct audio_stream_out *stream, const void *buffer,
    }

    if (audio_extn_passthru_should_drop_data(out)) {
        ALOGD(" %s : Drop data as compress passthrough session is going on", __func__);
        usleep((uint64_t)bytes * 1000000 / audio_stream_out_frame_size(stream) /
                        out_get_sample_rate(&out->stream.common));
        ALOGV(" %s : Drop data as compress passthrough session is going on", __func__);
        if (audio_bytes_per_sample(out->format) != 0)
            out->written += bytes / (out->config.channels * audio_bytes_per_sample(out->format));
        ret = -EIO;
        goto exit;
    }

@@ -3168,7 +2984,6 @@ static int out_resume(struct audio_stream_out* stream)
                if (out->devices & AUDIO_DEVICE_OUT_AUX_DIGITAL) {
                    pthread_mutex_lock(&out->dev->lock);
                    ALOGV("offload resume, check and set hdmi backend again");
                    check_and_set_hdmi_backend(out);
                    pthread_mutex_unlock(&out->dev->lock);
                }
                status = compress_resume(out->compr);
+192 −125

File changed.

Preview size limit exceeded, changes collapsed.

Loading