Files
2026-08-24 10:03:24 +08:00

252 lines
9.5 KiB
C

/*
* Copyright (C) 2025 Amlogic Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define LOG_TAG "aml_policy"
#include "audio_hw.h"
#include "aml_policy.h"
#include "aml_audio_log.h"
typedef struct {
aml_output_device_type out_device;
aml_output_source_type src_type; //from mixer or main decoder
int decoder_id; //main decoder id
} aml_policy_mapping_t;
aml_policy_mapping_t g_policy_mappings[] = {
{AML_OUTPUT_DEVICE_DEFAULT, AML_OUTPUT_SOURCE_MIXER_OUTPUT, -1},
{AML_OUTPUT_DEVICE_HEADPHONE, AML_OUTPUT_SOURCE_MIXER_OUTPUT, -1},
{AML_OUTPUT_DEVICE_USB_SPEAKER, AML_OUTPUT_SOURCE_MIXER_OUTPUT, -1},
{AML_OUTPUT_DEVICE_BLUETOOTH_SPEAKER, AML_OUTPUT_SOURCE_MIXER_OUTPUT, -1},
};
pthread_mutex_t g_policy_lock = PTHREAD_MUTEX_INITIALIZER;
aml_output_device_type aml_get_output_device_by_id(int decoder_id) {
aml_output_device_type device = AML_OUTPUT_DEVICE_DEFAULT;
if (decoder_id < 0) {
AM_LOGV("Invalid decoder_id: %d", decoder_id);
return device;
}
pthread_mutex_lock(&g_policy_lock);
for (int i = 0; i < AML_OUTPUT_DEVICE_MAX; i++) {
if (g_policy_mappings[i].decoder_id == decoder_id) {
device = g_policy_mappings[i].out_device;
AM_LOGV("Found decoder_id %d mapped to device %d", decoder_id, device);
break;
}
}
pthread_mutex_unlock(&g_policy_lock);
if (device == AML_OUTPUT_DEVICE_MAX) {
AM_LOGV("Decoder_id %d not found in policy mappings", decoder_id);
}
return device;
}
aml_policy_type aml_get_policy_by_id(int decoder_id) {
aml_policy_type policy = AML_POLICY_TYPE_NORMAL;
aml_output_device_type device = AML_OUTPUT_DEVICE_DEFAULT;
device = aml_get_output_device_by_id(decoder_id);
switch (device) {
case AML_OUTPUT_DEVICE_DEFAULT:
policy = AML_POLICY_TYPE_NORMAL;
break;
case AML_OUTPUT_DEVICE_HEADPHONE:
policy = AML_POLICY_TYPE_HEADPHONE;
break;
case AML_OUTPUT_DEVICE_USB_SPEAKER:
policy = AML_POLICY_TYPE_USB_SPEAKER;
break;
case AML_OUTPUT_DEVICE_BLUETOOTH_SPEAKER:
policy = AML_POLICY_TYPE_BLUETOOTH_SPEAKER;
break;
default:
break;
}
return policy;
}
aml_policy_type aml_get_stream_policy(struct aml_stream_out *out, aml_stream_port_type out_type) {
aml_policy_type policy = AML_POLICY_TYPE_NORMAL;
struct aml_audio_device *adev = out->dev;
switch (out_type) {
case AML_STREAM_STEREO_PORT:
policy = aml_get_policy_by_id(out->decoder_id);
break;
case AML_STREAM_MULTI_CHANNEL_PORT:
policy = AML_POLICY_TYPE_NORMAL;
break;
case AML_STREAM_BYPASS_PORT:
//for dcv decoder, bypass port means ddp
if (out->optical_format >= out->hal_internal_format) {
policy = AML_POLICY_TYPE_NORMAL;
}
break;
case AML_STREAM_TRANSCODE_PORT:
if (out->optical_format >= out->hal_internal_format) {
if (out->hal_internal_format == AUDIO_FORMAT_E_AC3) {
if (adev->dual_spdif_support) {
policy = AML_POLICY_TYPE_NORMAL;
}
} else {
policy = AML_POLICY_TYPE_NORMAL;
}
}
break;
default:
AM_LOGE("unsupported output port type: %d", out_type);
policy = AML_POLICY_TYPE_NONE;
break;
}
return policy;
}
aml_output_source_type aml_get_output_device_src_type(aml_output_device_type device) {
return g_policy_mappings[device].src_type;
}
int aml_restore_output_device_to_mixer_by_id(int id) {
if (id < 0) {
AM_LOGW("Invalid decoder_id: %d", id);
return 0;
}
//restore output_device by id
pthread_mutex_lock(&g_policy_lock);
for (int i = 0; i < AML_OUTPUT_DEVICE_MAX; i++) {
if (g_policy_mappings[i].decoder_id == id) {
g_policy_mappings[i].src_type = AML_OUTPUT_SOURCE_MIXER_OUTPUT;
g_policy_mappings[i].decoder_id = -1;
AM_LOGI("restore device %d to mixer out, id from %d to -1", g_policy_mappings[i].out_device, id);
break;
}
}
pthread_mutex_unlock(&g_policy_lock);
return 0;
}
int aml_restore_stream_by_output_device(aml_output_device_type device) {
struct aml_audio_device *adev = (struct aml_audio_device *)adev_get_handle();
int i = 0;
int decoder_id = g_policy_mappings[device].decoder_id;
//restore output_device
pthread_mutex_lock(&g_policy_lock);
if (g_policy_mappings[device].src_type != AML_OUTPUT_SOURCE_DECODER_OUTPUT) {
pthread_mutex_unlock(&g_policy_lock);
return 0;
}
g_policy_mappings[device].src_type = AML_OUTPUT_SOURCE_MIXER_OUTPUT;
g_policy_mappings[device].decoder_id = -1;
pthread_mutex_unlock(&g_policy_lock);
//restore old bound stream
pthread_mutex_lock (&adev->active_outputs_lock);
for (i = 0; i < STREAM_NUM_MAX; i++) {
struct aml_stream_out *aml_out = adev->active_outputs[i];
if (aml_out && STREAM_MAIN_INPUT == aml_out->stream_type && decoder_id == aml_out->decoder_id) {
aml_out->out_device = AUDIO_DEVICE_OUT_SPEAKER;
out_set_stream_mute((struct audio_stream_out *)aml_out, false, false);
AM_LOGI("restore device %d to mixer out<--->restore stream:%p to speaker out",device, aml_out);
break;
}
}
pthread_mutex_unlock (&adev->active_outputs_lock);
return 0;
}
int aml_set_output_device_by_id(int id, aml_output_device_type device) {
struct aml_audio_device *adev = (struct aml_audio_device *)adev_get_handle();
int i = 0;
audio_devices_t hal_device = AUDIO_DEVICE_OUT_SPEAKER;
//If output device is bound to an old id, unbind it first.
aml_restore_stream_by_output_device(device);
pthread_mutex_lock (&adev->active_outputs_lock);
for (i = 0; i < STREAM_NUM_MAX; i++) {
struct aml_stream_out *aml_out = adev->active_outputs[i];
if (aml_out && STREAM_MAIN_INPUT == aml_out->stream_type && id == aml_out->decoder_id) {
// if the id is already bound to the old device, unbind it first.
aml_restore_output_device_to_mixer_by_id(id);
//set internal mute when binding to specified device
if (device != AML_OUTPUT_DEVICE_DEFAULT) {
out_set_stream_mute((struct audio_stream_out *)aml_out, true, false);
}
//set output device
switch (device) {
case AML_OUTPUT_DEVICE_DEFAULT:
hal_device = AUDIO_DEVICE_OUT_SPEAKER;
out_set_stream_mute((struct audio_stream_out *)aml_out, false, false);
break;
case AML_OUTPUT_DEVICE_HEADPHONE:
hal_device = AUDIO_DEVICE_OUT_WIRED_HEADPHONE;
break;
case AML_OUTPUT_DEVICE_USB_SPEAKER:
hal_device = AUDIO_DEVICE_OUT_USB_HEADSET;
break;
case AML_OUTPUT_DEVICE_BLUETOOTH_SPEAKER:
hal_device = AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET;
break;
default:
break;
}
aml_out->out_device = hal_device;
pthread_mutex_lock (&g_policy_lock);
g_policy_mappings[device].decoder_id = id;
g_policy_mappings[device].src_type = AML_OUTPUT_SOURCE_DECODER_OUTPUT;
pthread_mutex_unlock (&g_policy_lock);
break;
}
}
pthread_mutex_unlock (&adev->active_outputs_lock);
if (i == STREAM_NUM_MAX) {
AM_LOGI("can't find stream which id = %d", id);
}
AM_LOGI("set output device by id: %d, device: 0x%x", id, device);
return 0;
}
void aml_dump_policy_mappings(struct aml_audio_device* adev, int fd) {
pthread_mutex_lock(&g_policy_lock);
dprintf(fd, "\n-------------[AML_HAL] Current policy mappings: ---------------\n");
for (int i = 0; i < AML_OUTPUT_DEVICE_MAX; i++) {
const char *device_name = "Unknown";
const char *source_name = "Unknown";
switch (g_policy_mappings[i].out_device) {
case AML_OUTPUT_DEVICE_DEFAULT: device_name = "Default"; break;
case AML_OUTPUT_DEVICE_HEADPHONE: device_name = "Headphone"; break;
case AML_OUTPUT_DEVICE_USB_SPEAKER: device_name = "USB Speaker"; break;
case AML_OUTPUT_DEVICE_BLUETOOTH_SPEAKER: device_name = "Bluetooth"; break;
default: break;
}
switch (g_policy_mappings[i].src_type) {
case AML_OUTPUT_SOURCE_MIXER_OUTPUT: source_name = "Mixer"; break;
case AML_OUTPUT_SOURCE_DECODER_OUTPUT: source_name = "Decoder"; break;
default: break;
}
dprintf(fd,"\t\tDevice: %s, Source: %s, Decoder ID: %d\n",
device_name, source_name, g_policy_mappings[i].decoder_id);
}
pthread_mutex_unlock(&g_policy_lock);
}