/* * 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); }