/* * Copyright (C) 2017 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. */ /** ** aml_alsa_mixer.c ** ** This program is APIs for read/write mixers of alsa. ** author: shen pengru ** */ #define LOG_TAG "audio_hw_utils_alsamixer" #include #include #include #include #include "aml_audio_log.h" #include #include #include #include #include "audio_hw_utils.h" #include "alsa_device_parser.h" #include "aml_malloc_debug.h" static struct aml_mixer_list gAmlMixerList[] = { /* for i2s out status */ {AML_MIXER_ID_I2S_MUTE, "Audio i2s mute"}, /* for spdif out status */ {AML_MIXER_ID_SPDIF_MUTE, "Audio spdif mute"}, {AML_MIXER_ID_SPDIF_B_MUTE, "Audio spdif_b mute"}, /* for HDMI TX status */ {AML_MIXER_ID_HDMI_OUT_AUDIO_MUTE, "Audio hdmi-out mute"}, /* for HDMI ARC status */ {AML_MIXER_ID_HDMI_ARC_AUDIO_ENABLE, "HDMI ARC Switch"}, {AML_MIXER_ID_EARCRX_ATTENDED_TYPE, "eARC_RX attended type"}, {AML_MIXER_ID_AUDIO_IN_SRC, "Audio In Source"}, {AML_MIXER_ID_I2SIN_AUDIO_TYPE, "I2SIN Audio Type"}, {AML_MIXER_ID_SPDIFIN_AUDIO_TYPE, "SPDIFIN Audio Type"}, {AML_MIXER_ID_HW_RESAMPLE_ENABLE, "Hardware resample enable"}, {AML_MIXER_ID_OUTPUT_SWAP, "Output Swap"}, /* for HDMI RX status */ {AML_MIXER_ID_HDMI_IN_AUDIO_STABLE, "HDMIIN audio stable"}, {AML_MIXER_ID_HDMI_IN_SAMPLERATE, "HDMIIN audio samplerate"}, {AML_MIXER_ID_HDMI_IN_CHANNELS, "HDMIIN audio channels"}, {AML_MIXER_ID_HDMI_IN_FORMATS, "HDMIIN audio format"}, {AML_MIXER_ID_HDMI_IN_EDID, "HDMIIN AUDIO EDID"}, /* for ATV status */ {AML_MIXER_ID_ATV_IN_AUDIO_STABLE, "ATV audio stable"}, {AML_MIXER_ID_SPDIF_FORMAT, "Audio spdif format"}, {AML_MIXER_ID_SPDIF_B_FORMAT, "Audio spdif_b format"}, {AML_MIXER_ID_AUDIO_SRC_TO_HDMI, "HDMITX Audio Source Select"}, /* for AV status */ {AML_MIXER_ID_AV_IN_AUDIO_STABLE, "AV audio stable"}, /* for Speaker master volume */ {AML_MIXER_ID_EQ_LCH_VOLUME, "AED Lch volume"}, {AML_MIXER_ID_EQ_RCH_VOLUME, "AED Rch volume"}, {AML_MIXER_ID_EQ_MASTER_VOLUME, "AED master volume"}, {AML_MIXER_ID_AED_EQ_ENABLE, "AED EQ enable"}, /* ARCIN and SPDIFIN switch*/ {AML_MIXER_ID_SPDIFIN_ARCIN_SWITCH, "AudioIn Switch"}, {AML_MIXER_ID_SPDIFIN_PAO, "SPDIFIN PAO"}, /* HDMI IN audio format */ {AML_MIXER_ID_HDMIIN_AUDIO_TYPE, "HDMIIN Audio Type"}, /* SPDIF IN audio SRC select */ {AML_MIXER_ID_SPDIFIN_SRC, "Audio spdifin source"}, {AML_MIXER_ID_HDMIIN_AUDIO_PACKET, "HDMIIN Audio Packet"}, {AML_MIXER_ID_CHANGE_SPDIF_PLL, "SPDIF CLK Fine Setting"}, {AML_MIXER_ID_CHANGE_SPDIF_PLL_PPM, "SPDIF CLK Fine PPM Tuning"}, {AML_MIXER_ID_CHANGE_SPDIFB_PLL, "SPDIF_B CLK Fine Setting"}, {AML_MIXER_ID_CHANGE_SPDIFB_PLL_PPM, "SPDIF_B CLK Fine PPM Tuning"}, {AML_MIXER_ID_CHANGE_I2S_PLL, "TDM MCLK Fine Setting"}, {AML_MIXER_ID_CHANGE_I2S_PLL_PPM, "TDM MCLK Fine PPM Tuning"}, {AML_MIXER_ID_CHANGE_EARC_PLL, "eARC_TX CLK Fine Setting"}, {AML_MIXER_ID_CHANGE_EARC_PLL_PPM, "eARC_TX CLK Fine PPM Tuning"}, {AML_MIXER_ID_SPDIF_IN_SAMPLERATE, "SPDIFIN audio samplerate"}, {AML_MIXER_ID_HW_RESAMPLE_SOURCE, "Hw resample module"}, {AML_MIXER_ID_EARCRX_AUDIO_CODING_TYPE, "eARC_RX Audio Coding Type"}, {AML_MIXER_ID_EARCRX_CS_MUTE, "eARC_RX CS Mute"}, {AML_MIXER_ID_EARCRX_STABLE, "eARC RX stable"}, {AML_MIXER_ID_EARCRX_AUDIO_SAMPLERATE, "eARC_RX Audio Sample Frequency"}, {AML_MIXER_ID_EARCRX_AUDIO_BIT_DEPTH, "eARC_RX Audio Word Length"}, {AML_MIXER_ID_EARCRX_CHANNEL_MAP, "eARC_RX Channel Map"}, {AML_MIXER_ID_EARCRX_CA, "eARC_RX Channel Allocation"}, {AML_MIXER_ID_AUDIO_HAL_FORMAT, "Audio HAL Format"}, {AML_MIXER_ID_HDMIIN_AUDIO_EDID, "HDMIIN AUDIO EDID"}, {AML_MIXER_ID_EARC_TX_ATTENDED_TYPE, "eARC_TX attended type"}, {AML_MIXER_ID_EARC_TX_AUDIO_TYPE, "eARC_TX Audio Coding Type"}, {AML_MIXER_ID_EARC_TX_EARC_MODE, "eARC_TX eARC Mode"}, {AML_MIXER_ID_ARC_EARC_TX_ENABLE, "ARC eARC TX enable"}, {AML_MIXER_ID_EARCTX_CDS, "eARC_TX CDS"}, {AML_MIXER_ID_EARC_TX_LATENCY, "eARC_TX Latency"}, {AML_MIXER_ID_ARC_EARC_SPDIFOUT_REG_MUTE, "ARC eARC Spdifout Reg Mute"}, {AML_MIXER_ID_EARC_TX_CA, "eARC_TX Channel Allocation"}, {AML_MIXER_ID_DIGITAL_MODE, "Audio Digital Mode"}, {AML_MIXER_ID_DRC_CONTROL, "Audio DRC Control"}, {AML_MIXER_ID_OUTPUT_SELECT, "Audio Output Select"}, {AML_MIXER_ID_AML_CHIP_ID, "AML chip id"}, {AML_MIXER_ID_TVIN_VIDEO_DELAY, "TVIN VIDEO DELAY"}, {AML_MIXER_ID_TVIN_VIDEO_MIN_DELAY, "TVIN VIDEO MIN DELAY"}, {AML_MIXER_ID_TVIN_VIDEO_MAX_DELAY, "TVIN VIDEO MAX DELAY"}, {AML_MIXER_ID_SPDIF_OUT_CHANNEL_STATUS, "spdif out channel status"}, {AML_MIXER_ID_SPDIF_B_OUT_CHANNEL_STATUS, "spdif_b out channel status"}, {AML_MIXER_ID_MEDIA_VIDEO_DELAY, "Media Video Delay"}, {AML_MIXER_ID_HDMIIN_AUDIO_MODE, "HDMIIN Audio output mode"}, {AML_MIXER_ID_VAD_ENABLE, "VAD enable"}, {AML_MIXER_ID_VAD_SOURCE_SEL, "VAD Source sel"}, {AML_MIXER_ID_VAD_SWITCH, "VAD Switch"}, {AML_MIXER_ID_DTV_CLK_TUNING, "DTV clk force MPLL"}, {AML_MIXER_ID_I2S2HDMI_FORMAT, "Audio I2S to HDMITX Format"}, {AML_MIXER_ID_SPDIF_OUT_CHANNEL_STATUS, "spdif out channel status"}, {AML_MIXER_ID_AUDIO_PCPD_MONITOR_ENABLE, "Pc_Pd_Monitor_A Detect enable"}, {AML_MIXER_ID_AUDIO_PCPD_MONITOR_DATA_TYPE, "Pc_Pd_Monitor_A Audio Type"}, {AML_MIXER_ID_DAC_DIGITAL_VOLUME, "DAC Digital Playback Volume"}, {AML_MIXER_ID_AMP_MASTER_VOLUME, "AMP Master Volume"}, {AML_MIXER_ID_AED_FULL_DRC_ENABLE, "AED Full-band DRC enable"}, {AML_MIXER_ID_DAC_DIGITAL_VOLUME, "AMP DAC Digital Playback Volume"}, {AML_MIXER_ID_HDMI_IN1_AUDIO_STABLE, "HDMIIN_1 audio stable"}, {AML_MIXER_ID_HDMI_IN1_SAMPLERATE, "HDMIIN_1 audio samplerate"}, {AML_MIXER_ID_HDMI_IN1_CHANNELS, "HDMIIN_1 audio channels"}, {AML_MIXER_ID_HDMIIN1_ALLOCATION, "HDMIIN_1 audio allocation"}, {AML_MIXER_ID_HDMI_IN1_FORMATS, "HDMIIN_1 audio format"}, {AML_MIXER_ID_HDMIIN1_AUDIO_PACKET, "HDMIIN_1 Audio Packet"}, {AML_MIXER_ID_HDMI_IN1_NONAUDIO, "HDMIIN_1 NONAUDIO"}, {AML_MIXER_ID_HW_RESAMPLE_C_SOURCE, "Hw resample c module"}, {AML_MIXER_ID_HW_RESAMPLE_C_ENABLE, "Hardware resample c enable"}, {AML_MIXER_ID_AUDIO_PCPD_MONITOR_B_DATA_TYPE, "Pc_Pd_Monitor_B Audio Type"}, {AML_MIXER_ID_AUDIO_PCPD_MONITOR_B_ENABLE, "Pc_Pd_Monitor_B Detect enable"}, {AML_MIXER_ID_HW_MIXER_VOLUME, "mixer vol"}, {AML_MIXER_ID_HW_MIXER_LANE_SOURCE, "MIXER LANE SOURCE SELECT"}, {AML_MIXER_ID_HW_MIXER_XRUN, "Mixer Xrun"}, {AML_MIXER_ID_AD82128_C30_MASTER_VOLUME,"ad82128_c_30 Master Volume"}, {AML_MIXER_ID_AD82128_C31_MASTER_VOLUME,"ad82128_c_31 Master Volume"}, {AML_MIXER_ID_AI_SOUND_MODE, "AI Sort Result"}, }; static char *get_mixer_name_by_id(int mixer_id) { int i; int cnt_mixer = sizeof(gAmlMixerList) / sizeof(struct aml_mixer_list); for (i = 0; i < cnt_mixer; i++) { if (gAmlMixerList[i].id == mixer_id) { return gAmlMixerList[i].mixer_name; } } return NULL; } int open_mixer_handle(struct aml_mixer_handle *mixer_handle) { int card = 0; struct mixer *pmixer = NULL; card = alsa_device_get_card_index(); if (card < 0) { AM_LOGE("Failed to get sound card"); return -1; } pmixer = mixer_open(card); if (NULL == pmixer) { AM_LOGE("Failed to open mixer"); return -1; } mixer_handle->pMixer = pmixer; pthread_mutex_init(&mixer_handle->lock, NULL); return 0; } int close_mixer_handle(struct aml_mixer_handle *mixer_handle) { struct mixer *pMixer = mixer_handle->pMixer; if (NULL != pMixer) { mixer_close(pMixer); } return 0; } static struct mixer_ctl *get_mixer_ctl_handle(struct mixer *pmixer, int mixer_id) { struct mixer_ctl *pCtrl = NULL; if (get_mixer_name_by_id(mixer_id) != NULL) { pCtrl = mixer_get_ctl_by_name(pmixer, get_mixer_name_by_id(mixer_id)); } return pCtrl; } int aml_mixer_ctrl_get_array(struct aml_mixer_handle *mixer_handle, int mixer_id, void *array, int count) { struct mixer *pMixer = mixer_handle->pMixer; struct mixer_ctl *pCtrl; if (pMixer == NULL) { AM_LOGE("pMixer is invalid!"); return -1; } pthread_mutex_lock(&mixer_handle->lock); pCtrl = get_mixer_ctl_handle(pMixer, mixer_id); if (pCtrl == NULL) { AM_LOGE("Failed to open mixer %s", get_mixer_name_by_id(mixer_id)); pthread_mutex_unlock(&mixer_handle->lock); return -1; } mixer_ctl_get_array(pCtrl, array, count); pthread_mutex_unlock(&mixer_handle->lock); return 0; } int aml_mixer_ctrl_get_int(struct aml_mixer_handle *mixer_handle, int mixer_id) { struct mixer *pMixer = mixer_handle->pMixer; struct mixer_ctl *pCtrl; int value = -1; if (pMixer == NULL) { AM_LOGE("pMixer is invalid!"); return -1; } pthread_mutex_lock(&mixer_handle->lock); pCtrl = get_mixer_ctl_handle(pMixer, mixer_id); if (pCtrl == NULL) { AM_LOGE("Failed to open mixer %s", get_mixer_name_by_id(mixer_id)); pthread_mutex_unlock(&mixer_handle->lock); return -1; } value = mixer_ctl_get_value(pCtrl, 0); pthread_mutex_unlock(&mixer_handle->lock); return value; } int aml_mixer_ctrl_get_enum_str_to_int(struct aml_mixer_handle *mixer_handle, int mixer_id, int *ret) { struct mixer *pMixer = mixer_handle->pMixer; struct mixer_ctl *pCtrl; const char *string = NULL; int value = -1; if (pMixer == NULL) { AM_LOGE("pMixer is invalid!"); return -1; } pthread_mutex_lock(&mixer_handle->lock); pCtrl = get_mixer_ctl_handle(pMixer, mixer_id); if (pCtrl == NULL) { AM_LOGE("Failed to open mixer %s", get_mixer_name_by_id(mixer_id)); pthread_mutex_unlock(&mixer_handle->lock); return -1; } value = mixer_ctl_get_value(pCtrl, 0); string = mixer_ctl_get_enum_string(pCtrl, value); pthread_mutex_unlock(&mixer_handle->lock); if (string) { *ret = atoi(string); return 0; } else { return -1; } } int aml_mixer_ctrl_set_array(struct aml_mixer_handle *mixer_handle, int mixer_id, void *array, int count) { struct mixer *pMixer = mixer_handle->pMixer; struct mixer_ctl *pCtrl = NULL; int i = 0; if (pMixer == NULL) { AM_LOGE("pMixer is invalid!"); return -1; } AM_LOGI("audiohal set kctrl: %s, array length: %d", get_mixer_name_by_id(mixer_id), count); for (i = 0; i < count; i ++) { AM_LOGI("array[%d]: %#x", i, ((char *)array)[i]); } pthread_mutex_lock(&mixer_handle->lock); pCtrl = get_mixer_ctl_handle(pMixer, mixer_id); if (pCtrl == NULL) { AM_LOGE("Failed to open mixer %s", get_mixer_name_by_id(mixer_id)); pthread_mutex_unlock(&mixer_handle->lock); return -1; } mixer_ctl_set_array(pCtrl, array, count); pthread_mutex_unlock(&mixer_handle->lock); return 0; } int aml_mixer_ctrl_set_int(struct aml_mixer_handle *mixer_handle, int mixer_id, int value) { struct mixer *pMixer = mixer_handle->pMixer; struct mixer_ctl *pCtrl = NULL; int ret = -1; if (pMixer == NULL) { AM_LOGE("pMixer is invalid!"); return -1; } AM_LOGI("audiohal set kctrl: %s, value: %d", get_mixer_name_by_id(mixer_id), value); pthread_mutex_lock(&mixer_handle->lock); pCtrl = get_mixer_ctl_handle(pMixer, mixer_id); if (pCtrl == NULL) { AM_LOGE("Failed to open mixer %s", get_mixer_name_by_id(mixer_id)); pthread_mutex_unlock(&mixer_handle->lock); return -1; } ret = mixer_ctl_set_value(pCtrl, 0, value); pthread_mutex_unlock(&mixer_handle->lock); return ret; } int aml_mixer_ctrl_set_str(struct aml_mixer_handle *mixer_handle, int mixer_id, char *value) { struct mixer *pMixer = mixer_handle->pMixer; struct mixer_ctl *pCtrl = NULL; if (pMixer == NULL) { AM_LOGE("pMixer is invalid!"); return -1; } AM_LOGI("audiohal set kctrl: %s, value: %s", get_mixer_name_by_id(mixer_id), value); pthread_mutex_lock(&mixer_handle->lock); pCtrl = get_mixer_ctl_handle(pMixer, mixer_id); if (pCtrl == NULL) { AM_LOGE("Failed to open mixer %s", get_mixer_name_by_id(mixer_id)); pthread_mutex_unlock(&mixer_handle->lock); return -1; } mixer_ctl_set_enum_by_string(pCtrl, value); pthread_mutex_unlock(&mixer_handle->lock); return 0; } static void print_enum(struct mixer_ctl *ctl, int fd) { unsigned int num_enums; unsigned int i; unsigned int value; const char *string; num_enums = mixer_ctl_get_num_enums(ctl); value = mixer_ctl_get_value(ctl, 0); for (i = 0; i < num_enums; i++) { string = mixer_ctl_get_enum_string(ctl, i); dprintf(fd, "%s%s, ", value == i ? "> " : "", string); } } static void print_control_values(struct mixer_ctl *control, int fd) { enum mixer_ctl_type type; unsigned int num_values; unsigned int i; int min, max; int ret; char *buf = NULL; unsigned int tlv_header_size = 0; type = mixer_ctl_get_type(control); num_values = mixer_ctl_get_num_values(control); if ((type == MIXER_CTL_TYPE_BYTE) && (num_values > 0)) { if (mixer_ctl_is_access_tlv_rw(control) != 0) { tlv_header_size = TLV_HEADER_SIZE; } buf = aml_audio_calloc(1, num_values + tlv_header_size); if (buf == NULL) { AM_LOGE("Failed to alloc mem for bytes %u", num_values); return; } ret = mixer_ctl_get_array(control, buf, num_values + tlv_header_size); if (ret < 0) { AM_LOGE("Failed to mixer_ctl_get_array"); aml_audio_free(buf); return; } } for (i = 0; i < num_values; i++) { switch (type) { case MIXER_CTL_TYPE_INT: dprintf(fd,"%d", mixer_ctl_get_value(control, i)); break; case MIXER_CTL_TYPE_BOOL: dprintf(fd,"%s", mixer_ctl_get_value(control, i) ? "On" : "Off"); break; case MIXER_CTL_TYPE_ENUM: print_enum(control, fd); break; case MIXER_CTL_TYPE_BYTE: dprintf(fd,"%02hhx", buf[i]); break; default: dprintf(fd,"unknown"); break; }; if ((i + 1) < num_values) { dprintf(fd, ", "); } } if (type == MIXER_CTL_TYPE_INT) { min = mixer_ctl_get_range_min(control); max = mixer_ctl_get_range_max(control); dprintf(fd, " (range %d->%d)", min, max); } aml_audio_free(buf); } int set_spdifin_pao(struct aml_mixer_handle *mixer_handle,int enable) { return aml_mixer_ctrl_set_int(mixer_handle, AML_MIXER_ID_SPDIFIN_PAO, enable); } int get_spdifin_samplerate(struct aml_mixer_handle *mixer_handle) { int index = aml_mixer_ctrl_get_int(mixer_handle, AML_MIXER_ID_SPDIF_IN_SAMPLERATE); return index; } int get_hdmiin_sample_rate_index(struct aml_mixer_handle *mixer_handle, int is_hdmi1) { int stable = 0; int mixer_id = AML_MIXER_ID_HDMI_IN_AUDIO_STABLE; if (is_hdmi1) { mixer_id = AML_MIXER_ID_HDMI_IN1_AUDIO_STABLE; } stable = aml_mixer_ctrl_get_int(mixer_handle, mixer_id); if (!stable) { return -1; } if (is_hdmi1) { mixer_id = AML_MIXER_ID_HDMI_IN1_SAMPLERATE; } else { mixer_id = AML_MIXER_ID_HDMI_IN_SAMPLERATE; } return aml_mixer_ctrl_get_int(mixer_handle, mixer_id); } int get_hdmiin_samplerate(int resample_index) { int sample_rate; switch (resample_index) { case HW_RESAMPLE_32K: sample_rate = 32000; break; case HW_RESAMPLE_44K: sample_rate = 44100; break; case HW_RESAMPLE_48K: sample_rate = 48000; break; case HW_RESAMPLE_88K: sample_rate = 88200; break; case HW_RESAMPLE_96K: sample_rate = 96000; break; case HW_RESAMPLE_176K: sample_rate = 176400; break; case HW_RESAMPLE_192K: sample_rate = 192000; break; default: sample_rate = 0; break; } return sample_rate; } int get_hdmiin_channel(struct aml_mixer_handle *mixer_handle, int is_hdmi1) { int channel_index = 0; int mixer_id = AML_MIXER_ID_HDMI_IN_CHANNELS; /*hmdirx audio support: N/A, 2, 3, 4, 5, 6, 7, 8*/ if (is_hdmi1) { mixer_id = AML_MIXER_ID_HDMI_IN1_CHANNELS; } else { mixer_id = AML_MIXER_ID_HDMI_IN_CHANNELS; } channel_index = aml_mixer_ctrl_get_int(mixer_handle, mixer_id); if (channel_index == 0) { return 0; } else if (1 == channel_index) { return 2; } else { return 8; } } hdmiin_audio_packet_t get_hdmiin_audio_packet(struct aml_mixer_handle *mixer_handle, int is_hdmi1) { int audio_packet = 0; if (is_hdmi1) { audio_packet = aml_mixer_ctrl_get_int(mixer_handle,AML_MIXER_ID_HDMIIN1_AUDIO_PACKET); } else { audio_packet = aml_mixer_ctrl_get_int(mixer_handle,AML_MIXER_ID_HDMIIN_AUDIO_PACKET); } if (audio_packet < 0) { return AUDIO_PACKET_NONE; } return (hdmiin_audio_packet_t)audio_packet; } int set_hdmiin_audio_mode(struct aml_mixer_handle *mixer_handle, char *mode) { if (mode == NULL || strlen(mode) > 5) return -EINVAL; return aml_mixer_ctrl_set_str(mixer_handle, AML_MIXER_ID_HDMIIN_AUDIO_MODE, mode); } enum hdmiin_audio_mode get_hdmiin_audio_mode(struct aml_mixer_handle *mixer_handle) { return (enum hdmiin_audio_mode)aml_mixer_ctrl_get_int(mixer_handle, AML_MIXER_ID_HDMIIN_AUDIO_MODE); } static inline eMixerHwResample _sr_enum(int sr) { switch (sr) { case 32000: return HW_RESAMPLE_32K; case 44100: return HW_RESAMPLE_44K; case 48000: return HW_RESAMPLE_48K; case 88200: return HW_RESAMPLE_88K; case 96000: return HW_RESAMPLE_96K; case 176400: return HW_RESAMPLE_176K; case 192000: return HW_RESAMPLE_192K; default: return HW_RESAMPLE_DISABLE; } } eMixerHwResample get_eArcIn_samplerate(struct aml_mixer_handle *mixer_handle) { int sr = aml_mixer_ctrl_get_int(mixer_handle, AML_MIXER_ID_EARCRX_AUDIO_SAMPLERATE); /* eARC mix control does not return sample rate as enum type as HDMIIN and SPDIF */ return _sr_enum(sr); } void aml_alsa_mixer_status_dump(struct aml_mixer_handle *mixer_handle, int fd) { dprintf(fd, "\n-------------[AML_HAL] ALSA mxier ctrl ------------------------\n"); struct mixer_ctl *ctl; const char *name, *type; unsigned int num_ctls, num_values; unsigned int i; //struct aml_mixer_handle *aml_mixer = &adev->alsa_mixer; struct aml_mixer_handle *aml_mixer = mixer_handle; if (!aml_mixer->pMixer) { ALOGW("%s() Warning! mixer = NULL!, return!", __func__); return; } num_ctls = mixer_get_num_ctls(aml_mixer->pMixer); dprintf(fd,"Number of controls: %u\n", num_ctls); dprintf(fd,"ctl\ttype\tnum\t%-40svalue\n", "name"); for (i = 0; i < num_ctls; i++) { ctl = mixer_get_ctl(aml_mixer->pMixer, i); //ask one mixer_ctrl name = mixer_ctl_get_name(ctl); type = mixer_ctl_get_type_string(ctl); num_values = mixer_ctl_get_num_values(ctl); dprintf(fd, "%u\t%s\t%u\t%-40s", i, type, num_values, name); pthread_mutex_lock(&aml_mixer->lock); print_control_values(ctl, fd); pthread_mutex_unlock(&aml_mixer->lock); dprintf(fd, "\n"); } }