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

Commit 2d809e0d authored by Haynes Mathew George's avatar Haynes Mathew George Committed by Eric Laurent
Browse files

audio: derive sound device for concurrent playback usecases

When a new playback usecase has to be started, existing playback
usecases might have to be re-routed (or not) to the new device
based on usecase requirements or h/w limitations
(e.g headphones and speaker sharing the same backend).

This change addresses this requirement by deriving the
new device based on pre-defined cases.

Bug: 31671778
Change-Id: Ic0fd4e8d2c1119e7198dc5bb5e5a51817f0110c1
parent 7401c7cf
Loading
Loading
Loading
Loading
+120 −5
Original line number Diff line number Diff line
@@ -619,7 +619,9 @@ int enable_snd_device(struct audio_device *adev,
            ALOGE("%s: spkr_start_processing failed", __func__);
            goto on_error;
        }
    } else if (platform_can_split_snd_device(snd_device, &num_devices, new_snd_devices)) {
    } else if (platform_can_split_snd_device(snd_device,
                                             &num_devices,
                                             new_snd_devices) == 0) {
        for (i = 0; i < num_devices; i++) {
            enable_snd_device(adev, new_snd_devices[i]);
        }
@@ -663,7 +665,9 @@ int disable_snd_device(struct audio_device *adev,
            snd_device == SND_DEVICE_OUT_VOICE_SPEAKER) &&
            audio_extn_spkr_prot_is_enabled()) {
            audio_extn_spkr_prot_stop_processing(snd_device);
        } else if (platform_can_split_snd_device(snd_device, &num_devices, new_snd_devices)) {
        } else if (platform_can_split_snd_device(snd_device,
                                                 &num_devices,
                                                 new_snd_devices) == 0) {
            for (i = 0; i < num_devices; i++) {
                disable_snd_device(adev, new_snd_devices[i]);
            }
@@ -685,6 +689,114 @@ int disable_snd_device(struct audio_device *adev,
    return 0;
}

/*
  legend:
  uc - existing usecase
  new_uc - new usecase
  d1, d11, d2 - SND_DEVICE enums
  a1, a2 - corresponding ANDROID device enums
  B, B1, B2 - backend strings

case 1
  uc->dev  d1 (a1)               B1
  new_uc->dev d1 (a1), d2 (a2)   B1, B2

  resolution: disable and enable uc->dev on d1

case 2
  uc->dev d1 (a1)        B1
  new_uc->dev d11 (a1)   B1

  resolution: need to switch uc since d1 and d11 are related
  (e.g. speaker and voice-speaker)
  use ANDROID_DEVICE_OUT enums to match devices since SND_DEVICE enums may vary

case 3
  uc->dev d1 (a1)        B1
  new_uc->dev d2 (a2)    B2

  resolution: no need to switch uc

case 4
  uc->dev d1 (a1)      B
  new_uc->dev d2 (a2)  B

  resolution: disable enable uc-dev on d2 since backends match
  we cannot enable two streams on two different devices if they
  share the same backend. e.g. if offload is on speaker device using
  QUAD_MI2S backend and a low-latency stream is started on voice-handset
  using the same backend, offload must also be switched to voice-handset.

case 5
  uc->dev  d1 (a1)                  B
  new_uc->dev d1 (a1), d2 (a2)      B

  resolution: disable enable uc-dev on d2 since backends match
  we cannot enable two streams on two different devices if they
  share the same backend.

case 6
  uc->dev  d1 a1    B1
  new_uc->dev d2 a1 B2

  resolution: no need to switch

case 7

  uc->dev d1 (a1), d2 (a2)       B1, B2
  new_uc->dev d1                 B1

  resolution: no need to switch

*/
static snd_device_t derive_playback_snd_device(struct audio_usecase *uc,
                                               struct audio_usecase *new_uc,
                                               snd_device_t new_snd_device)
{
    audio_devices_t a1 = uc->stream.out->devices;
    audio_devices_t a2 = new_uc->stream.out->devices;

    snd_device_t d1 = uc->out_snd_device;
    snd_device_t d2 = new_snd_device;

    // Treat as a special case when a1 and a2 are not disjoint
    if ((a1 != a2) && (a1 & a2)) {
        snd_device_t d3[2];
        int num_devices = 0;
        int ret = platform_can_split_snd_device(popcount(a1) > 1 ? d1 : d2,
                                                &num_devices,
                                                d3);
        if (ret < 0) {
            if (ret != -ENOSYS) {
                ALOGW("%s failed to split snd_device %d",
                      __func__,
                      popcount(a1) > 1 ? d1 : d2);
            }
            goto end;
        }

        // NB: case 7 is hypothetical and isn't a practical usecase yet.
        // But if it does happen, we need to give priority to d2 if
        // the combo devices active on the existing usecase share a backend.
        // This is because we cannot have a usecase active on a combo device
        // and a new usecase requests one device in this combo pair.
        if (platform_check_backends_match(d3[0], d3[1])) {
            return d2; // case 5
        } else {
            return d1; // case 1
        }
    } else {
        if (platform_check_backends_match(d1, d2)) {
            return d2; // case 2, 4
        } else {
            return d1; // case 6, 3
        }
    }

end:
    return d2; // return whatever was calculated before.
}

static void check_and_route_playback_usecases(struct audio_device *adev,
                                              struct audio_usecase *uc_info,
                                              snd_device_t snd_device)
@@ -733,10 +845,15 @@ static void check_and_route_playback_usecases(struct audio_device *adev,
            }
        }

        snd_device_t d_device;
        list_for_each(node, &adev->usecase_list) {
            usecase = node_to_item(node, struct audio_usecase, list);
            if (switch_device[usecase->id]) {
                enable_snd_device(adev, snd_device);
                d_device = derive_playback_snd_device(usecase, uc_info,
                                                      snd_device);
                enable_snd_device(adev, d_device);
                /* Update the out_snd_device before enabling the audio route */
                usecase->out_snd_device = d_device;
            }
        }

@@ -744,9 +861,7 @@ static void check_and_route_playback_usecases(struct audio_device *adev,
           specified usecase to new snd devices */
        list_for_each(node, &adev->usecase_list) {
            usecase = node_to_item(node, struct audio_usecase, list);
            /* Update the out_snd_device only before enabling the audio route */
            if (switch_device[usecase->id] ) {
                usecase->out_snd_device = snd_device;
                enable_audio_route(adev, usecase);
            }
        }
+7 −7
Original line number Diff line number Diff line
@@ -1424,35 +1424,35 @@ int platform_set_device_mute(void *platform, bool state, char *dir)
    return ret;
}

bool platform_can_split_snd_device(snd_device_t snd_device,
int platform_can_split_snd_device(snd_device_t snd_device,
                                   int *num_devices,
                                   snd_device_t *new_snd_devices)
{
    bool status = false;
    int ret = -EINVAL;

    if (NULL == num_devices || NULL == new_snd_devices) {
        ALOGE("%s: NULL pointer ..", __func__);
        return false;
        return -EINVAL;
    }

    /*
     * If wired headset/headphones/line devices share the same backend
     * with speaker/earpiece this routine returns false.
     * with speaker/earpiece this routine -EINVAL.
     */
    if (snd_device == SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES &&
        !platform_check_backends_match(SND_DEVICE_OUT_SPEAKER, SND_DEVICE_OUT_HEADPHONES)) {
        *num_devices = 2;
        new_snd_devices[0] = SND_DEVICE_OUT_SPEAKER;
        new_snd_devices[1] = SND_DEVICE_OUT_HEADPHONES;
        status = true;
        ret = 0;
    } else if (snd_device == SND_DEVICE_OUT_SPEAKER_AND_LINE &&
               !platform_check_backends_match(SND_DEVICE_OUT_SPEAKER, SND_DEVICE_OUT_LINE)) {
        *num_devices = 2;
        new_snd_devices[0] = SND_DEVICE_OUT_SPEAKER;
        new_snd_devices[1] = SND_DEVICE_OUT_LINE;
        status = true;
        ret = 0;
    }
    return status;
    return ret;
}

snd_device_t platform_get_output_snd_device(void *platform, audio_devices_t devices)
+2 −2
Original line number Diff line number Diff line
@@ -1075,11 +1075,11 @@ bool platform_send_gain_dep_cal(void *platform __unused,
    return true;
}

bool platform_can_split_snd_device(snd_device_t in_snd_device __unused,
int platform_can_split_snd_device(snd_device_t in_snd_device __unused,
                                   int *num_devices __unused,
                                   snd_device_t *out_snd_devices __unused)
{
    return false;
    return -ENOSYS;
}

bool platform_check_backends_match(snd_device_t snd_device1 __unused,
+11 −12
Original line number Diff line number Diff line
@@ -1863,47 +1863,46 @@ int platform_set_device_mute(void *platform, bool state, char *dir)
    return ret;
}

bool platform_can_split_snd_device(snd_device_t snd_device,
int platform_can_split_snd_device(snd_device_t snd_device,
                                  int *num_devices,
                                  snd_device_t *new_snd_devices)
{
    bool status = false;

    int ret = -EINVAL;
    if (NULL == num_devices || NULL == new_snd_devices) {
        ALOGE("%s: NULL pointer ..", __func__);
        return false;
        return -EINVAL;
    }

    /*
     * If wired headset/headphones/line devices share the same backend
     * with speaker/earpiece this routine returns false.
     * with speaker/earpiece this routine returns -EINVAL.
     */
    if (snd_device == SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES &&
        !platform_check_backends_match(SND_DEVICE_OUT_SPEAKER, SND_DEVICE_OUT_HEADPHONES)) {
        *num_devices = 2;
        new_snd_devices[0] = SND_DEVICE_OUT_SPEAKER;
        new_snd_devices[1] = SND_DEVICE_OUT_HEADPHONES;
        status = true;
        ret = 0;
    } else if (snd_device == SND_DEVICE_OUT_SPEAKER_AND_LINE &&
               !platform_check_backends_match(SND_DEVICE_OUT_SPEAKER, SND_DEVICE_OUT_LINE)) {
        *num_devices = 2;
        new_snd_devices[0] = SND_DEVICE_OUT_SPEAKER;
        new_snd_devices[1] = SND_DEVICE_OUT_LINE;
        status = true;
        ret = 0;
    } else if (snd_device == SND_DEVICE_OUT_SPEAKER_SAFE_AND_HEADPHONES &&
               !platform_check_backends_match(SND_DEVICE_OUT_SPEAKER_SAFE, SND_DEVICE_OUT_HEADPHONES)) {
        *num_devices = 2;
        new_snd_devices[0] = SND_DEVICE_OUT_SPEAKER_SAFE;
        new_snd_devices[1] = SND_DEVICE_OUT_HEADPHONES;
        status = true;
        ret = 0;
    } else if (snd_device == SND_DEVICE_OUT_SPEAKER_SAFE_AND_LINE &&
               !platform_check_backends_match(SND_DEVICE_OUT_SPEAKER_SAFE, SND_DEVICE_OUT_LINE)) {
        *num_devices = 2;
        new_snd_devices[0] = SND_DEVICE_OUT_SPEAKER_SAFE;
        new_snd_devices[1] = SND_DEVICE_OUT_LINE;
        status = true;
        ret = 0;
    }
    return status;
    return ret;
}

snd_device_t platform_get_output_snd_device(void *platform, audio_devices_t devices)
+3 −3
Original line number Diff line number Diff line
@@ -100,7 +100,7 @@ int platform_set_usecase_pcm_id(audio_usecase_t usecase, int32_t type, int32_t p
void platform_set_echo_reference(struct audio_device *adev, bool enable, audio_devices_t out_device);
int platform_swap_lr_channels(struct audio_device *adev, bool swap_channels);

bool platform_can_split_snd_device(snd_device_t in_snd_device,
int platform_can_split_snd_device(snd_device_t in_snd_device,
                                  int *num_devices,
                                  snd_device_t *out_snd_devices);