summaryrefslogtreecommitdiffstats
path: root/audio
diff options
context:
space:
mode:
authorEric Laurent <elaurent@google.com>2012-04-30 12:30:50 -0700
committerEric Laurent <elaurent@google.com>2012-06-01 18:10:59 -0700
commit4bf3d14aa11008149793c5d70714face83cf12ca (patch)
treed8983422d0546bf27561ee65ead66fb56bceedb3 /audio
parent8a1d4ed8f9f88fb44ceaf3bdf270fd372d7b89d3 (diff)
downloaddevice_samsung_tuna-4bf3d14aa11008149793c5d70714face83cf12ca.zip
device_samsung_tuna-4bf3d14aa11008149793c5d70714face83cf12ca.tar.gz
device_samsung_tuna-4bf3d14aa11008149793c5d70714face83cf12ca.tar.bz2
audio: add support for multichannel HDMI
Added a dedicated audio output stream for multichannel HDMI. This output stream is used when an HDMI sink supporting 6 or 8 PCM channels is connected and 5.1 or 7.1 multichannel content it played. Change-Id: I7ad1cd6be4c2b3a9e24a4811aa87e7223badedc4
Diffstat (limited to 'audio')
-rw-r--r--audio/audio_hw.c254
-rw-r--r--audio/audio_policy.conf7
2 files changed, 248 insertions, 13 deletions
diff --git a/audio/audio_hw.c b/audio/audio_hw.c
index 54ee3c8..fb5a423 100644
--- a/audio/audio_hw.c
+++ b/audio/audio_hw.c
@@ -103,6 +103,9 @@
#define MIXER_FLAT_RESPONSE "Flat response"
#define MIXER_4KHZ_LPF_0DB "4Khz LPF 0dB"
+/* HDMI mixer controls */
+#define MIXER_MAXIMUM_LPCM_CHANNELS "Maximum LPCM channels"
+
/* ALSA cards for OMAP4 */
#define CARD_OMAP4_ABE 0
@@ -161,6 +164,13 @@
/* number of periods for deep buffer playback (screen off) */
#define PLAYBACK_DEEP_BUFFER_LONG_PERIOD_COUNT 2
+/* number of frames per period for HDMI multichannel output */
+#define HDMI_MULTI_PERIOD_SIZE 1024
+/* number of periods for HDMI multichannel output */
+#define HDMI_MULTI_PERIOD_COUNT 4
+/* default number of channels for HDMI multichannel output */
+#define HDMI_MULTI_DEFAULT_CHANNEL_COUNT 6
+
/* Number of pseudo periods for low latency playback.
* These are called "pseudo" periods in that they are not known as periods by ALSA.
* Formerly, ALSA was configured in MMAP mode with 2 large periods, and this
@@ -278,6 +288,8 @@
#define PRODUCT_DEVICE_TORO "toro"
#define PRODUCT_NAME_YAKJU "yakju"
+#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
+
enum tty_modes {
TTY_MODE_OFF,
TTY_MODE_VCO,
@@ -310,6 +322,16 @@ struct pcm_config pcm_config_tones = {
#endif
};
+struct pcm_config pcm_config_hdmi_multi = {
+ .channels = HDMI_MULTI_DEFAULT_CHANNEL_COUNT, /* changed when the stream is opened */
+ .rate = MM_FULL_POWER_SAMPLING_RATE, /* changed when the stream is opened */
+ .period_size = HDMI_MULTI_PERIOD_SIZE,
+ .period_count = HDMI_MULTI_PERIOD_COUNT,
+ .format = PCM_FORMAT_S16_LE,
+ .start_threshold = 0,
+ .avail_min = 0,
+};
+
struct pcm_config pcm_config_mm_ul = {
.channels = 2,
.rate = MM_FULL_POWER_SAMPLING_RATE,
@@ -604,6 +626,7 @@ struct mixer_ctls
enum output_type {
OUTPUT_DEEP_BUF, // deep PCM buffers output stream
OUTPUT_LOW_LATENCY, // low latency output stream
+ OUTPUT_HDMI,
OUTPUT_TOTAL
};
@@ -654,6 +677,8 @@ struct tuna_stream_out {
struct echo_reference_itfe *echo_reference;
int write_threshold;
bool use_long_periods;
+ audio_channel_mask_t channel_mask;
+ audio_channel_mask_t sup_channel_masks[3];
struct tuna_audio_device *dev;
};
@@ -712,6 +737,21 @@ struct tuna_stream_in {
struct tuna_audio_device *dev;
};
+
+#define STRING_TO_ENUM(string) { #string, string }
+
+struct string_to_enum {
+ const char *name;
+ uint32_t value;
+};
+
+const struct string_to_enum out_channels_name_to_enum_table[] = {
+ STRING_TO_ENUM(AUDIO_CHANNEL_OUT_STEREO),
+ STRING_TO_ENUM(AUDIO_CHANNEL_OUT_5POINT1),
+ STRING_TO_ENUM(AUDIO_CHANNEL_OUT_7POINT1),
+};
+
+
/**
* NOTE: when multiple mutexes have to be acquired, always respect the following order:
* hw device > in stream > out stream
@@ -1361,7 +1401,9 @@ static int start_output_stream_low_latency(struct tuna_stream_out *out)
flags, &out->config[PCM_SPDIF]);
}
- if(adev->devices & AUDIO_DEVICE_OUT_AUX_DIGITAL) {
+ /* priority is given to multichannel HDMI output */
+ if ((adev->devices & AUDIO_DEVICE_OUT_AUX_DIGITAL) &&
+ (adev->outputs[OUTPUT_HDMI] == NULL || adev->outputs[OUTPUT_HDMI]->standby)) {
/* HDMI output in use */
out->config[PCM_HDMI] = pcm_config_tones;
out->config[PCM_HDMI].rate = MM_LOW_POWER_SAMPLING_RATE;
@@ -1372,7 +1414,7 @@ static int start_output_stream_low_latency(struct tuna_stream_out *out)
/* Close any PCMs that could not be opened properly and return an error */
for (i = 0; i < PCM_TOTAL; i++) {
if (out->pcm[i] && !pcm_is_ready(out->pcm[i])) {
- ALOGE("cannot open pcm_out driver: %s", pcm_get_error(out->pcm[i]));
+ ALOGE("cannot open pcm_out driver %d: %s", i, pcm_get_error(out->pcm[i]));
pcm_close(out->pcm[i]);
out->pcm[i] = NULL;
success = false;
@@ -1423,6 +1465,30 @@ static int start_output_stream_deep_buffer(struct tuna_stream_out *out)
return 0;
}
+static int start_output_stream_hdmi(struct tuna_stream_out *out)
+{
+ struct tuna_audio_device *adev = out->dev;
+
+ /* force standby on low latency output stream to close HDMI driver in case it was in use */
+ if (adev->outputs[OUTPUT_LOW_LATENCY] != NULL &&
+ !adev->outputs[OUTPUT_LOW_LATENCY]->standby) {
+ struct tuna_stream_out *ll_out = adev->outputs[OUTPUT_LOW_LATENCY];
+ pthread_mutex_lock(&ll_out->lock);
+ do_output_standby(ll_out);
+ pthread_mutex_unlock(&ll_out->lock);
+ }
+
+ out->pcm[PCM_HDMI] = pcm_open(CARD_OMAP4_HDMI, PORT_HDMI, PCM_OUT, &out->config[PCM_HDMI]);
+
+ if (out->pcm[PCM_HDMI] && !pcm_is_ready(out->pcm[PCM_HDMI])) {
+ ALOGE("cannot open pcm_out driver: %s", pcm_get_error(out->pcm[PCM_HDMI]));
+ pcm_close(out->pcm[PCM_HDMI]);
+ out->pcm[PCM_HDMI] = NULL;
+ return -ENOMEM;
+ }
+ return 0;
+}
+
static int check_input_parameters(uint32_t sample_rate, audio_format_t format, int channel_count)
{
if (format != AUDIO_FORMAT_PCM_16_BIT)
@@ -1567,6 +1633,13 @@ static uint32_t out_get_sample_rate(const struct audio_stream *stream)
return DEFAULT_OUT_SAMPLING_RATE;
}
+static uint32_t out_get_sample_rate_hdmi(const struct audio_stream *stream)
+{
+ struct tuna_stream_out *out = (struct tuna_stream_out *)stream;
+
+ return out->config[PCM_HDMI].rate;
+}
+
static int out_set_sample_rate(struct audio_stream *stream, uint32_t rate)
{
return 0;
@@ -1599,9 +1672,16 @@ static size_t out_get_buffer_size_deep_buffer(const struct audio_stream *stream)
return size * audio_stream_frame_size((struct audio_stream *)stream);
}
+static size_t out_get_buffer_size_hdmi(const struct audio_stream *stream)
+{
+ return HDMI_MULTI_PERIOD_SIZE * audio_stream_frame_size((struct audio_stream *)stream);
+}
+
static uint32_t out_get_channels(const struct audio_stream *stream)
{
- return AUDIO_CHANNEL_OUT_STEREO;
+ struct tuna_stream_out *out = (struct tuna_stream_out *)stream;
+
+ return out->channel_mask;
}
static audio_format_t out_get_format(const struct audio_stream *stream)
@@ -1644,6 +1724,18 @@ static int do_output_standby(struct tuna_stream_out *out)
set_route_by_array(adev->mixer, hf_output, 0);
}
+ /* force standby on low latency output stream so that it can reuse HDMI driver if
+ * necessary when restarted */
+ if (out == adev->outputs[OUTPUT_HDMI]) {
+ if (adev->outputs[OUTPUT_LOW_LATENCY] != NULL &&
+ !adev->outputs[OUTPUT_LOW_LATENCY]->standby) {
+ struct tuna_stream_out *ll_out = adev->outputs[OUTPUT_LOW_LATENCY];
+ pthread_mutex_lock(&ll_out->lock);
+ do_output_standby(ll_out);
+ pthread_mutex_unlock(&ll_out->lock);
+ }
+ }
+
/* stop writing to echo reference */
if (out->echo_reference != NULL) {
out->echo_reference->write(out->echo_reference, NULL);
@@ -1722,9 +1814,11 @@ static int out_set_parameters(struct audio_stream *stream, const char *kvpairs)
(adev->devices & AUDIO_DEVICE_OUT_SPEAKER)))
do_output_standby(out);
}
- adev->devices &= ~AUDIO_DEVICE_OUT_ALL;
- adev->devices |= val;
- select_output_device(adev);
+ if (out != adev->outputs[OUTPUT_HDMI]) {
+ adev->devices &= ~AUDIO_DEVICE_OUT_ALL;
+ adev->devices |= val;
+ select_output_device(adev);
+ }
}
pthread_mutex_unlock(&out->lock);
if (force_input_standby) {
@@ -1742,7 +1836,41 @@ static int out_set_parameters(struct audio_stream *stream, const char *kvpairs)
static char * out_get_parameters(const struct audio_stream *stream, const char *keys)
{
- return strdup("");
+ struct tuna_stream_out *out = (struct tuna_stream_out *)stream;
+
+ struct str_parms *query = str_parms_create_str(keys);
+ char *str;
+ char value[256];
+ struct str_parms *reply = str_parms_create();
+ size_t i, j;
+ int ret;
+ bool first = true;
+
+ ret = str_parms_get_str(query, AUDIO_PARAMETER_STREAM_SUP_CHANNELS, value, sizeof(value));
+ if (ret >= 0) {
+ value[0] = '\0';
+ i = 0;
+ while (out->sup_channel_masks[i] != 0) {
+ for (j = 0; j < ARRAY_SIZE(out_channels_name_to_enum_table); j++) {
+ if (out_channels_name_to_enum_table[j].value == out->sup_channel_masks[i]) {
+ if (!first) {
+ strcat(value, "|");
+ }
+ strcat(value, out_channels_name_to_enum_table[j].name);
+ first = false;
+ break;
+ }
+ }
+ i++;
+ }
+ str_parms_add_str(reply, AUDIO_PARAMETER_STREAM_SUP_CHANNELS, value);
+ str = strdup(str_parms_to_str(reply));
+ } else {
+ str = strdup(keys);
+ }
+ str_parms_destroy(query);
+ str_parms_destroy(reply);
+ return str;
}
static uint32_t out_get_latency_low_latency(const struct audio_stream_out *stream)
@@ -1762,6 +1890,13 @@ static uint32_t out_get_latency_deep_buffer(const struct audio_stream_out *strea
pcm_config_mm.rate;
}
+static uint32_t out_get_latency_hdmi(const struct audio_stream_out *stream)
+{
+ struct tuna_stream_out *out = (struct tuna_stream_out *)stream;
+
+ return (HDMI_MULTI_PERIOD_SIZE * HDMI_MULTI_PERIOD_COUNT * 1000) / out->config[PCM_HDMI].rate;
+}
+
static int out_set_volume(struct audio_stream_out *stream, float left,
float right)
{
@@ -1952,6 +2087,46 @@ exit:
return bytes;
}
+static ssize_t out_write_hdmi(struct audio_stream_out *stream, const void* buffer,
+ size_t bytes)
+{
+ int ret;
+ struct tuna_stream_out *out = (struct tuna_stream_out *)stream;
+ struct tuna_audio_device *adev = out->dev;
+ size_t frame_size = audio_stream_frame_size(&out->stream.common);
+ size_t in_frames = bytes / frame_size;
+
+ /* acquiring hw device mutex systematically is useful if a low priority thread is waiting
+ * on the output stream mutex - e.g. executing select_mode() while holding the hw device
+ * mutex
+ */
+ pthread_mutex_lock(&adev->lock);
+ pthread_mutex_lock(&out->lock);
+ if (out->standby) {
+ ret = start_output_stream_hdmi(out);
+ if (ret != 0) {
+ pthread_mutex_unlock(&adev->lock);
+ goto exit;
+ }
+ out->standby = 0;
+ }
+ pthread_mutex_unlock(&adev->lock);
+
+ ret = pcm_write(out->pcm[PCM_HDMI],
+ buffer,
+ pcm_frames_to_bytes(out->pcm[PCM_HDMI], in_frames));
+
+exit:
+ pthread_mutex_unlock(&out->lock);
+
+ if (ret != 0) {
+ usleep(bytes * 1000000 / audio_stream_frame_size(&stream->common) /
+ out_get_sample_rate_hdmi(&stream->common));
+ }
+
+ return bytes;
+}
+
static int out_get_render_position(const struct audio_stream_out *stream,
uint32_t *dsp_frames)
{
@@ -2987,6 +3162,31 @@ exit:
return status;
}
+static int out_read_hdmi_channel_masks(struct tuna_stream_out *out) {
+ int max_channels = 0;
+ struct mixer *mixer_hdmi;
+
+ mixer_hdmi = mixer_open(CARD_OMAP4_HDMI);
+ if (mixer_hdmi) {
+ struct mixer_ctl *ctl;
+
+ ctl = mixer_get_ctl_by_name(mixer_hdmi, MIXER_MAXIMUM_LPCM_CHANNELS);
+ if (ctl)
+ max_channels = mixer_ctl_get_value(ctl, 0);
+ mixer_close(mixer_hdmi);
+ }
+
+ ALOGV("out_read_hdmi_channel_masks() got %d max channels", max_channels);
+
+ if (max_channels != 6 && max_channels != 8)
+ return -ENOSYS;
+
+ out->sup_channel_masks[0] = AUDIO_CHANNEL_OUT_5POINT1;
+ if (max_channels == 8)
+ out->sup_channel_masks[1] = AUDIO_CHANNEL_OUT_7POINT1;
+
+ return 0;
+}
static int adev_open_output_stream(struct audio_hw_device *dev,
audio_io_handle_t handle,
@@ -3006,14 +3206,42 @@ static int adev_open_output_stream(struct audio_hw_device *dev,
if (!out)
return -ENOMEM;
- if (flags & AUDIO_OUTPUT_FLAG_DEEP_BUFFER) {
+ out->sup_channel_masks[0] = AUDIO_CHANNEL_OUT_STEREO;
+ out->channel_mask = AUDIO_CHANNEL_OUT_STEREO;
+
+ if (flags & AUDIO_OUTPUT_FLAG_DIRECT &&
+ devices == AUDIO_DEVICE_OUT_AUX_DIGITAL) {
+ ALOGV("adev_open_output_stream() HDMI multichannel");
+ if (ladev->outputs[OUTPUT_HDMI] != NULL) {
+ ret = -ENOSYS;
+ goto err_open;
+ }
+ ret = out_read_hdmi_channel_masks(out);
+ if (ret != 0)
+ goto err_open;
+ output_type = OUTPUT_HDMI;
+ if (config->sample_rate == 0)
+ config->sample_rate = MM_FULL_POWER_SAMPLING_RATE;
+ if (config->channel_mask == 0)
+ config->channel_mask = AUDIO_CHANNEL_OUT_5POINT1;
+ out->channel_mask = config->channel_mask;
+ out->stream.common.get_buffer_size = out_get_buffer_size_hdmi;
+ out->stream.common.get_sample_rate = out_get_sample_rate_hdmi;
+ out->stream.get_latency = out_get_latency_hdmi;
+ out->stream.write = out_write_hdmi;
+ out->config[PCM_HDMI] = pcm_config_hdmi_multi;
+ out->config[PCM_HDMI].rate = config->sample_rate;
+ out->config[PCM_HDMI].channels = popcount(config->channel_mask);
+ } else if (flags & AUDIO_OUTPUT_FLAG_DEEP_BUFFER) {
ALOGV("adev_open_output_stream() deep buffer");
if (ladev->outputs[OUTPUT_DEEP_BUF] != NULL) {
ret = -ENOSYS;
goto err_open;
}
output_type = OUTPUT_DEEP_BUF;
+ out->channel_mask = AUDIO_CHANNEL_OUT_STEREO;
out->stream.common.get_buffer_size = out_get_buffer_size_deep_buffer;
+ out->stream.common.get_sample_rate = out_get_sample_rate;
out->stream.get_latency = out_get_latency_deep_buffer;
out->stream.write = out_write_deep_buffer;
} else {
@@ -3024,6 +3252,7 @@ static int adev_open_output_stream(struct audio_hw_device *dev,
}
output_type = OUTPUT_LOW_LATENCY;
out->stream.common.get_buffer_size = out_get_buffer_size_low_latency;
+ out->stream.common.get_sample_rate = out_get_sample_rate;
out->stream.get_latency = out_get_latency_low_latency;
out->stream.write = out_write_low_latency;
}
@@ -3037,7 +3266,6 @@ static int adev_open_output_stream(struct audio_hw_device *dev,
if (ret != 0)
goto err_open;
- out->stream.common.get_sample_rate = out_get_sample_rate;
out->stream.common.set_sample_rate = out_set_sample_rate;
out->stream.common.get_channels = out_get_channels;
out->stream.common.get_format = out_get_format;
@@ -3062,9 +3290,9 @@ static int adev_open_output_stream(struct audio_hw_device *dev,
* This is because out_set_parameters() with a route is not
* guaranteed to be called after an output stream is opened. */
- config->format = out_get_format(&out->stream.common);
- config->channel_mask = out_get_channels(&out->stream.common);
- config->sample_rate = out_get_sample_rate(&out->stream.common);
+ config->format = out->stream.common.get_format(&out->stream.common);
+ config->channel_mask = out->stream.common.get_channels(&out->stream.common);
+ config->sample_rate = out->stream.common.get_sample_rate(&out->stream.common);
*stream_out = &out->stream;
ladev->outputs[output_type] = out;
@@ -3400,7 +3628,7 @@ static int adev_open(const hw_module_t* module, const char* name,
adev->hw_device.close_input_stream = adev_close_input_stream;
adev->hw_device.dump = adev_dump;
- adev->mixer = mixer_open(0);
+ adev->mixer = mixer_open(CARD_OMAP4_ABE);
if (!adev->mixer) {
free(adev);
ALOGE("Unable to open the mixer, aborting.");
diff --git a/audio/audio_policy.conf b/audio/audio_policy.conf
index a2971b0..3a41ec1 100644
--- a/audio/audio_policy.conf
+++ b/audio/audio_policy.conf
@@ -36,6 +36,13 @@ audio_hw_modules {
devices AUDIO_DEVICE_OUT_SPEAKER|AUDIO_DEVICE_OUT_WIRED_HEADSET|AUDIO_DEVICE_OUT_WIRED_HEADPHONE
flags AUDIO_OUTPUT_FLAG_DEEP_BUFFER
}
+ hdmi {
+ sampling_rates 44100|48000
+ channel_masks dynamic
+ formats AUDIO_FORMAT_PCM_16_BIT
+ devices AUDIO_DEVICE_OUT_AUX_DIGITAL
+ flags AUDIO_OUTPUT_FLAG_DIRECT
+ }
}
inputs {
primary {