You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
968 lines
27 KiB
968 lines
27 KiB
/* |
|
* Copyright (C) 2012 The Android Open Source Project |
|
* |
|
* 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/ |
|
* Copyright (C) 2012 The Android Open Source Project |
|
* |
|
* 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. |
|
*/ |
|
|
|
/* |
|
* This code has modified by Intel Corporation |
|
*/ |
|
|
|
/* |
|
* Copyright (c) 2014, Intel 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 |
|
* |
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE |
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
|
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
*/ |
|
|
|
#define LOG_TAG "tiny_hdmi_audio_hw" |
|
//#define LOG_NDEBUG 0 |
|
|
|
#include <errno.h> |
|
#include <pthread.h> |
|
#include <stdint.h> |
|
#include <sys/time.h> |
|
#include <stdlib.h> |
|
|
|
#include <cutils/log.h> |
|
#include <cutils/str_parms.h> |
|
#include <cutils/properties.h> |
|
|
|
#include <hardware/hardware.h> |
|
#include <system/audio.h> |
|
#include <hardware/audio.h> |
|
|
|
#include <sound/asound.h> |
|
#include <tinyalsa/asoundlib.h> |
|
|
|
#define UNUSED_PARAMETER(x) (void)(x) |
|
|
|
#define DEFAULT_CARD 0 |
|
#define DEFAULT_DEVICE 0 |
|
|
|
/*this is used to avoid starvation*/ |
|
#define LATENCY_TO_BUFFER_SIZE_RATIO 2 |
|
|
|
/*Playback Channel Map*/ |
|
#define CHANNEL_MAP_REQUEST 2 |
|
|
|
/*global - keep track of the active device. |
|
This is needed since we are supporting more |
|
than one profile for HDMI. The Flinger |
|
assumes we can suport multiple streams |
|
at the same time. This makes sure only one stream |
|
is active at a time.*/ |
|
struct pcm * activePcm = NULL; |
|
/*TODO - move active channel inside activepcm*/ |
|
static unsigned int activeChannel; |
|
|
|
#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) |
|
|
|
#define STRING_TO_ENUM(string) { #string, string } |
|
|
|
struct channel_list { |
|
const char *name; |
|
uint32_t value; |
|
}; |
|
|
|
const struct channel_list channel_list_table[] = { |
|
STRING_TO_ENUM(AUDIO_CHANNEL_OUT_STEREO), |
|
STRING_TO_ENUM(AUDIO_CHANNEL_OUT_5POINT1), |
|
STRING_TO_ENUM(AUDIO_CHANNEL_OUT_7POINT1), |
|
}; |
|
|
|
struct pcm_config pcm_config_default = { |
|
.channels = 2, |
|
.rate = 44100, |
|
.period_size = 1024, |
|
.period_count = 4, |
|
.format = PCM_FORMAT_S24_LE, |
|
}; |
|
|
|
#define CHANNEL_MASK_MAX 3 |
|
struct audio_device { |
|
struct audio_hw_device hw_device; |
|
|
|
pthread_mutex_t lock; |
|
int card; |
|
int device; |
|
bool standby; |
|
int sink_sup_channels; |
|
audio_channel_mask_t sup_channel_masks[CHANNEL_MASK_MAX]; |
|
}; |
|
|
|
struct stream_out { |
|
struct audio_stream_out stream; |
|
|
|
pthread_mutex_t lock; |
|
struct pcm *pcm; |
|
bool standby; |
|
|
|
/* PCM Stream Configurations */ |
|
struct pcm_config pcm_config; |
|
uint32_t channel_mask; |
|
|
|
/* ALSA PCM Configurations */ |
|
uint32_t sample_rate; |
|
uint32_t buffer_size; |
|
uint32_t channels; |
|
uint32_t latency; |
|
|
|
struct audio_device *dev; |
|
}; |
|
|
|
/** |
|
* NOTE: when multiple mutexes have to be acquired, always respect the |
|
* following order: hw device > out stream |
|
*/ |
|
|
|
/* Helper functions */ |
|
|
|
// This function return the card number associated with the card ID (name) |
|
// passed as argument |
|
static int get_card_number_by_name(const char* name) |
|
{ |
|
char id_filepath[PATH_MAX] = {0}; |
|
char number_filepath[PATH_MAX] = {0}; |
|
ssize_t written; |
|
|
|
snprintf(id_filepath, sizeof(id_filepath), "/proc/asound/%s", name); |
|
|
|
written = readlink(id_filepath, number_filepath, sizeof(number_filepath)); |
|
if (written < 0) { |
|
ALOGE("Sound card %s does not exist - setting default", name); |
|
return DEFAULT_CARD; |
|
} else if (written >= (ssize_t)sizeof(id_filepath)) { |
|
ALOGE("Sound card %s name is too long - setting default", name); |
|
return DEFAULT_CARD; |
|
} |
|
|
|
// We are assured, because of the check in the previous elseif, that this |
|
// buffer is null-terminated. So this call is safe. |
|
// 4 == strlen("card") |
|
return atoi(number_filepath + 4); |
|
} |
|
|
|
static enum pcm_format Get_SinkSupported_format() |
|
{ |
|
/*TODO : query sink supported formats*/ |
|
return PCM_FORMAT_S24_LE; |
|
} |
|
|
|
static int make_sinkcompliant_buffers(void* input, void *output, int ipbytes) |
|
{ |
|
int i = 0,outbytes = 0; |
|
enum pcm_format out_pcmformat; |
|
int *src = (int*)input; |
|
int *dst = (int*)output; |
|
|
|
/*by default android currently support only |
|
16 bit signed PCM*/ |
|
out_pcmformat = Get_SinkSupported_format(); |
|
|
|
switch (out_pcmformat) { |
|
default: |
|
case PCM_FORMAT_S24_LE: |
|
{ |
|
ALOGV("convert 16 to 24 bits for %d",ipbytes); |
|
/*convert 16 bit input to 24 bit output |
|
in a 32 bit sample*/ |
|
if(0 == ipbytes) |
|
break; |
|
|
|
for(i = 0; i < (ipbytes/4); i++){ |
|
int x = (int)((int*)src)[i]; |
|
dst[i*2] = ((int)( x & 0x0000FFFF)) << 8; |
|
// trying to sign exdent |
|
dst[i*2] = dst[i*2] << 8; |
|
dst[i*2] = dst[i*2] >> 8; |
|
//shift to middle |
|
dst[i*2 + 1] = (int)(( x & 0xFFFF0000) >> 8); |
|
dst[i*2 + 1] = dst[i*2 + 1] << 8; |
|
dst[i*2 + 1] = dst[i*2 + 1] >> 8; |
|
} |
|
outbytes=ipbytes * 2; |
|
|
|
}//case |
|
};//switch |
|
|
|
return outbytes; |
|
} |
|
|
|
/* must be called with hw device and output stream mutexes locked */ |
|
static int start_output_stream(struct stream_out *out) |
|
{ |
|
struct audio_device *adev = out->dev; |
|
|
|
ALOGV("%s enter",__func__); |
|
|
|
if ((adev->card < 0) || (adev->device < 0)){ |
|
/*this will be updated once the hot plug intent |
|
sends these information.*/ |
|
adev->card = DEFAULT_CARD; |
|
adev->device = DEFAULT_DEVICE; |
|
ALOGV("%s : Setting default card/ device %d,%d",__func__,adev->card,adev->device); |
|
} |
|
|
|
ALOGV("%s enter %d,%d,%d,%d,%d",__func__, |
|
out->pcm_config.channels, |
|
out->pcm_config.rate, |
|
out->pcm_config.period_size, |
|
out->pcm_config.period_count, |
|
out->pcm_config.format); |
|
|
|
out->pcm_config.start_threshold = 0; |
|
out->pcm_config.stop_threshold = 0; |
|
out->pcm_config.silence_threshold = 0; |
|
|
|
if(activePcm){ |
|
ALOGV("Closing already open tiny alsa stream running state %d",(int)(activePcm)); |
|
pcm_close(activePcm); |
|
activePcm = NULL; |
|
} |
|
|
|
/*TODO - this needs to be updated once the device connect intent sends |
|
card, device id*/ |
|
adev->card = get_card_number_by_name("IntelHDMI"); |
|
ALOGD("%s: HDMI card number = %d, device = %d",__func__,adev->card,adev->device); |
|
|
|
out->pcm = pcm_open(adev->card, adev->device, PCM_OUT, &out->pcm_config); |
|
|
|
if (out->pcm && !pcm_is_ready(out->pcm)) { |
|
ALOGE("pcm_open() failed: %s", pcm_get_error(out->pcm)); |
|
pcm_close(out->pcm); |
|
activePcm = NULL; |
|
return -ENOMEM; |
|
} |
|
|
|
activePcm = out->pcm; |
|
activeChannel = out->pcm_config.channels; |
|
|
|
ALOGV("Initialized PCM device for channels %d handle = %d",out->pcm_config.channels, (int)activePcm); |
|
ALOGV("%s exit",__func__); |
|
return 0; |
|
} |
|
|
|
/* API functions */ |
|
|
|
static uint32_t out_get_sample_rate(const struct audio_stream *stream) |
|
{ |
|
struct stream_out *out = (struct stream_out *)stream; |
|
return out->pcm_config.rate; |
|
} |
|
|
|
static int out_set_sample_rate(struct audio_stream *stream, uint32_t rate) |
|
{ |
|
UNUSED_PARAMETER(stream); |
|
UNUSED_PARAMETER(rate); |
|
|
|
return 0; |
|
} |
|
|
|
static size_t out_get_buffer_size(const struct audio_stream *stream) |
|
{ |
|
struct stream_out *out = (struct stream_out *)stream; |
|
size_t buf_size; |
|
|
|
if(out->channel_mask > 2){ |
|
buf_size = out->pcm_config.period_size * |
|
audio_stream_out_frame_size((struct audio_stream_out *)stream); |
|
} |
|
else{ |
|
buf_size = out->pcm_config.period_size * |
|
out->pcm_config.period_count * |
|
audio_stream_out_frame_size((struct audio_stream_out *)stream); |
|
|
|
/*latency of audio flinger is based on this |
|
buffer size. modifying the buffer size to avoid |
|
starvation*/ |
|
buf_size/=LATENCY_TO_BUFFER_SIZE_RATIO; |
|
} |
|
|
|
ALOGV("%s : %d, period_size : %d, frame_size : %d", |
|
__func__, |
|
buf_size, |
|
out->pcm_config.period_size, |
|
audio_stream_out_frame_size((struct audio_stream_out *)stream)); |
|
|
|
return buf_size; |
|
|
|
} |
|
|
|
static uint32_t out_get_channels(const struct audio_stream *stream) |
|
{ |
|
struct stream_out *out = (struct stream_out *)stream; |
|
ALOGV("%s channel mask : %x",__func__,out->channel_mask); |
|
return out->channel_mask; |
|
} |
|
|
|
static audio_format_t out_get_format(const struct audio_stream *stream) |
|
{ |
|
UNUSED_PARAMETER(stream); |
|
|
|
return AUDIO_FORMAT_PCM_16_BIT; |
|
} |
|
|
|
static int out_set_format(struct audio_stream *stream, audio_format_t format) |
|
{ |
|
UNUSED_PARAMETER(stream); |
|
UNUSED_PARAMETER(format); |
|
|
|
return 0; |
|
} |
|
|
|
static int out_standby(struct audio_stream *stream) |
|
{ |
|
struct stream_out *out = (struct stream_out *)stream; |
|
|
|
ALOGV("%s enter standby = %d",__func__,out->standby); |
|
|
|
pthread_mutex_lock(&out->dev->lock); |
|
pthread_mutex_lock(&out->lock); |
|
|
|
if (!out->standby && activePcm) { |
|
pcm_close(activePcm); |
|
out->pcm = NULL; |
|
out->standby = true; |
|
activePcm = NULL; |
|
ALOGV("%s PCM device closed",__func__); |
|
} |
|
|
|
pthread_mutex_unlock(&out->lock); |
|
pthread_mutex_unlock(&out->dev->lock); |
|
|
|
ALOGV("%s exit",__func__); |
|
return 0; |
|
} |
|
|
|
static int out_dump(const struct audio_stream *stream, int fd) |
|
{ |
|
UNUSED_PARAMETER(stream); |
|
UNUSED_PARAMETER(fd); |
|
return 0; |
|
} |
|
|
|
static int out_set_parameters(struct audio_stream *stream, const char *kvpairs) |
|
{ |
|
struct stream_out *out = (struct stream_out *)stream; |
|
struct audio_device *adev = out->dev; |
|
struct str_parms *parms; |
|
char value[32]; |
|
int ret; |
|
ALOGV("%s enter",__func__); |
|
|
|
parms = str_parms_create_str(kvpairs); |
|
|
|
pthread_mutex_lock(&adev->lock); |
|
|
|
if (parms == NULL) { |
|
ALOGE("couldn't extract string params from key value pairs"); |
|
pthread_mutex_unlock(&adev->lock); |
|
return 0; |
|
} |
|
|
|
ret = str_parms_get_str(parms, "card", value, sizeof(value)); |
|
if (ret >= 0) |
|
adev->card = atoi(value); |
|
|
|
ret = str_parms_get_str(parms, "device", value, sizeof(value)); |
|
if (ret >= 0) |
|
adev->device = atoi(value); |
|
|
|
pthread_mutex_unlock(&adev->lock); |
|
str_parms_destroy(parms); |
|
|
|
ALOGV("%s exit",__func__); |
|
return 0; |
|
} |
|
|
|
static int parse_channel_map() |
|
{ |
|
struct mixer *mixer; |
|
int card = 0; |
|
struct mixer_ctl *ctl; |
|
enum mixer_ctl_type type; |
|
unsigned int num_values; |
|
unsigned int i,id; |
|
int chcount=0, chmap=0; |
|
|
|
card = get_card_number_by_name("IntelHDMI"); |
|
mixer = mixer_open(card); |
|
if (!mixer) { |
|
ALOGE("[EDID] Failed to open mixer\n"); |
|
goto chmap_error; |
|
} |
|
|
|
id = CHANNEL_MAP_REQUEST; |
|
if (id >= mixer_get_num_ctls(mixer)) { |
|
ALOGE("[EDID] Invalid request for channel map %d",id); |
|
goto chmap_error; |
|
} |
|
|
|
ctl = mixer_get_ctl_by_name(mixer, "Playback Channel Map"); |
|
|
|
//ctl = mixer_get_ctl(mixer, id); |
|
|
|
type = mixer_ctl_get_type(ctl); |
|
num_values = mixer_ctl_get_num_values(ctl); |
|
|
|
ALOGV("[EDID]id = %d",id); |
|
ALOGV("[EDID]type = %d",type); |
|
ALOGV("[EDID]count = %d",num_values); |
|
|
|
for (i = 0; i < num_values; i++) { |
|
switch (type) |
|
{ |
|
case MIXER_CTL_TYPE_INT: |
|
chmap = mixer_ctl_get_value(ctl, i); |
|
ALOGD("[EDID]chmap = %d", chmap); |
|
if(chmap > 0) ++chcount; |
|
break; |
|
default: |
|
printf(" unknown"); |
|
break; |
|
}; |
|
}//for |
|
|
|
ALOGD("[EDID]valid number of channels supported by sink = %d",chcount); |
|
|
|
mixer_close(mixer); |
|
|
|
return chcount; |
|
|
|
chmap_error: |
|
mixer_close(mixer); |
|
return 2;//stereo by default |
|
|
|
} |
|
|
|
static int out_read_edid(const struct stream_out *stream) |
|
{ |
|
struct stream_out *out = (struct stream_out *)stream; |
|
struct audio_device *adev = out->dev; |
|
|
|
/**read the channel max param from the sink*/ |
|
adev->sink_sup_channels = parse_channel_map(); |
|
|
|
if(adev->sink_sup_channels == 8) { |
|
adev->sup_channel_masks[0] = AUDIO_CHANNEL_OUT_5POINT1; |
|
adev->sup_channel_masks[1] = AUDIO_CHANNEL_OUT_7POINT1; |
|
} |
|
else if((adev->sink_sup_channels == 6) || (adev->sink_sup_channels > 2)) { |
|
adev->sup_channel_masks[0] = AUDIO_CHANNEL_OUT_5POINT1; |
|
} |
|
else { |
|
adev->sup_channel_masks[0] = AUDIO_CHANNEL_OUT_STEREO; |
|
} |
|
|
|
ALOGV("%s sink supports 0x%x max channels", __func__,adev->sink_sup_channels); |
|
return 0; |
|
} |
|
|
|
static char * out_get_parameters(const struct audio_stream *stream, const char *keys) |
|
{ |
|
struct stream_out *out = (struct stream_out *)stream; |
|
struct audio_device *adev = out->dev; |
|
struct str_parms *params_in = str_parms_create_str(keys); |
|
char *str = NULL; |
|
char value[256] = {0}; |
|
int ret; |
|
size_t i, j; |
|
bool append = false; |
|
|
|
struct str_parms *params_out = str_parms_create(); |
|
|
|
ALOGV("%s Entered %s", __func__,keys); |
|
|
|
if (params_in) { |
|
ret = str_parms_get_str(params_in, AUDIO_PARAMETER_STREAM_SUP_CHANNELS, value, sizeof(value)); |
|
if (ret >= 0) { |
|
/*read the channel support from sink*/ |
|
out_read_edid(out); |
|
|
|
value[0] = '\0'; |
|
for (i = 0; i < CHANNEL_MASK_MAX; i++) { |
|
for (j = 0; j < ARRAY_SIZE(channel_list_table); j++) { |
|
if (channel_list_table[j].value == adev->sup_channel_masks[i]) { |
|
if (append) { |
|
strcat(value, "|"); |
|
} |
|
strcat(value, channel_list_table[j].name); |
|
append = true; |
|
break; |
|
} |
|
} |
|
} |
|
} |
|
} |
|
if (params_out) { |
|
str_parms_add_str(params_out, AUDIO_PARAMETER_STREAM_SUP_CHANNELS, value); |
|
str = str_parms_to_str(params_out); |
|
} else { |
|
str = strdup(keys); |
|
} |
|
|
|
ALOGV("%s AUDIO_PARAMETER_STREAM_SUP_CHANNELS %s", __func__,str); |
|
if (params_in) { |
|
str_parms_destroy(params_in); |
|
} |
|
if (params_out) { |
|
str_parms_destroy(params_out); |
|
} |
|
|
|
return str; |
|
} |
|
|
|
static uint32_t out_get_latency(const struct audio_stream_out *stream) |
|
{ |
|
struct stream_out *out = (struct stream_out *)stream; |
|
return (out->pcm_config.period_size * out->pcm_config.period_count * 1000) / |
|
out_get_sample_rate(&stream->common); |
|
} |
|
|
|
static int out_set_volume(struct audio_stream_out *stream, float left, |
|
float right) |
|
{ |
|
UNUSED_PARAMETER(stream); |
|
UNUSED_PARAMETER(left); |
|
UNUSED_PARAMETER(right); |
|
|
|
return -ENOSYS; |
|
} |
|
|
|
static ssize_t out_write(struct audio_stream_out *stream, const void* buffer, |
|
size_t bytes) |
|
{ |
|
int ret = 0; |
|
struct stream_out *out = (struct stream_out *)stream; |
|
int32_t* dstbuff = NULL; |
|
int outbytes = 0; |
|
|
|
ALOGV("%s enter for bytes = %d channels = %d",__func__,bytes, out->pcm_config.channels); |
|
|
|
pthread_mutex_lock(&out->dev->lock); |
|
pthread_mutex_lock(&out->lock); |
|
|
|
if(activePcm == NULL) { |
|
ALOGV("%s: previous stream closed- open again",__func__); |
|
out->standby = true; |
|
} |
|
|
|
if (out->standby) { |
|
ret = start_output_stream(out); |
|
if (ret != 0) { |
|
goto err; |
|
} |
|
out->standby = false; |
|
} |
|
|
|
if((!out->pcm) || (activeChannel != out->pcm_config.channels)){ |
|
ALOGD("%s: null handle to write - device already closed",__func__); |
|
goto err; |
|
} |
|
|
|
if(Get_SinkSupported_format() == out->pcm_config.format){ |
|
|
|
/*16 bit data will be converted to 24 bit over 32 bit data type |
|
hence the multiplier 2*/ |
|
dstbuff = (int32_t*)malloc(bytes* 2); |
|
if (!dstbuff) { |
|
pthread_mutex_unlock(&out->lock); |
|
pthread_mutex_unlock(&out->dev->lock); |
|
ALOGE("%s : memory allocation failed",__func__); |
|
return -ENOMEM; |
|
} |
|
|
|
memset(dstbuff,0,bytes * 2); |
|
|
|
outbytes = make_sinkcompliant_buffers((void*)buffer, (void*)dstbuff,bytes); |
|
} //if()for conversion |
|
|
|
if(dstbuff){ |
|
ret = pcm_write(out->pcm, (void *)dstbuff, outbytes); |
|
} |
|
else |
|
ret = pcm_write(out->pcm, (void *)buffer, bytes); |
|
|
|
ALOGV("pcm_write: %s done for %d input bytes, output bytes = %d ", pcm_get_error(out->pcm),bytes,outbytes); |
|
|
|
free(dstbuff); |
|
|
|
err: |
|
pthread_mutex_unlock(&out->lock); |
|
pthread_mutex_unlock(&out->dev->lock); |
|
|
|
if(ret !=0){ |
|
uint64_t duration_ms = ((bytes * 1000)/ |
|
(audio_stream_out_frame_size(stream)) / |
|
(out_get_sample_rate(&stream->common))); |
|
ALOGV("%s : silence written", __func__); |
|
usleep(duration_ms * 1000); |
|
} |
|
|
|
ALOGV("%s exit",__func__); |
|
return bytes; |
|
} |
|
|
|
static int out_get_render_position(const struct audio_stream_out *stream, |
|
uint32_t *dsp_frames) |
|
{ |
|
UNUSED_PARAMETER(stream); |
|
UNUSED_PARAMETER(dsp_frames); |
|
|
|
return -EINVAL; |
|
} |
|
|
|
static int out_add_audio_effect(const struct audio_stream *stream, effect_handle_t effect) |
|
{ |
|
UNUSED_PARAMETER(stream); |
|
UNUSED_PARAMETER(effect); |
|
|
|
return 0; |
|
} |
|
|
|
static int out_remove_audio_effect(const struct audio_stream *stream, effect_handle_t effect) |
|
{ |
|
UNUSED_PARAMETER(stream); |
|
UNUSED_PARAMETER(effect); |
|
|
|
return 0; |
|
} |
|
|
|
static int out_get_next_write_timestamp(const struct audio_stream_out *stream, |
|
int64_t *timestamp) |
|
{ |
|
UNUSED_PARAMETER(stream); |
|
UNUSED_PARAMETER(timestamp); |
|
|
|
return -EINVAL; |
|
} |
|
|
|
static int adev_open_output_stream(struct audio_hw_device *dev, |
|
audio_io_handle_t handle, |
|
audio_devices_t devices, |
|
audio_output_flags_t flags, |
|
struct audio_config *config, |
|
struct audio_stream_out **stream_out, |
|
const char *address) |
|
{ |
|
UNUSED_PARAMETER(devices); |
|
UNUSED_PARAMETER(handle); |
|
UNUSED_PARAMETER(address); |
|
|
|
struct audio_device *adev = (struct audio_device *)dev; |
|
struct stream_out *out; |
|
int ret; |
|
ALOGV("%s enter",__func__); |
|
|
|
out = (struct stream_out *)calloc(1, sizeof(struct stream_out)); |
|
if (!out) |
|
return -ENOMEM; |
|
|
|
|
|
out->dev = adev; |
|
out->channel_mask = AUDIO_CHANNEL_OUT_STEREO; |
|
adev->sup_channel_masks[0] = AUDIO_CHANNEL_OUT_STEREO; |
|
|
|
if (flags & AUDIO_OUTPUT_FLAG_DIRECT) { |
|
ALOGV("%s: HDMI Multichannel",__func__); |
|
if (config->sample_rate == 0) |
|
config->sample_rate = pcm_config_default.rate; |
|
if (config->channel_mask == 0){ |
|
/*read the channel support from sink*/ |
|
out_read_edid(out); |
|
if(config->channel_mask == 0) |
|
config->channel_mask = AUDIO_CHANNEL_OUT_5POINT1; |
|
} |
|
} else { |
|
ALOGV("%s: HDMI Stereo",__func__); |
|
if (config->sample_rate == 0) |
|
config->sample_rate = pcm_config_default.rate; |
|
if (config->channel_mask == 0) |
|
config->channel_mask = AUDIO_CHANNEL_OUT_STEREO; |
|
} |
|
|
|
out->channel_mask = config->channel_mask; |
|
|
|
out->pcm_config.channels = popcount(config->channel_mask); |
|
out->pcm_config.rate = config->sample_rate; |
|
out->pcm_config.period_size = pcm_config_default.period_size; |
|
out->pcm_config.period_count = pcm_config_default.period_count; |
|
out->pcm_config.format = pcm_config_default.format; |
|
|
|
out->stream.common.get_sample_rate = out_get_sample_rate; |
|
out->stream.common.set_sample_rate = out_set_sample_rate; |
|
out->stream.common.get_buffer_size = out_get_buffer_size; |
|
out->stream.common.get_channels = out_get_channels; |
|
out->stream.common.get_format = out_get_format; |
|
out->stream.common.set_format = out_set_format; |
|
out->stream.common.standby = out_standby; |
|
out->stream.common.dump = out_dump; |
|
out->stream.common.set_parameters = out_set_parameters; |
|
out->stream.common.get_parameters = out_get_parameters; |
|
out->stream.common.add_audio_effect = out_add_audio_effect; |
|
out->stream.common.remove_audio_effect = out_remove_audio_effect; |
|
out->stream.get_latency = out_get_latency; |
|
out->stream.set_volume = out_set_volume; |
|
out->stream.write = out_write; |
|
out->stream.get_render_position = out_get_render_position; |
|
out->stream.get_next_write_timestamp = out_get_next_write_timestamp; |
|
|
|
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); |
|
|
|
out->standby = true; |
|
|
|
adev->card = -1; |
|
adev->device = -1; |
|
|
|
pthread_mutex_lock(&out->dev->lock); |
|
pthread_mutex_lock(&out->lock); |
|
|
|
if(activePcm){ |
|
ALOGV("Closing already open tiny alsa stream %d",(int)out->pcm); |
|
pcm_close(activePcm); |
|
activePcm = NULL; |
|
} |
|
ret = start_output_stream(out); |
|
if (ret != 0) { |
|
ALOGV("%s: stream start failed", __func__); |
|
goto err_open; |
|
} |
|
|
|
out->standby = false; |
|
|
|
*stream_out = &out->stream; |
|
|
|
pthread_mutex_unlock(&out->lock); |
|
pthread_mutex_unlock(&out->dev->lock); |
|
|
|
ALOGV("%s exit",__func__); |
|
return 0; |
|
|
|
err_open: |
|
ALOGE("%s exit with error",__func__); |
|
pthread_mutex_unlock(&out->lock); |
|
pthread_mutex_unlock(&out->dev->lock); |
|
free(out); |
|
*stream_out = NULL; |
|
return ret; |
|
} |
|
|
|
static void adev_close_output_stream(struct audio_hw_device *dev, |
|
struct audio_stream_out *stream) |
|
{ |
|
UNUSED_PARAMETER(dev); |
|
|
|
struct stream_out *out = (struct stream_out *)stream; |
|
|
|
ALOGV("%s enter",__func__); |
|
out->standby = false; |
|
out_standby(&stream->common); |
|
free(stream); |
|
ALOGV("%s exit",__func__); |
|
} |
|
|
|
static int adev_set_parameters(struct audio_hw_device *dev, const char *kvpairs) |
|
{ |
|
UNUSED_PARAMETER(dev); |
|
UNUSED_PARAMETER(kvpairs); |
|
|
|
return 0; |
|
} |
|
|
|
static char * adev_get_parameters(const struct audio_hw_device *dev, |
|
const char *keys) |
|
{ |
|
UNUSED_PARAMETER(dev); |
|
UNUSED_PARAMETER(keys); |
|
|
|
return strdup(""); |
|
} |
|
|
|
static int adev_init_check(const struct audio_hw_device *dev) |
|
{ |
|
UNUSED_PARAMETER(dev); |
|
|
|
return 0; |
|
} |
|
|
|
static int adev_set_voice_volume(struct audio_hw_device *dev, float volume) |
|
{ |
|
UNUSED_PARAMETER(dev); |
|
UNUSED_PARAMETER(volume); |
|
|
|
return -ENOSYS; |
|
} |
|
|
|
static int adev_set_master_volume(struct audio_hw_device *dev, float volume) |
|
{ |
|
UNUSED_PARAMETER(dev); |
|
UNUSED_PARAMETER(volume); |
|
|
|
return -ENOSYS; |
|
} |
|
|
|
static int adev_set_mode(struct audio_hw_device *dev, audio_mode_t mode) |
|
{ |
|
UNUSED_PARAMETER(dev); |
|
UNUSED_PARAMETER(mode); |
|
|
|
return 0; |
|
} |
|
|
|
static int adev_set_mic_mute(struct audio_hw_device *dev, bool state) |
|
{ |
|
UNUSED_PARAMETER(dev); |
|
UNUSED_PARAMETER(state); |
|
|
|
return -ENOSYS; |
|
} |
|
|
|
static int adev_get_mic_mute(const struct audio_hw_device *dev, bool *state) |
|
{ |
|
UNUSED_PARAMETER(dev); |
|
UNUSED_PARAMETER(state); |
|
|
|
return -ENOSYS; |
|
} |
|
|
|
static size_t adev_get_input_buffer_size(const struct audio_hw_device *dev, |
|
const struct audio_config *config) |
|
{ |
|
UNUSED_PARAMETER(dev); |
|
UNUSED_PARAMETER(config); |
|
|
|
return 0; |
|
} |
|
|
|
static int adev_open_input_stream(struct audio_hw_device *dev, |
|
audio_io_handle_t handle, |
|
audio_devices_t devices, |
|
struct audio_config *config, |
|
struct audio_stream_in **stream_in, |
|
audio_input_flags_t flags, |
|
const char *address, |
|
audio_source_t source) |
|
{ |
|
UNUSED_PARAMETER(dev); |
|
UNUSED_PARAMETER(handle); |
|
UNUSED_PARAMETER(devices); |
|
UNUSED_PARAMETER(config); |
|
UNUSED_PARAMETER(stream_in); |
|
UNUSED_PARAMETER(flags); |
|
UNUSED_PARAMETER(address); |
|
UNUSED_PARAMETER(source); |
|
|
|
return -ENOSYS; |
|
} |
|
|
|
static void adev_close_input_stream(struct audio_hw_device *dev, |
|
struct audio_stream_in *stream) |
|
{ |
|
UNUSED_PARAMETER(dev); |
|
UNUSED_PARAMETER(stream); |
|
} |
|
|
|
static int adev_dump(const audio_hw_device_t *device, int fd) |
|
{ |
|
UNUSED_PARAMETER(device); |
|
UNUSED_PARAMETER(fd); |
|
|
|
return 0; |
|
} |
|
|
|
static int adev_close(hw_device_t *device) |
|
{ |
|
free(device); |
|
return 0; |
|
} |
|
|
|
static int adev_open(const hw_module_t* module, const char* name, |
|
hw_device_t** device) |
|
{ |
|
struct audio_device *adev; |
|
|
|
ALOGV("%s enter",__func__); |
|
|
|
if (strcmp(name, AUDIO_HARDWARE_INTERFACE) != 0) |
|
return -EINVAL; |
|
|
|
adev = calloc(1, sizeof(struct audio_device)); |
|
if (!adev) |
|
return -ENOMEM; |
|
|
|
adev->hw_device.common.tag = HARDWARE_DEVICE_TAG; |
|
adev->hw_device.common.version = AUDIO_DEVICE_API_VERSION_2_0; |
|
adev->hw_device.common.module = (struct hw_module_t *) module; |
|
adev->hw_device.common.close = adev_close; |
|
|
|
adev->hw_device.init_check = adev_init_check; |
|
adev->hw_device.set_voice_volume = adev_set_voice_volume; |
|
adev->hw_device.set_master_volume = adev_set_master_volume; |
|
adev->hw_device.set_mode = adev_set_mode; |
|
adev->hw_device.set_mic_mute = adev_set_mic_mute; |
|
adev->hw_device.get_mic_mute = adev_get_mic_mute; |
|
adev->hw_device.set_parameters = adev_set_parameters; |
|
adev->hw_device.get_parameters = adev_get_parameters; |
|
adev->hw_device.get_input_buffer_size = adev_get_input_buffer_size; |
|
adev->hw_device.open_output_stream = adev_open_output_stream; |
|
adev->hw_device.close_output_stream = adev_close_output_stream; |
|
adev->hw_device.open_input_stream = adev_open_input_stream; |
|
adev->hw_device.close_input_stream = adev_close_input_stream; |
|
adev->hw_device.dump = adev_dump; |
|
|
|
*device = &adev->hw_device.common; |
|
|
|
ALOGV("%s exit",__func__); |
|
|
|
return 0; |
|
} |
|
|
|
static struct hw_module_methods_t hal_module_methods = { |
|
.open = adev_open, |
|
}; |
|
|
|
struct audio_module HAL_MODULE_INFO_SYM = { |
|
.common = { |
|
.tag = HARDWARE_MODULE_TAG, |
|
.module_api_version = AUDIO_MODULE_API_VERSION_0_1, |
|
.hal_api_version = HARDWARE_HAL_API_VERSION, |
|
.id = AUDIO_HARDWARE_MODULE_ID, |
|
.name = "tiny_hdmi audio HW HAL", |
|
.author = "The Android Open Source Project", |
|
.methods = &hal_module_methods, |
|
}, |
|
};
|
|
|