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

Commit 1b9e83af authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

Merge "hal: Enable USB Service Interval Config" into audio-hal.lnx.5.0

parents 28bf01fb 5fd0b552
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -401,6 +401,10 @@ ifeq ($(strip $(AUDIO_FEATURE_ENABLED_INSTANCE_ID)), true)
    LOCAL_CFLAGS += -DINSTANCE_ID_ENABLED
endif

ifeq ($(strip $(AUDIO_FEATURE_ENABLED_USB_BURST_MODE)), true)
    LOCAL_CFLAGS += -DUSB_BURST_MODE_ENABLED
endif

LOCAL_CFLAGS += -Wall -Werror

LOCAL_COPY_HEADERS_TO   := mm-audio

hal/audio_extn/audio_extn.h

100755 → 100644
+27 −0
Original line number Diff line number Diff line
@@ -222,6 +222,7 @@ int32_t audio_extn_get_afe_proxy_channel_count();
#define audio_extn_usb_get_sup_sample_rates(t, s, l)                   (0)
#define audio_extn_usb_is_tunnel_supported()                           (0)
#define audio_extn_usb_alive(adev)                                     (false)
#undef USB_BURST_MODE_ENABLED
#else
void audio_extn_usb_init(void *adev);
void audio_extn_usb_deinit();
@@ -242,6 +243,32 @@ bool audio_extn_usb_is_tunnel_supported();
bool audio_extn_usb_alive(int card);
#endif

#ifndef USB_BURST_MODE_ENABLED
#define audio_extn_usb_find_service_interval(m, p)                     (0)
#define audio_extn_usb_altset_for_service_interval(p, si, bw, sr, ch)  (-1)
#define audio_extn_usb_set_service_interval(p, si, recfg)              (-1)
#define audio_extn_usb_get_service_interval(p, si)                     (-1)
#define audio_extn_usb_check_and_set_svc_int(uc,ss)                    (0)
#define audio_extn_usb_is_reconfig_req()                               (0)
#define audio_extn_usb_set_reconfig(isreq)                             (0)
#else
unsigned long audio_extn_usb_find_service_interval(bool min, bool playback);
int audio_extn_usb_altset_for_service_interval(bool is_playback,
                                               unsigned long service_interval,
                                               uint32_t *bit_width,
                                               uint32_t *sample_rate,
                                               uint32_t *channel_count);
int audio_extn_usb_set_service_interval(bool playback,
                                        unsigned long service_interval,
                                        bool *reconfig);
int audio_extn_usb_get_service_interval(bool playback,
                                        unsigned long *service_interval);
int audio_extn_usb_check_and_set_svc_int(struct audio_usecase *uc_info,
                                         bool starting_output_stream);
bool audio_extn_usb_is_reconfig_req();
void audio_extn_usb_set_reconfig(bool is_required);
#endif

#ifndef SPLIT_A2DP_ENABLED
#define audio_extn_a2dp_init(adev)                       (0)
#define audio_extn_a2dp_start_playback()                 (0)
+284 −5
Original line number Diff line number Diff line
@@ -48,6 +48,7 @@
#define CHANNEL_NUMBER_STR      "Channels: "
#define PLAYBACK_PROFILE_STR    "Playback:"
#define CAPTURE_PROFILE_STR     "Capture:"
#define DATA_PACKET_INTERVAL_STR "Data packet interval:"
#define USB_SIDETONE_GAIN_STR   "usb_sidetone_gain"
#define ABS_SUB(A, B) (((A) > (B)) ? ((A) - (B)):((B) - (A)))
#define SAMPLE_RATE_8000          8000
@@ -60,13 +61,15 @@ static uint32_t supported_sample_rates_mask[2];

#define  MAX_SAMPLE_RATE_SIZE  sizeof(supported_sample_rates)/sizeof(supported_sample_rates[0])

#define DEFAULT_SERVICE_INTERVAL_US    0

#define _MAX(x, y) (((x) >= (y)) ? (x) : (y))
#define _MIN(x, y) (((x) <= (y)) ? (x) : (y))

enum usb_usecase_type{
typedef enum usb_usecase_type{
    USB_PLAYBACK = 0,
    USB_CAPTURE,
};
} usb_usecase_type_t;

enum {
    USB_SIDETONE_ENABLE_INDEX = 0,
@@ -80,6 +83,8 @@ struct usb_device_config {
    unsigned int channels;
    unsigned int rate_size;
    unsigned int rates[MAX_SAMPLE_RATE_SIZE];
    unsigned long service_interval_us;
    usb_usecase_type_t type;
};

struct usb_card_config {
@@ -99,6 +104,7 @@ struct usb_module {
    struct audio_device *adev;
    int sidetone_gain;
    bool is_capture_supported;
    bool usb_reconfig;
};

static struct usb_module *usbmod = NULL;
@@ -318,6 +324,43 @@ static int usb_get_sample_rates(int type, char *rates_str,
    return 0;
}

static int usb_get_service_interval(const char *interval_str_start,
                                    struct usb_device_config *usb_device_info)
{
    unsigned long interval = 0;
    char time_unit[8] = {0};
    int multiplier = 0;

    char *eol = strchr(interval_str_start, '\n');
    if (!eol) {
        ALOGE("%s: No EOL found", __func__);
        return -1;
    }
    char *tmp = (char *)calloc(1, eol-interval_str_start+1);
    if (!tmp) {
        ALOGE("%s: failed to allocate tmp", __func__);
        return -1;
    }
    memcpy(tmp, interval_str_start, eol-interval_str_start);
    sscanf(tmp, "%lu %2s", &interval, &time_unit[0]);
    if (!strcmp(time_unit, "us")) {
        multiplier = 1;
    } else if (!strcmp(time_unit, "ms")) {
        multiplier = 1000;
    } else if (!strcmp(time_unit, "s")) {
        multiplier = 1000000;
    } else {
        ALOGE("%s: unknown time_unit %s, assume default", __func__, time_unit);
        interval = DEFAULT_SERVICE_INTERVAL_US;
        multiplier = 1;
    }
    interval *= multiplier;
    ALOGD("%s: set service_interval_us %lu", __func__, interval);
    usb_device_info->service_interval_us = interval;
    free(tmp);
    return 0;
}

static int usb_get_capability(int type,
                              struct usb_card_config *usb_card_info,
                              int card)
@@ -333,6 +376,7 @@ static int usb_get_capability(int type,
    char *target = NULL;
    char *read_buf = NULL;
    char *rates_str = NULL;
    char *interval_str_start = NULL;
    char path[128];
    int ret = 0;
    char *bit_width_str = NULL;
@@ -402,6 +446,7 @@ static int usb_get_capability(int type,
            ret = -ENOMEM;
            break;
        }
        usb_device_info->type = type;
        /* Bit bit_width parsing */
        bit_width_start = strstr(str_start, "Format: ");
        if (bit_width_start == NULL) {
@@ -482,6 +527,19 @@ static int usb_get_capability(int type,
            free(usb_device_info);
            continue;
        }
        // Data packet interval is an optional field.
        // Assume 0ms interval if this cannot be read
        // LPASS USB and HLOS USB will figure out the default to use
        usb_device_info->service_interval_us = DEFAULT_SERVICE_INTERVAL_US;
        interval_str_start = strstr(str_start, DATA_PACKET_INTERVAL_STR);
        if (interval_str_start != NULL) {
            interval_str_start += strlen(DATA_PACKET_INTERVAL_STR);
            ret = usb_get_service_interval(interval_str_start, usb_device_info);
            if (ret < 0) {
                ALOGE("%s: error unable to get service interval, assume default",
                      __func__);
            }
        }
        /* Add to list if every field is valid */
        list_add_tail(&usb_card_info->usb_device_conf_list,
                      &usb_device_info->list);
@@ -750,7 +808,9 @@ static bool usb_get_best_match_for_sample_rate(
                            unsigned int bit_width,
                            unsigned int ch,
                            unsigned int stream_sample_rate,
                            unsigned int *sr)
                            unsigned int *sr,
                            unsigned int service_interval,
                            bool do_service_interval_check)
{
    struct listnode *node_i;
    struct usb_device_config *dev_info;
@@ -768,7 +828,11 @@ static bool usb_get_best_match_for_sample_rate(
                 "%s: USB ch(%d)bw(%d), stm ch(%d)bw(%d)sr(%d), candidate(%d)",
                 __func__, dev_info->channels, dev_info->bit_width,
                 ch, bit_width, stream_sample_rate, candidate);
        if ((dev_info->bit_width != bit_width) || dev_info->channels != ch)

        if ((dev_info->bit_width != bit_width) ||
            (dev_info->channels != ch) ||
            (do_service_interval_check && (dev_info->service_interval_us !=
                                           service_interval)))
            continue;

        candidate = 0;
@@ -837,7 +901,9 @@ static bool usb_audio_backend_apply_policy(struct listnode *dev_list,
                                       *bit_width,
                                       *ch,
                                       *sample_rate,
                                       sample_rate);
                                       sample_rate,
                                       0 /*service int*/,
                                       false /*do service int check*/);
exit:
    ALOGV("%s: Updated sample rate per profile: bit-width(%d) rate(%d) chs(%d)",
           __func__, *bit_width, *sample_rate, *ch);
@@ -1152,6 +1218,218 @@ bool audio_extn_usb_alive(int card) {
    return access(path, F_OK) == 0;
}

unsigned long audio_extn_usb_find_service_interval(bool min,
                                                   bool playback) {
    struct usb_card_config *card_info = NULL;
    struct usb_device_config *dev_info = NULL;
    struct listnode *node_i = NULL;
    struct listnode *node_j = NULL;
    unsigned long interval_us = min ? UINT_MAX : 0;
    list_for_each(node_i, &usbmod->usb_card_conf_list) {
        card_info = node_to_item(node_i, struct usb_card_config, list);
        list_for_each(node_j, &card_info->usb_device_conf_list) {
            dev_info = node_to_item(node_j, struct usb_device_config, list);
            bool match = (playback && (dev_info->type == USB_PLAYBACK)) ||
                    (!playback && (dev_info->type == USB_CAPTURE));
            if (match) {
                interval_us = min ?
                        _MIN(interval_us, dev_info->service_interval_us) :
                        _MAX(interval_us, dev_info->service_interval_us);
            }
        }
        break;
    }
    return interval_us;
}

int audio_extn_usb_altset_for_service_interval(bool playback,
                                               unsigned long service_interval,
                                               uint32_t *bit_width,
                                               uint32_t *sample_rate,
                                               uint32_t *channels)
{
    struct usb_card_config *card_info = NULL;
    struct usb_device_config *dev_info = NULL;;
    struct listnode *node_i = NULL;;
    struct listnode *node_j = NULL;;
    uint32_t bw = 0;
    uint32_t ch = 0;
    uint32_t sr = 0;

    if (service_interval == 0)
        return 0;
        /* not a valid service interval to search for */

#define FIND_BEST_MATCH(local_var, field, cond)                                      \
    list_for_each(node_i, &usbmod->usb_card_conf_list) {                             \
            /* Currently only apply the first playback sound card configuration */   \
            card_info = node_to_item(node_i, struct usb_card_config, list);          \
            list_for_each(node_j, &card_info->usb_device_conf_list) {                \
                    dev_info = node_to_item(node_j, struct usb_device_config, list); \
                    bool match = (playback && (dev_info->type == USB_PLAYBACK)) ||   \
                            (!playback && (dev_info->type == USB_CAPTURE));          \
                    if (match && (cond)) {                                           \
                        local_var = _MAX(local_var, dev_info->field);                \
                    }                                                                \
            }                                                                        \
            break;                                                                   \
    }

    FIND_BEST_MATCH(bw, bit_width, dev_info->service_interval_us == service_interval);
    FIND_BEST_MATCH(ch, channels, \
                    dev_info->service_interval_us == service_interval && \
                    dev_info->bit_width == bw);
    list_for_each(node_i, &usbmod->usb_card_conf_list) {
        /* Currently only apply the first playback sound card configuration */
        card_info = node_to_item(node_i, struct usb_card_config, list);
        if ((playback && usb_output_device(card_info->usb_device_type)) ||
            (!playback && usb_input_device(card_info->usb_device_type))) {
            usb_get_best_match_for_sample_rate(&card_info->usb_device_conf_list,
                                               bw, ch, sr, &sr,
                                               service_interval,
                                               true);
        }
        break;
    }

#define SET_OR_RETURN_ON_ERROR(arg, local_var, cond) \
    if (local_var != (cond)) arg = local_var; else return -1;

    SET_OR_RETURN_ON_ERROR(*bit_width, bw, 0);
    SET_OR_RETURN_ON_ERROR(*sample_rate, sr, 0);
    SET_OR_RETURN_ON_ERROR(*channels, ch, 0);
    return 0;
#undef FIND_BEST_MATCH
#undef SET_OR_RETURN_ON_ERROR
}

int audio_extn_usb_get_service_interval(bool playback,
                                        unsigned long *service_interval)
{
    const char *ctl_name = "USB_AUDIO_RX service_interval";
    struct mixer_ctl *ctl = mixer_get_ctl_by_name(usbmod->adev->mixer,
                                                  ctl_name);

    if (!playback) {
        ALOGE("%s not valid for capture", __func__);
        return -1;
    }

    if (!ctl) {
        ALOGV("%s: could not get mixer %s", __func__, ctl_name);
        return -1;
    }

    *service_interval = mixer_ctl_get_value(ctl, 0);
    return 0;
}

int audio_extn_usb_set_service_interval(bool playback,
                                        unsigned long service_interval,
                                        bool *reconfig)
{
    *reconfig = false;
    unsigned long current_service_interval = 0;
    const char *ctl_name = "USB_AUDIO_RX service_interval";
    struct mixer_ctl *ctl = mixer_get_ctl_by_name(usbmod->adev->mixer,
                                                  ctl_name);

    if (!playback) {
        ALOGE("%s not valid for capture", __func__);
        return -1;
    }

    if (!ctl) {
        ALOGV("%s: could not get mixer %s", __func__, ctl_name);
        return -1;
    }

    if (audio_extn_usb_get_service_interval(playback,
                                            &current_service_interval) != 0) {
        ALOGE("%s Unable to get current service interval", __func__);
        return -1;
    }

    if (current_service_interval != service_interval) {
        mixer_ctl_set_value(ctl, 0, service_interval);
        *reconfig = usbmod->usb_reconfig = true;
    }
    else
        *reconfig = usbmod->usb_reconfig = false;
    return 0;
}

int audio_extn_usb_check_and_set_svc_int(struct audio_usecase *uc_info,
                                         bool starting_output_stream)
{
    struct listnode *node = NULL;
    struct audio_usecase *usecase = uc_info;
    bool reconfig = false;
    bool burst_mode = true;
    unsigned long service_interval = 0;
    struct audio_device *adev = usbmod->adev;

    ALOGV("%s: enter:", __func__);

    if ((starting_output_stream == true &&
        ((uc_info->id == USECASE_AUDIO_PLAYBACK_MMAP) ||
        (uc_info->id == USECASE_AUDIO_PLAYBACK_ULL))) ||
        (voice_is_call_state_active(usbmod->adev))) {
        burst_mode = false;
    } else {
    /* set if the valid usecase do not already exist */
        list_for_each(node, &adev->usecase_list) {
            usecase = node_to_item(node, struct audio_usecase, list);
            if (usecase->type == PCM_PLAYBACK &&
                audio_is_usb_out_device(usecase->devices & AUDIO_DEVICE_OUT_ALL_USB )) {
                switch (usecase->id) {
                    case USECASE_AUDIO_PLAYBACK_MMAP:
                    case USECASE_AUDIO_PLAYBACK_ULL:
                    {
                        if (uc_info != usecase) {
                            //another ULL stream exists
                            ALOGV("%s: another ULL Stream in active use-case list burst mode = false.", __func__);
                            burst_mode = false;
                        } else {
                            ALOGV("%s:current ULL uc is the same as incoming uc_info \
                                   which means we are stopping the output stream, \
                                   we don't want to set burst mode to false", __func__);
                        }
                        break;
                    }
                    default:
                        break;
                }
            }
        }
    }

    ALOGV("%s: burst mode(%d).", __func__,burst_mode);

    service_interval =
            audio_extn_usb_find_service_interval(!burst_mode, true /*playback*/);

    if (service_interval != 0)
        audio_extn_usb_set_service_interval(true /*playback*/,
                                            service_interval,
                                            &reconfig);

    /* no change or not supported or no active usecases */
    if (reconfig)
        return -1;
    return 0;
}

bool audio_extn_usb_is_reconfig_req()
{
    return usbmod->usb_reconfig;
}

void audio_extn_usb_set_reconfig(bool is_required)
{
    usbmod->usb_reconfig = is_required;
}

void audio_extn_usb_init(void *adev)
{
    if (usbmod == NULL) {
@@ -1165,6 +1443,7 @@ void audio_extn_usb_init(void *adev)
    usbmod->adev = (struct audio_device*)adev;
    usbmod->sidetone_gain = usb_sidetone_gain;
    usbmod->is_capture_supported = false;
    usbmod->usb_reconfig = false;
exit:
    return;
}
+50 −3
Original line number Diff line number Diff line
@@ -2899,9 +2899,18 @@ static int stop_output_stream(struct stream_out *out)
                                                adev->dsp_bit_width_enforce_mode,
                                                false);
    }
    if (audio_is_usb_out_device(out->devices & AUDIO_DEVICE_OUT_ALL_USB)) {
        ret = audio_extn_usb_check_and_set_svc_int(uc_info,
                                                   false);

        if (ret != 0)
            check_usecases_codec_backend(adev, uc_info, uc_info->out_snd_device);
            /* default service interval was successfully updated,
            reopen USB backend with new service interval */
        ret = 0;
    }

    list_remove(&uc_info->list);
    free(uc_info);
    out->started = 0;
    if (is_offload_usecase(out->usecase) &&
        (audio_extn_passthru_is_passthrough_stream(out))) {
@@ -2922,6 +2931,7 @@ static int stop_output_stream(struct stream_out *out)
            ALOGE("%s: audio_extn_ip_hdlr_intf_close failed %d",__func__, ret);
    }

    free(uc_info);
    ALOGV("%s: exit: status(%d)", __func__, ret);
    return ret;
}
@@ -3001,6 +3011,14 @@ 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 (audio_is_usb_out_device(out->devices & AUDIO_DEVICE_OUT_ALL_USB)) {
       audio_extn_usb_check_and_set_svc_int(uc_info, true);
       /* USB backend is not reopened immediately.
       This is eventually done as part of select_devices */
    }

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

    audio_extn_perf_lock_acquire(&adev->perf_lock_handle, 0,
@@ -3625,6 +3643,8 @@ static int out_set_parameters(struct audio_stream *stream, const char *kvpairs)
    char value[32];
    int ret = 0, val = 0, err;
    bool bypass_a2dp = false;
    bool reconfig = false;
    unsigned long service_interval = 0;

    ALOGD("%s: enter: usecase(%d: %s) kvpairs: %s",
          __func__, out->usecase, use_case_table[out->usecase], kvpairs);
@@ -3727,6 +3747,13 @@ static int out_set_parameters(struct audio_stream *stream, const char *kvpairs)
                if (!voice_is_call_state_active(adev)) {
                    if (adev->mode == AUDIO_MODE_IN_CALL) {
                        adev->current_call_output = out;
                        if (audio_is_usb_out_device(out->devices & AUDIO_DEVICE_OUT_ALL_USB)) {
                            service_interval = audio_extn_usb_find_service_interval(true, true /*playback*/);
                            audio_extn_usb_set_service_interval(true /*playback*/,
                                                                service_interval,
                                                                &reconfig);
                            ALOGD("%s, svc_int(%ld),reconfig(%d)",__func__,service_interval, reconfig);
                         }
                         ret = voice_start_call(adev);
                    }
                } else {
@@ -6560,12 +6587,32 @@ static int adev_get_master_mute(struct audio_hw_device *dev __unused,
static int adev_set_mode(struct audio_hw_device *dev, audio_mode_t mode)
{
    struct audio_device *adev = (struct audio_device *)dev;

    struct listnode *node;
    struct audio_usecase *usecase = NULL;
    int ret = 0;
    pthread_mutex_lock(&adev->lock);
    if (adev->mode != mode) {
        ALOGD("%s: mode %d\n", __func__, mode);
        adev->mode = mode;
        if ((mode == AUDIO_MODE_NORMAL) && voice_is_in_call(adev)) {
            list_for_each(node, &adev->usecase_list) {
                usecase = node_to_item(node, struct audio_usecase, list);
                if (usecase->type == VOICE_CALL)
                    break;
            }
            if (usecase &&
                audio_is_usb_out_device(usecase->out_snd_device & AUDIO_DEVICE_OUT_ALL_USB)) {
                ret = audio_extn_usb_check_and_set_svc_int(usecase,
                                                           true);
                if (ret != 0) {
                    /* default service interval was successfully updated,
                       reopen USB backend with new service interval */
                    check_usecases_codec_backend(adev,
                                                 usecase,
                                                 usecase->out_snd_device);
                }
            }

            voice_stop_call(adev);
            platform_set_gsm_mode(adev->platform, false);
            adev->current_call_output = NULL;
+24 −2
Original line number Diff line number Diff line
@@ -2266,7 +2266,6 @@ void *platform_init(struct audio_device *adev)
            ALOGD("ACDB initialization failed");
        }
    }

    /* init keep-alive for compress passthru */
    audio_extn_keep_alive_init(adev);
#ifdef DYNAMIC_LOG_ENABLED
@@ -5952,6 +5951,8 @@ static bool platform_check_codec_backend_cfg(struct audio_device* adev,
    unsigned int bit_width;
    unsigned int sample_rate;
    unsigned int channels;
    unsigned long service_interval = 0;
    bool service_interval_update = false;
    bool passthrough_enabled = false;
    bool voice_call_active = false;
    int backend_idx = DEFAULT_CODEC_BACKEND;
@@ -6100,6 +6101,27 @@ static bool platform_check_codec_backend_cfg(struct audio_device* adev,
        audio_extn_usb_is_config_supported(&bit_width, &sample_rate, &channels, true);
        ALOGV("%s: USB BE configured as bit_width(%d)sample_rate(%d)channels(%d)",
                   __func__, bit_width, sample_rate, channels);

        if (audio_extn_usb_get_service_interval(true,
                                                &service_interval) == 0) {
            /* overwrite with best altset for this service interval */
            int ret =
                    audio_extn_usb_altset_for_service_interval(true /*playback*/,
                                                               service_interval,
                                                               &bit_width,
                                                               &sample_rate,
                                                               &channels);
            ALOGD("%s: Override USB BE configured as bit_width(%d)sample_rate(%d)channels(%d)SI(%lu)",
           __func__, bit_width, sample_rate, channels, service_interval);
            if (ret < 0) {
                ALOGW("Failed to find altset for service interval %lu, skip reconfig",
                      service_interval);
                return false;
            }
            service_interval_update = audio_extn_usb_is_reconfig_req();
            audio_extn_usb_set_reconfig(false);
        }

        if (channels != my_data->current_backend_cfg[backend_idx].channels)
            channels_updated = true;
    }
@@ -6138,7 +6160,7 @@ static bool platform_check_codec_backend_cfg(struct audio_device* adev,
    // is not same as current backend comfiguration
    if ((bit_width != my_data->current_backend_cfg[backend_idx].bit_width) ||
        (sample_rate != my_data->current_backend_cfg[backend_idx].sample_rate) ||
         passthrough_enabled || channels_updated) {
         passthrough_enabled || channels_updated || service_interval_update ) {
        backend_cfg->bit_width = bit_width;
        backend_cfg->sample_rate = sample_rate;
        backend_cfg->channels = channels;