From 37d5c6b8812bf17307a4400861ee867adce47d9c Mon Sep 17 00:00:00 2001 From: nikk gitanes Date: Sun, 17 Jun 2012 19:30:29 +0400 Subject: [PATCH] Updated USB audio support Initial code for kernel-based rates detection and failsafe operation. --- libaudio/Android.mk | 1 + libaudio/audio_hw.c | 129 ++++++++++++++++++++++++++++++++++---------- 2 files changed, 102 insertions(+), 28 deletions(-) mode change 100755 => 100644 libaudio/audio_hw.c diff --git a/libaudio/Android.mk b/libaudio/Android.mk index b6e24a1..73e8734 100755 --- a/libaudio/Android.mk +++ b/libaudio/Android.mk @@ -23,6 +23,7 @@ LOCAL_C_INCLUDES += \ external/tinyalsa/include \ system/media/audio_utils/include \ system/media/audio_effects/include +LOCAL_CFLAGS += -fstack-protector -fstack-protector-all LOCAL_SHARED_LIBRARIES := liblog libcutils libtinyalsa libaudioutils libdl LOCAL_MODULE_TAGS := optional diff --git a/libaudio/audio_hw.c b/libaudio/audio_hw.c old mode 100755 new mode 100644 index 73b8863..da96488 --- a/libaudio/audio_hw.c +++ b/libaudio/audio_hw.c @@ -14,13 +14,14 @@ * limitations under the License. */ -#define LOG_TAG "A10_audio_HW" +#define LOG_TAG "sun4i_audio_hardware" #define LOG_NDEBUG 0 #include #include #include #include +#include #include #include @@ -140,8 +141,6 @@ D/tinyalsa( 602): mix id:15 name:ADC Input Mux /* ALSA cards for A10 */ #define CARD_A10_ABE 0 #define CARD_A10_HDMI 1 -#define CARD_A10_USB 2 -#define CARD_A10_FM 3 #define CARD_DEFAULT CARD_A10_ABE /* ALSA ports for A10 */ @@ -156,16 +155,20 @@ D/tinyalsa( 602): mix id:15 name:ADC Input Mux #define PORT_HDMI 0 #define PORT_USB 0 +/* EXTERNAL DAC */ +#define OUT_CARD_CID_PROPERTY "usb.audio.out.device" +#define OUT_CARD_CID "pcmC2D0p" +#define CAP_CARD_CID_PROPERTY "usb.audio.cap.device" +#define CAP_CARD_CID "pcmC2D0c" + /* constraint imposed by ABE: all period sizes must be multiples of 24 */ #define ABE_BASE_FRAME_COUNT 24 /* number of base blocks in a short period (low latency) */ -//#define SHORT_PERIOD_MULTIPLIER 44 /* 22 ms */ -#define SHORT_PERIOD_MULTIPLIER 80 /* 40 ms */ +#define SHORT_PERIOD_MULTIPLIER 80 /* 40 ms */ //ex.44 /* 22 ms */ /* number of frames per short period (low latency) */ #define SHORT_PERIOD_SIZE (ABE_BASE_FRAME_COUNT * SHORT_PERIOD_MULTIPLIER) /* number of short periods in a long period (low power) */ -//#define LONG_PERIOD_MULTIPLIER 14 /* 308 ms */ -#define LONG_PERIOD_MULTIPLIER 6 /* 240 ms */ +#define LONG_PERIOD_MULTIPLIER 6 /* 240 ms */ //ex.14 /* 308 ms */ /* number of frames per long period (low power) */ #define LONG_PERIOD_SIZE (SHORT_PERIOD_SIZE * LONG_PERIOD_MULTIPLIER) /* number of periods for low power playback */ @@ -173,8 +176,7 @@ D/tinyalsa( 602): mix id:15 name:ADC Input Mux /* number of pseudo periods for low latency playback */ #define PLAYBACK_SHORT_PERIOD_COUNT 4 /* number of periods for capture */ -// #define CAPTURE_PERIOD_COUNT 2 -#define CAPTURE_PERIOD_COUNT 4 +#define CAPTURE_PERIOD_COUNT 4 //ex.2 /* minimum sleep time in out_write() when write threshold is not reached */ #define MIN_WRITE_SLEEP_US 5000 @@ -260,10 +262,6 @@ D/tinyalsa( 602): mix id:15 name:ADC Input Mux #define PRODUCT_NAME_PROPERTY "ro.product.name" #define PRODUCT_DEVICE_TORO "toro" #define PRODUCT_NAME_YAKJU "yakju" -#define AUDIO_OUT_DEV_PROPERTY "media.audio.out.mode" -#define AUDIO_OUT_CODEC "codec" -#define AUDIO_OUT_USB "usb" -#define AUDIO_OUT_FM "fm" enum tty_modes { TTY_MODE_OFF, @@ -628,10 +626,26 @@ static int is_device_toro(void) static int is_device_usb_dac(void) { char property[PROPERTY_VALUE_MAX]; - property_get(AUDIO_OUT_DEV_PROPERTY, property, AUDIO_OUT_CODEC); - LOGV("### property: %s, value: %s", AUDIO_OUT_DEV_PROPERTY, property); - /* return true if the property matches the given value */ - return strcmp(property, AUDIO_OUT_USB) == 0; + property_get(OUT_CARD_CID_PROPERTY, property, OUT_CARD_CID); + struct stat info; + char path[18]="/dev/snd/"; + strcat(path, property); + int ret = stat(path, &info); + LOGV("# property: %s, value: %s, ret: %d", OUT_CARD_CID_PROPERTY, property, ret); + return(ret == -1 ? 0 : 1); +} + +/* Returns true for external DAC, false otherwise */ +static int is_device_usb_cap(void) +{ + char property[PROPERTY_VALUE_MAX]; + property_get(CAP_CARD_CID_PROPERTY, property, CAP_CARD_CID); + struct stat info; + char path[18]="/dev/snd/"; + strcat(path, property); + int ret = stat(path, &info); + LOGV("# property: %s, value: %s, ret: %d", CAP_CARD_CID_PROPERTY, property, ret); + return(ret == -1 ? 0 : 1); } /* The enable flag when 0 makes the assumption that enums are disabled by @@ -1159,7 +1173,7 @@ static void select_input_device(struct sun4i_audio_device *adev) /* must be called with hw device and output stream mutexes locked */ static int start_output_stream(struct sun4i_stream_out *out) { - F_LOG; + F_LOG; struct sun4i_audio_device *adev = out->dev; unsigned int card = CARD_DEFAULT; unsigned int port = PORT_MM; @@ -1175,21 +1189,47 @@ static int start_output_stream(struct sun4i_stream_out *out) out->config.rate = MM_FULL_POWER_SAMPLING_RATE; if (adev->devices & AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET) { port = PORT_SPDIF; - LOGV("### SPDIF audio out selected! Sampling rate: %d Hz", MM_FULL_POWER_SAMPLING_RATE); + LOGV("### SPDIF audio out selected! Sampling rate: %d Hz", out->config.rate); } else if(adev->devices & AUDIO_DEVICE_OUT_AUX_DIGITAL) { - LOGV("### HDMI audio out selected! Sampling rate: %d Hz", MM_LOW_POWER_SAMPLING_RATE); card = CARD_A10_HDMI; port = PORT_HDMI; out->config.rate = MM_LOW_POWER_SAMPLING_RATE; + LOGV("### HDMI audio out selected! Sampling rate: %d Hz", out->config.rate); } /* HACK: USB DAC output */ else if(is_device_usb_dac()) { - LOGV("### USB audio out selected! Sampling rate: %d Hz", MM_LOW_POWER_SAMPLING_RATE); - card = CARD_A10_USB; - port = PORT_USB; + char property[PROPERTY_VALUE_MAX]; + property_get(OUT_CARD_CID_PROPERTY, property, OUT_CARD_CID); + // property value: pcmC[4]D[6]p + char *ptr; + ptr = property; + card = property[4] - '0'; + port = property[6] - '0'; + LOGV("# card: %u, port: %u, type: %s", card, port, &ptr[7]); + /* Define preferred rate */ out->config.rate = MM_LOW_POWER_SAMPLING_RATE; + /* HW Info (failsafe check) */ + struct pcm_config config; + struct pcm *pcm; + pcm = pcm_hwinfo(card, port, PCM_OUT, &config); + if (!pcm || !pcm_is_ready(pcm)) { + LOGE("### Unable to get Hardware information for device %s (%s)\n", + property, pcm_get_error(pcm)); + goto exit; + } + LOGV("# Supported Rates: (%uHz - %uHz)\n", config.rate_min, config.rate_max); + LOGV("# Supported Channels: (%uCh - %uCh)\n", config.channels_min, config.channels_max); + if (!(out->config.rate >= config.rate_min && + out->config.rate <= config.rate_max)) { + LOGV("# Requested %dHz using supported value %dHz\n",out->config.rate, config.rate_max); + out->config.rate = config.rate_max; + } + pcm_close(pcm); + /* END of HW Info */ + LOGV("### USB audio out selected! Sampling rate: %dHz", out->config.rate); } +exit: /* default to low power: will be corrected in out_write if necessary before first write to * tinyalsa. */ @@ -1626,10 +1666,11 @@ static int out_remove_audio_effect(const struct audio_stream *stream, effect_han /* must be called with hw device and input stream mutexes locked */ static int start_input_stream(struct sun4i_stream_in *in) { - F_LOG; + F_LOG; int ret = 0; struct sun4i_audio_device *adev = in->dev; - + unsigned int card = CARD_DEFAULT; + unsigned int port = PORT_MM2_UL; adev->active_input = in; if (adev->mode != AUDIO_MODE_IN_CALL) { @@ -1643,9 +1684,41 @@ static int start_input_stream(struct sun4i_stream_in *in) AUDIO_FORMAT_PCM_16_BIT, in->config.channels, in->requested_rate); + /* HACK: USB DAC input */ + if(is_device_usb_cap()) { + char property[PROPERTY_VALUE_MAX]; + property_get(CAP_CARD_CID_PROPERTY, property, CAP_CARD_CID); + // property value: pcmC[4]D[6]c + char *ptr = property; + card = property[4] - '0'; + port = property[6] - '0'; + LOGV("# card: %u, port: %u, type: %s", card, port, &ptr[7]); + /* Define preferred rate */ + in->config.rate = MM_LOW_POWER_SAMPLING_RATE; + /* HW Info (failsafe check) */ + struct pcm_config config; + struct pcm *pcm; + pcm = pcm_hwinfo(card, port, PCM_IN, &config); + if (!pcm || !pcm_is_ready(pcm)) { + LOGE("### Unable to get Hardware information for device %s (%s)\n", + property, pcm_get_error(pcm)); + goto exit; + } + LOGV("# Supported Rates: (%uHz - %uHz)\n", config.rate_min, config.rate_max); + LOGV("# Supported Channels: (%uCh - %uCh)\n", config.channels_min, config.channels_max); + if (!(in->config.rate >= config.rate_min && + in->config.rate <= config.rate_max)) { + LOGV("# Requested %dHz using supported value %dHz\n",in->config.rate, config.rate_max); + in->config.rate = config.rate_max; + } + pcm_close(pcm); + /* END of HW Info */ + LOGV("### USB audio input selected! Sampling rate: %dHz", in->config.rate); + } +exit: /* this assumes routing is done previously */ - in->pcm = pcm_open(0, PORT_MM2_UL, PCM_IN, &in->config); + in->pcm = pcm_open(card, port, PCM_IN, &in->config); if (!pcm_is_ready(in->pcm)) { LOGE("cannot open pcm_in driver: %s", pcm_get_error(in->pcm)); pcm_close(in->pcm); @@ -1655,12 +1728,12 @@ static int start_input_stream(struct sun4i_stream_in *in) /* if no supported sample rate is available, use the resampler */ if (in->resampler) { - F_LOG; + // F_LOG; LOGV("### in->resampler"); in->resampler->reset(in->resampler); in->frames_in = 0; } - F_LOG; + // F_LOG; return 0; }