diff options
author | Praneeth Bajjuri | 2016-04-27 00:14:05 -0500 |
---|---|---|
committer | Praneeth Bajjuri | 2016-04-27 00:14:05 -0500 |
commit | ea94a53a31427ada1f8b6219b4cb0fc6398e5f52 (patch) | |
tree | f3141ed65262469d48dbcee65e79b3fb8178ffcb /audio | |
parent | e47dcceeac4e601f3203c61f5230c91fdb69e72d (diff) | |
download | device-ti-am57xevm-ea94a53a31427ada1f8b6219b4cb0fc6398e5f52.tar.gz device-ti-am57xevm-ea94a53a31427ada1f8b6219b4cb0fc6398e5f52.tar.xz device-ti-am57xevm-ea94a53a31427ada1f8b6219b4cb0fc6398e5f52.zip |
AM57xevm: Initial Device project baseline
Initial commit based on jacinto6evm board
Signed-off-by: Praneeth Bajjuri <praneeth@ti.com>
Diffstat (limited to 'audio')
-rw-r--r-- | audio/Android.mk | 16 | ||||
-rw-r--r-- | audio/audio_policy.conf | 102 | ||||
-rw-r--r-- | audio/hdmi/Android.mk | 40 | ||||
-rw-r--r-- | audio/hdmi/hdmi_audio_hal.h | 37 | ||||
-rw-r--r-- | audio/hdmi/hdmi_audio_hw.c | 824 | ||||
-rw-r--r-- | audio/hdmi/hdmi_audio_utils.c | 414 | ||||
-rw-r--r-- | audio/primary/Android.mk | 43 | ||||
-rw-r--r-- | audio/primary/audio_hw.c | 1766 | ||||
-rw-r--r-- | audio/primary/mixer_paths.xml | 31 |
9 files changed, 3273 insertions, 0 deletions
diff --git a/audio/Android.mk b/audio/Android.mk new file mode 100644 index 0000000..9e14b16 --- /dev/null +++ b/audio/Android.mk | |||
@@ -0,0 +1,16 @@ | |||
1 | # Copyright (C) 2015 Texas Instruments | ||
2 | # | ||
3 | # Licensed under the Apache License, Version 2.0 (the "License"); | ||
4 | # you may not use this file except in compliance with the License. | ||
5 | # You may obtain a copy of the License at | ||
6 | # | ||
7 | # http://www.apache.org/licenses/LICENSE-2.0 | ||
8 | # | ||
9 | # Unless required by applicable law or agreed to in writing, software | ||
10 | # distributed under the License is distributed on an "AS IS" BASIS, | ||
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
12 | # See the License for the specific language governing permissions and | ||
13 | # limitations under the License. | ||
14 | |||
15 | LOCAL_PATH := $(call my-dir) | ||
16 | include $(call all-makefiles-under,$(LOCAL_PATH)) | ||
diff --git a/audio/audio_policy.conf b/audio/audio_policy.conf new file mode 100644 index 0000000..f7a6f02 --- /dev/null +++ b/audio/audio_policy.conf | |||
@@ -0,0 +1,102 @@ | |||
1 | # Global configuration section: lists input and output devices always present on the device | ||
2 | # as well as the output device selected by default. | ||
3 | # Devices are designated by a string that corresponds to the enum in audio.h | ||
4 | |||
5 | global_configuration { | ||
6 | attached_output_devices AUDIO_DEVICE_OUT_SPEAKER | ||
7 | default_output_device AUDIO_DEVICE_OUT_SPEAKER | ||
8 | attached_input_devices AUDIO_DEVICE_IN_BUILTIN_MIC|AUDIO_DEVICE_IN_LINE | ||
9 | } | ||
10 | |||
11 | # audio hardware module section: contains descriptors for all audio hw modules present on the | ||
12 | # device. Each hw module node is named after the corresponding hw module library base name. | ||
13 | # For instance, "primary" corresponds to audio.primary.<device>.so. | ||
14 | # The "primary" module is mandatory and must include at least one output with | ||
15 | # AUDIO_OUTPUT_FLAG_PRIMARY flag. | ||
16 | # Each module descriptor contains one or more output profile descriptors and zero or more | ||
17 | # input profile descriptors. Each profile lists all the parameters supported by a given output | ||
18 | # or input stream category. | ||
19 | # The "channel_masks", "formats", "devices" and "flags" are specified using strings corresponding | ||
20 | # to enums in audio.h and audio_policy.h. They are concatenated by use of "|" without space or "\n". | ||
21 | |||
22 | audio_hw_modules { | ||
23 | primary { | ||
24 | outputs { | ||
25 | primary { | ||
26 | sampling_rates 44100 | ||
27 | channel_masks AUDIO_CHANNEL_OUT_STEREO | ||
28 | formats AUDIO_FORMAT_PCM_16_BIT | ||
29 | devices AUDIO_DEVICE_OUT_SPEAKER|AUDIO_DEVICE_OUT_WIRED_HEADSET|AUDIO_DEVICE_OUT_WIRED_HEADPHONE | ||
30 | flags AUDIO_OUTPUT_FLAG_PRIMARY | ||
31 | } | ||
32 | } | ||
33 | inputs { | ||
34 | primary { | ||
35 | sampling_rates 8000|11025|16000|22050|32000|44100|48000 | ||
36 | channel_masks AUDIO_CHANNEL_IN_MONO|AUDIO_CHANNEL_IN_STEREO | ||
37 | formats AUDIO_FORMAT_PCM_16_BIT | ||
38 | devices AUDIO_DEVICE_IN_BUILTIN_MIC | ||
39 | } | ||
40 | } | ||
41 | } | ||
42 | hdmi { | ||
43 | outputs { | ||
44 | stereo { | ||
45 | sampling_rates 44100|48000 | ||
46 | channel_masks AUDIO_CHANNEL_OUT_STEREO | ||
47 | formats AUDIO_FORMAT_PCM_16_BIT | ||
48 | devices AUDIO_DEVICE_OUT_AUX_DIGITAL | ||
49 | } | ||
50 | multichannel { | ||
51 | sampling_rates 44100|48000 | ||
52 | channel_masks dynamic | ||
53 | formats AUDIO_FORMAT_PCM_16_BIT | ||
54 | devices AUDIO_DEVICE_OUT_AUX_DIGITAL | ||
55 | flags AUDIO_OUTPUT_FLAG_DIRECT | ||
56 | } | ||
57 | } | ||
58 | } | ||
59 | a2dp { | ||
60 | outputs { | ||
61 | a2dp { | ||
62 | sampling_rates 44100 | ||
63 | channel_masks AUDIO_CHANNEL_OUT_STEREO | ||
64 | formats AUDIO_FORMAT_PCM_16_BIT | ||
65 | devices AUDIO_DEVICE_OUT_ALL_A2DP | ||
66 | } | ||
67 | } | ||
68 | inputs { | ||
69 | a2dp { | ||
70 | sampling_rates 44100|48000 | ||
71 | channel_masks AUDIO_CHANNEL_IN_MONO|AUDIO_CHANNEL_IN_STEREO | ||
72 | formats AUDIO_FORMAT_PCM_16_BIT | ||
73 | devices AUDIO_DEVICE_IN_BLUETOOTH_A2DP | ||
74 | } | ||
75 | } | ||
76 | } | ||
77 | r_submix { | ||
78 | outputs { | ||
79 | r_submix { | ||
80 | sampling_rates 44100|48000 | ||
81 | channel_masks AUDIO_CHANNEL_OUT_STEREO | ||
82 | formats AUDIO_FORMAT_PCM_16_BIT | ||
83 | devices AUDIO_DEVICE_OUT_REMOTE_SUBMIX | ||
84 | } | ||
85 | multichannel { | ||
86 | sampling_rates 44100|48000 | ||
87 | channel_masks AUDIO_CHANNEL_OUT_5POINT1 | ||
88 | formats AUDIO_FORMAT_PCM_16_BIT | ||
89 | devices AUDIO_DEVICE_OUT_REMOTE_SUBMIX | ||
90 | flags AUDIO_OUTPUT_FLAG_DIRECT | ||
91 | } | ||
92 | } | ||
93 | inputs { | ||
94 | r_submix { | ||
95 | sampling_rates 44100|48000 | ||
96 | channel_masks AUDIO_CHANNEL_IN_MONO|AUDIO_CHANNEL_IN_STEREO|AUDIO_CHANNEL_IN_5POINT1EMUL | ||
97 | formats AUDIO_FORMAT_PCM_16_BIT | ||
98 | devices AUDIO_DEVICE_IN_REMOTE_SUBMIX | ||
99 | } | ||
100 | } | ||
101 | } | ||
102 | } | ||
diff --git a/audio/hdmi/Android.mk b/audio/hdmi/Android.mk new file mode 100644 index 0000000..976f84b --- /dev/null +++ b/audio/hdmi/Android.mk | |||
@@ -0,0 +1,40 @@ | |||
1 | # Copyright (C) 2015 Texas Instruments | ||
2 | # | ||
3 | # Licensed under the Apache License, Version 2.0 (the "License"); | ||
4 | # you may not use this file except in compliance with the License. | ||
5 | # You may obtain a copy of the License at | ||
6 | # | ||
7 | # http://www.apache.org/licenses/LICENSE-2.0 | ||
8 | # | ||
9 | # Unless required by applicable law or agreed to in writing, software | ||
10 | # distributed under the License is distributed on an "AS IS" BASIS, | ||
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
12 | # See the License for the specific language governing permissions and | ||
13 | # limitations under the License. | ||
14 | |||
15 | LOCAL_PATH := $(call my-dir) | ||
16 | |||
17 | ifeq ($(TARGET_BOARD_PLATFORM), $(filter $(TARGET_BOARD_PLATFORM), am57x)) | ||
18 | |||
19 | include $(CLEAR_VARS) | ||
20 | |||
21 | LOCAL_MODULE := audio.hdmi.$(TARGET_BOARD_PLATFORM) | ||
22 | |||
23 | LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw | ||
24 | LOCAL_SRC_FILES := hdmi_audio_hw.c \ | ||
25 | hdmi_audio_utils.c | ||
26 | |||
27 | LOCAL_C_INCLUDES += \ | ||
28 | external/tinyalsa/include \ | ||
29 | external/libdrm \ | ||
30 | external/libdrm/include/drm \ | ||
31 | system/media/audio_utils/include \ | ||
32 | system/media/audio_effects/include \ | ||
33 | frameworks/native/include/media/openmax | ||
34 | |||
35 | LOCAL_SHARED_LIBRARIES := liblog libcutils libtinyalsa libaudioutils libdl libdrm | ||
36 | LOCAL_MODULE_TAGS := optional | ||
37 | |||
38 | include $(BUILD_SHARED_LIBRARY) | ||
39 | |||
40 | endif | ||
diff --git a/audio/hdmi/hdmi_audio_hal.h b/audio/hdmi/hdmi_audio_hal.h new file mode 100644 index 0000000..545e50e --- /dev/null +++ b/audio/hdmi/hdmi_audio_hal.h | |||
@@ -0,0 +1,37 @@ | |||
1 | /* -*- mode: C; c-file-style: "stroustrup"; indent-tabs-mode: nil; -*- */ | ||
2 | /* | ||
3 | * Copyright (C) 2012 Texas Instruments | ||
4 | * | ||
5 | * Licensed under the Apache License, Version 2.0 (the "License"); | ||
6 | * you may not use this file except in compliance with the License. | ||
7 | * You may obtain a copy of the License at | ||
8 | * | ||
9 | * http://www.apache.org/licenses/LICENSE-2.0 | ||
10 | * | ||
11 | * Unless required by applicable law or agreed to in writing, software | ||
12 | * distributed under the License is distributed on an "AS IS" BASIS, | ||
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
14 | * See the License for the specific language governing permissions and | ||
15 | * limitations under the License. | ||
16 | */ | ||
17 | #ifndef TI_HDMI_AUDIO_HAL | ||
18 | #define TI_HDMI_AUDIO_HAL | ||
19 | |||
20 | typedef struct _hdmi_audio_caps { | ||
21 | int has_audio; | ||
22 | int speaker_alloc; | ||
23 | } hdmi_audio_caps_t; | ||
24 | |||
25 | /* Speaker allocation bits */ | ||
26 | #define CEA_SPKR_FLFR (1 << 0) | ||
27 | #define CEA_SPKR_LFE (1 << 1) | ||
28 | #define CEA_SPKR_FC (1 << 2) | ||
29 | #define CEA_SPKR_RLRR (1 << 3) | ||
30 | #define CEA_SPKR_RC (1 << 4) | ||
31 | #define CEA_SPKR_FLCFRC (1 << 5) | ||
32 | #define CEA_SPKR_RLCRRC (1 << 6) | ||
33 | |||
34 | /* Defined in file hdmi_audio_utils.c */ | ||
35 | int hdmi_query_audio_caps(hdmi_audio_caps_t *caps); | ||
36 | |||
37 | #endif /* TI_HDMI_AUDIO_HAL */ | ||
diff --git a/audio/hdmi/hdmi_audio_hw.c b/audio/hdmi/hdmi_audio_hw.c new file mode 100644 index 0000000..1f5c520 --- /dev/null +++ b/audio/hdmi/hdmi_audio_hw.c | |||
@@ -0,0 +1,824 @@ | |||
1 | /* -*- mode: C; c-file-style: "stroustrup"; indent-tabs-mode: nil; -*- */ | ||
2 | /* | ||
3 | * Copyright (C) 2012 Texas Instruments | ||
4 | * | ||
5 | * Licensed under the Apache License, Version 2.0 (the "License"); | ||
6 | * you may not use this file except in compliance with the License. | ||
7 | * You may obtain a copy of the License at | ||
8 | * | ||
9 | * http://www.apache.org/licenses/LICENSE-2.0 | ||
10 | * | ||
11 | * Unless required by applicable law or agreed to in writing, software | ||
12 | * distributed under the License is distributed on an "AS IS" BASIS, | ||
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
14 | * See the License for the specific language governing permissions and | ||
15 | * limitations under the License. | ||
16 | */ | ||
17 | |||
18 | #define LOG_TAG "hdmi_audio_hw" | ||
19 | /* #define LOG_NDEBUG 0 */ | ||
20 | /* #define LOG_TRACE_FUNCTION */ | ||
21 | |||
22 | #ifndef LOG_TRACE_FUNCTION | ||
23 | #define TRACE() ((void)0) | ||
24 | #define TRACEM(fmt, ...) ((void)0) | ||
25 | #else | ||
26 | #define tfmt(x) x | ||
27 | #define TRACE() (ALOGV("%s() %s:%d", __func__, __FILE__, __LINE__)) | ||
28 | #define TRACEM(fmt, ...) (ALOGV("%s() " tfmt(fmt) " %s:%d", __func__, ##__VA_ARGS__, __FILE__, __LINE__)) | ||
29 | #endif | ||
30 | |||
31 | #include <errno.h> | ||
32 | #include <pthread.h> | ||
33 | #include <stdint.h> | ||
34 | #include <sys/time.h> | ||
35 | #include <stdlib.h> | ||
36 | #include <stdio.h> | ||
37 | |||
38 | #include <cutils/log.h> | ||
39 | #include <cutils/str_parms.h> | ||
40 | #include <cutils/properties.h> | ||
41 | |||
42 | #include <hardware/hardware.h> | ||
43 | #include <system/audio.h> | ||
44 | #include <hardware/audio.h> | ||
45 | |||
46 | #include <tinyalsa/asoundlib.h> | ||
47 | |||
48 | #include <OMX_Audio.h> | ||
49 | |||
50 | #include "hdmi_audio_hal.h" | ||
51 | |||
52 | #define UNUSED(x) (void)(x) | ||
53 | |||
54 | /* XXX TODO: Dynamically detect the HDMI card | ||
55 | * E.g. if a USB device is plugged in at boot time, | ||
56 | * it sometimes takes the card #1 slot and puts us | ||
57 | * on card #2. | ||
58 | */ | ||
59 | #define HDMI_PCM_CARD 1 | ||
60 | #define HDMI_PCM_DEV 0 | ||
61 | #define HDMI_SAMPLING_RATE 44100 | ||
62 | #define HDMI_PERIOD_SIZE 1920 | ||
63 | #define HDMI_PERIOD_COUNT 4 | ||
64 | #define HDMI_MAX_CHANNELS 8 | ||
65 | |||
66 | typedef struct _hdmi_device { | ||
67 | audio_hw_device_t device; | ||
68 | int map[HDMI_MAX_CHANNELS]; | ||
69 | bool CEAMap; | ||
70 | } hdmi_device_t; | ||
71 | |||
72 | int cea_channel_map[HDMI_MAX_CHANNELS] = {OMX_AUDIO_ChannelLF,OMX_AUDIO_ChannelRF,OMX_AUDIO_ChannelLFE, | ||
73 | OMX_AUDIO_ChannelCF,OMX_AUDIO_ChannelLS,OMX_AUDIO_ChannelRS, | ||
74 | OMX_AUDIO_ChannelLR,OMX_AUDIO_ChannelRR}; /*Using OMX_AUDIO_CHANNELTYPE mapping*/ | ||
75 | |||
76 | typedef struct _hdmi_out { | ||
77 | audio_stream_out_t stream_out; | ||
78 | hdmi_device_t *dev; | ||
79 | struct pcm_config config; | ||
80 | struct pcm *pcm; | ||
81 | audio_config_t android_config; | ||
82 | int up; | ||
83 | void *buffcpy; | ||
84 | } hdmi_out_t; | ||
85 | |||
86 | #define S16_SIZE sizeof(int16_t) | ||
87 | |||
88 | |||
89 | /***************************************************************** | ||
90 | * UTILITY FUNCTIONS | ||
91 | ***************************************************************** | ||
92 | */ | ||
93 | |||
94 | /***************************************************************** | ||
95 | * AUDIO STREAM OUT (hdmi_out_*) DEFINITION | ||
96 | ***************************************************************** | ||
97 | */ | ||
98 | |||
99 | uint32_t hdmi_out_get_sample_rate(const struct audio_stream *stream) | ||
100 | { | ||
101 | hdmi_out_t *out = (hdmi_out_t*)stream; | ||
102 | struct pcm_config *config = &out->config; | ||
103 | TRACEM("stream=%p returning %d", stream, config->rate); | ||
104 | return config->rate; | ||
105 | } | ||
106 | |||
107 | /* DEPRECATED API */ | ||
108 | int hdmi_out_set_sample_rate(struct audio_stream *stream, uint32_t rate) | ||
109 | { | ||
110 | TRACE(); | ||
111 | UNUSED(stream); | ||
112 | UNUSED(rate); | ||
113 | |||
114 | return -EINVAL; | ||
115 | } | ||
116 | |||
117 | /* Returns bytes for ONE PERIOD */ | ||
118 | size_t hdmi_out_get_buffer_size(const struct audio_stream *stream) | ||
119 | { | ||
120 | const struct audio_stream_out *s = (const struct audio_stream_out *)stream; | ||
121 | hdmi_out_t *out = (hdmi_out_t*)stream; | ||
122 | struct pcm_config *config = &out->config; | ||
123 | size_t ans; | ||
124 | |||
125 | ans = audio_stream_out_frame_size(s) * config->period_size; | ||
126 | |||
127 | TRACEM("stream=%p returning %u", stream, ans); | ||
128 | |||
129 | return ans; | ||
130 | } | ||
131 | |||
132 | audio_channel_mask_t hdmi_out_get_channels(const struct audio_stream *stream) | ||
133 | { | ||
134 | hdmi_out_t *out = (hdmi_out_t*)stream; | ||
135 | TRACEM("stream=%p returning %x", stream, out->android_config.channel_mask); | ||
136 | return out->android_config.channel_mask; | ||
137 | } | ||
138 | |||
139 | audio_format_t hdmi_out_get_format(const struct audio_stream *stream) | ||
140 | { | ||
141 | hdmi_out_t *out = (hdmi_out_t*)stream; | ||
142 | TRACEM("stream=%p returning %x", stream, out->android_config.format); | ||
143 | return out->android_config.format; | ||
144 | } | ||
145 | |||
146 | /* DEPRECATED API */ | ||
147 | int hdmi_out_set_format(struct audio_stream *stream, audio_format_t format) | ||
148 | { | ||
149 | TRACE(); | ||
150 | UNUSED(stream); | ||
151 | UNUSED(format); | ||
152 | |||
153 | return -EINVAL; | ||
154 | } | ||
155 | |||
156 | int hdmi_out_standby(struct audio_stream *stream) | ||
157 | { | ||
158 | hdmi_out_t *out = (hdmi_out_t*)stream; | ||
159 | |||
160 | TRACEM("stream=%p", stream); | ||
161 | |||
162 | if (out->up && out->pcm) { | ||
163 | out->up = 0; | ||
164 | pcm_close(out->pcm); | ||
165 | out->pcm = 0; | ||
166 | } | ||
167 | |||
168 | return 0; | ||
169 | } | ||
170 | |||
171 | int hdmi_out_dump(const struct audio_stream *stream, int fd) | ||
172 | { | ||
173 | TRACE(); | ||
174 | UNUSED(stream); | ||
175 | UNUSED(fd); | ||
176 | |||
177 | return 0; | ||
178 | } | ||
179 | |||
180 | audio_devices_t hdmi_out_get_device(const struct audio_stream *stream) | ||
181 | { | ||
182 | TRACEM("stream=%p", stream); | ||
183 | UNUSED(stream); | ||
184 | |||
185 | return AUDIO_DEVICE_OUT_AUX_DIGITAL; | ||
186 | } | ||
187 | |||
188 | /* DEPRECATED API */ | ||
189 | int hdmi_out_set_device(struct audio_stream *stream, audio_devices_t device) | ||
190 | { | ||
191 | TRACE(); | ||
192 | UNUSED(stream); | ||
193 | UNUSED(device); | ||
194 | |||
195 | return 0; | ||
196 | } | ||
197 | |||
198 | int hdmi_out_set_parameters(struct audio_stream *stream, const char *kv_pairs) | ||
199 | { | ||
200 | TRACEM("stream=%p kv_pairs='%s'", stream, kv_pairs); | ||
201 | UNUSED(stream); | ||
202 | UNUSED(kv_pairs); | ||
203 | |||
204 | return 0; | ||
205 | } | ||
206 | |||
207 | #define MASK_CEA_QUAD ( CEA_SPKR_FLFR | CEA_SPKR_RLRR ) | ||
208 | #define MASK_CEA_SURROUND ( CEA_SPKR_FLFR | CEA_SPKR_FC | CEA_SPKR_RC ) | ||
209 | #define MASK_CEA_5POINT1 ( CEA_SPKR_FLFR | CEA_SPKR_FC | CEA_SPKR_LFE | CEA_SPKR_RLRR ) | ||
210 | #define MASK_CEA_7POINT1 ( CEA_SPKR_FLFR | CEA_SPKR_FC | CEA_SPKR_LFE | CEA_SPKR_RLRR | CEA_SPKR_RLCRRC ) | ||
211 | #define SUPPORTS_ARR(spkalloc, profile) (((spkalloc) & (profile)) == (profile)) | ||
212 | |||
213 | char * hdmi_out_get_parameters(const struct audio_stream *stream, | ||
214 | const char *keys) | ||
215 | { | ||
216 | struct str_parms *query = str_parms_create_str(keys); | ||
217 | char *str; | ||
218 | char value[256]; | ||
219 | struct str_parms *reply = str_parms_create(); | ||
220 | int status; | ||
221 | hdmi_audio_caps_t caps; | ||
222 | |||
223 | TRACEM("stream=%p keys='%s'", stream, keys); | ||
224 | UNUSED(stream); | ||
225 | |||
226 | if (hdmi_query_audio_caps(&caps)) { | ||
227 | ALOGE("Unable to get the HDMI audio capabilities"); | ||
228 | str = calloc(1, 1); | ||
229 | goto end; | ||
230 | } | ||
231 | |||
232 | status = str_parms_get_str(query, AUDIO_PARAMETER_STREAM_SUP_CHANNELS, | ||
233 | value, sizeof(value)); | ||
234 | if (status >= 0) { | ||
235 | unsigned sa = caps.speaker_alloc; | ||
236 | bool first = true; | ||
237 | |||
238 | /* STEREO is intentionally skipped. This code is only | ||
239 | * executed for the 'DIRECT' interface, and we don't | ||
240 | * want stereo on a DIRECT thread. | ||
241 | */ | ||
242 | value[0] = '\0'; | ||
243 | if (SUPPORTS_ARR(sa, MASK_CEA_QUAD)) { | ||
244 | if (!first) { | ||
245 | strcat(value, "|"); | ||
246 | } | ||
247 | first = false; | ||
248 | strcat(value, "AUDIO_CHANNEL_OUT_QUAD"); | ||
249 | } | ||
250 | if (SUPPORTS_ARR(sa, MASK_CEA_SURROUND)) { | ||
251 | if (!first) { | ||
252 | strcat(value, "|"); | ||
253 | } | ||
254 | first = false; | ||
255 | strcat(value, "AUDIO_CHANNEL_OUT_SURROUND"); | ||
256 | } | ||
257 | if (SUPPORTS_ARR(sa, MASK_CEA_5POINT1)) { | ||
258 | if (!first) { | ||
259 | strcat(value, "|"); | ||
260 | } | ||
261 | first = false; | ||
262 | strcat(value, "AUDIO_CHANNEL_OUT_5POINT1"); | ||
263 | } | ||
264 | if (SUPPORTS_ARR(sa, MASK_CEA_7POINT1)) { | ||
265 | if (!first) { | ||
266 | strcat(value, "|"); | ||
267 | } | ||
268 | first = false; | ||
269 | strcat(value, "AUDIO_CHANNEL_OUT_7POINT1"); | ||
270 | } | ||
271 | str_parms_add_str(reply, AUDIO_PARAMETER_STREAM_SUP_CHANNELS, value); | ||
272 | str = strdup(str_parms_to_str(reply)); | ||
273 | } else { | ||
274 | str = strdup(keys); | ||
275 | } | ||
276 | |||
277 | ALOGV("%s() reply: '%s'", __func__, str); | ||
278 | |||
279 | end: | ||
280 | str_parms_destroy(query); | ||
281 | str_parms_destroy(reply); | ||
282 | return str; | ||
283 | } | ||
284 | int hdmi_out_add_audio_effect(const struct audio_stream *stream, | ||
285 | effect_handle_t effect) | ||
286 | { | ||
287 | TRACE(); | ||
288 | UNUSED(stream); | ||
289 | UNUSED(effect); | ||
290 | |||
291 | return 0; | ||
292 | } | ||
293 | int hdmi_out_remove_audio_effect(const struct audio_stream *stream, | ||
294 | effect_handle_t effect) | ||
295 | { | ||
296 | TRACE(); | ||
297 | UNUSED(stream); | ||
298 | UNUSED(effect); | ||
299 | |||
300 | return 0; | ||
301 | } | ||
302 | |||
303 | /* returns milliseconds */ | ||
304 | uint32_t hdmi_out_get_latency(const struct audio_stream_out *stream) | ||
305 | { | ||
306 | uint32_t latency; | ||
307 | hdmi_out_t *out = (hdmi_out_t*)stream; | ||
308 | struct pcm_config *config = &out->config; | ||
309 | |||
310 | TRACEM("stream=%p", stream); | ||
311 | |||
312 | return (1000 * config->period_size * config->period_count) / config->rate; | ||
313 | } | ||
314 | |||
315 | int hdmi_out_set_volume(struct audio_stream_out *stream, float left, float right) | ||
316 | { | ||
317 | TRACE(); | ||
318 | UNUSED(stream); | ||
319 | UNUSED(left); | ||
320 | UNUSED(right); | ||
321 | |||
322 | return -ENOSYS; | ||
323 | } | ||
324 | |||
325 | static int hdmi_out_open_pcm(hdmi_out_t *out) | ||
326 | { | ||
327 | int card = HDMI_PCM_CARD; | ||
328 | int dev = HDMI_PCM_DEV; | ||
329 | int ret; | ||
330 | |||
331 | TRACEM("out=%p", out); | ||
332 | |||
333 | /* out->up must be 0 (down) */ | ||
334 | if (out->up) { | ||
335 | ALOGE("Trying to open a PCM that's already up. " | ||
336 | "This will probably deadlock... so aborting"); | ||
337 | return 0; | ||
338 | } | ||
339 | |||
340 | out->pcm = pcm_open(card, dev, PCM_OUT, &out->config); | ||
341 | |||
342 | if(out->pcm && pcm_is_ready(out->pcm)) { | ||
343 | out->up = 1; | ||
344 | ret = 0; | ||
345 | } else { | ||
346 | ALOGE("cannot open HDMI pcm card %d dev %d error: %s", | ||
347 | card, dev, pcm_get_error(out->pcm)); | ||
348 | pcm_close(out->pcm); | ||
349 | out->pcm = 0; | ||
350 | out->up = 0; | ||
351 | ret = 1; | ||
352 | } | ||
353 | |||
354 | return ret; | ||
355 | } | ||
356 | |||
357 | void channel_remap(struct audio_stream_out *stream, const void *buffer, | ||
358 | size_t bytes) | ||
359 | { | ||
360 | hdmi_out_t *out = (hdmi_out_t*)stream; | ||
361 | hdmi_device_t *adev = out->dev; | ||
362 | int x, y, frames; | ||
363 | int16_t *buf = (int16_t *)buffer; | ||
364 | int16_t *tmp_buf = (int16_t *)out->buffcpy; | ||
365 | |||
366 | frames = bytes / audio_stream_out_frame_size(stream); | ||
367 | while (frames--){ | ||
368 | for(y = 0; y < (int)out->config.channels; y++){ | ||
369 | for(x = 0; x < (int)out->config.channels; x++){ | ||
370 | if (cea_channel_map[y] == adev->map[x]){ | ||
371 | tmp_buf[y] = buf[x]; | ||
372 | break; | ||
373 | } | ||
374 | } | ||
375 | } | ||
376 | tmp_buf += (int)out->config.channels; | ||
377 | buf += (int)out->config.channels; | ||
378 | } | ||
379 | } | ||
380 | |||
381 | ssize_t hdmi_out_write(struct audio_stream_out *stream, const void* buffer, | ||
382 | size_t bytes) | ||
383 | { | ||
384 | hdmi_out_t *out = (hdmi_out_t*)stream; | ||
385 | hdmi_device_t *adev = out->dev; | ||
386 | ssize_t ret; | ||
387 | |||
388 | TRACEM("stream=%p buffer=%p bytes=%d", stream, buffer, bytes); | ||
389 | |||
390 | if (!out->up) { | ||
391 | if(hdmi_out_open_pcm(out)) { | ||
392 | ret = -ENOSYS; | ||
393 | goto exit; | ||
394 | } | ||
395 | } | ||
396 | |||
397 | if (out->config.channels > 2 && !adev->CEAMap){ | ||
398 | channel_remap(stream, buffer, bytes); | ||
399 | ret = pcm_write(out->pcm, out->buffcpy, bytes); | ||
400 | } else { | ||
401 | ret = pcm_write(out->pcm, buffer, bytes); | ||
402 | } | ||
403 | exit: | ||
404 | if (ret != 0) { | ||
405 | ALOGE("Error writing to HDMI pcm: %s", | ||
406 | out->pcm ? pcm_get_error(out->pcm) : "failed to open PCM device"); | ||
407 | hdmi_out_standby((struct audio_stream*)stream); | ||
408 | unsigned int usecs = bytes * 1000000 / | ||
409 | audio_stream_out_frame_size(stream) / | ||
410 | hdmi_out_get_sample_rate((struct audio_stream*)stream); | ||
411 | if (usecs >= 1000000L) { | ||
412 | usecs = 999999L; | ||
413 | } | ||
414 | usleep(usecs); | ||
415 | } | ||
416 | |||
417 | return bytes; | ||
418 | } | ||
419 | |||
420 | |||
421 | int hdmi_out_get_render_position(const struct audio_stream_out *stream, | ||
422 | uint32_t *dsp_frames) | ||
423 | { | ||
424 | TRACE(); | ||
425 | UNUSED(stream); | ||
426 | UNUSED(dsp_frames); | ||
427 | |||
428 | return -EINVAL; | ||
429 | } | ||
430 | |||
431 | int hdmi_out_get_next_write_timestamp(const struct audio_stream_out *stream, | ||
432 | int64_t *timestamp) | ||
433 | { | ||
434 | TRACE(); | ||
435 | UNUSED(stream); | ||
436 | UNUSED(timestamp); | ||
437 | |||
438 | return -EINVAL; | ||
439 | } | ||
440 | |||
441 | |||
442 | |||
443 | audio_stream_out_t hdmi_stream_out_descriptor = { | ||
444 | .common = { | ||
445 | .get_sample_rate = hdmi_out_get_sample_rate, | ||
446 | .set_sample_rate = hdmi_out_set_sample_rate, | ||
447 | .get_buffer_size = hdmi_out_get_buffer_size, | ||
448 | .get_channels = hdmi_out_get_channels, | ||
449 | .get_format = hdmi_out_get_format, | ||
450 | .set_format = hdmi_out_set_format, | ||
451 | .standby = hdmi_out_standby, | ||
452 | .dump = hdmi_out_dump, | ||
453 | .get_device = hdmi_out_get_device, | ||
454 | .set_device = hdmi_out_set_device, | ||
455 | .set_parameters = hdmi_out_set_parameters, | ||
456 | .get_parameters = hdmi_out_get_parameters, | ||
457 | .add_audio_effect = hdmi_out_add_audio_effect, | ||
458 | .remove_audio_effect = hdmi_out_remove_audio_effect, | ||
459 | }, | ||
460 | .get_latency = hdmi_out_get_latency, | ||
461 | .set_volume = hdmi_out_set_volume, | ||
462 | .write = hdmi_out_write, | ||
463 | .get_render_position = hdmi_out_get_render_position, | ||
464 | .get_next_write_timestamp = hdmi_out_get_next_write_timestamp, | ||
465 | }; | ||
466 | |||
467 | /***************************************************************** | ||
468 | * AUDIO DEVICE (hdmi_adev_*) DEFINITION | ||
469 | ***************************************************************** | ||
470 | */ | ||
471 | |||
472 | static int hdmi_adev_close(struct hw_device_t *device) | ||
473 | { | ||
474 | TRACE(); | ||
475 | UNUSED(device); | ||
476 | |||
477 | return 0; | ||
478 | } | ||
479 | |||
480 | static uint32_t hdmi_adev_get_supported_devices(const audio_hw_device_t *dev) | ||
481 | { | ||
482 | TRACE(); | ||
483 | UNUSED(dev); | ||
484 | |||
485 | return AUDIO_DEVICE_OUT_AUX_DIGITAL; | ||
486 | } | ||
487 | |||
488 | static int hdmi_adev_init_check(const audio_hw_device_t *dev) | ||
489 | { | ||
490 | TRACE(); | ||
491 | UNUSED(dev); | ||
492 | |||
493 | return 0; | ||
494 | } | ||
495 | |||
496 | static int hdmi_adev_set_voice_volume(audio_hw_device_t *dev, float volume) | ||
497 | { | ||
498 | TRACE(); | ||
499 | UNUSED(dev); | ||
500 | UNUSED(volume); | ||
501 | |||
502 | return -ENOSYS; | ||
503 | } | ||
504 | |||
505 | static int hdmi_adev_set_master_volume(audio_hw_device_t *dev, float volume) | ||
506 | { | ||
507 | TRACE(); | ||
508 | UNUSED(dev); | ||
509 | UNUSED(volume); | ||
510 | |||
511 | return -ENOSYS; | ||
512 | } | ||
513 | |||
514 | static int hdmi_adev_get_master_volume(audio_hw_device_t *dev, float *volume) | ||
515 | { | ||
516 | TRACE(); | ||
517 | UNUSED(dev); | ||
518 | UNUSED(volume); | ||
519 | |||
520 | return -ENOSYS; | ||
521 | } | ||
522 | |||
523 | static int hdmi_adev_set_mode(audio_hw_device_t *dev, audio_mode_t mode) | ||
524 | { | ||
525 | TRACE(); | ||
526 | UNUSED(dev); | ||
527 | UNUSED(mode); | ||
528 | |||
529 | return 0; | ||
530 | } | ||
531 | |||
532 | static int hdmi_adev_set_mic_mute(audio_hw_device_t *dev, bool state) | ||
533 | { | ||
534 | TRACE(); | ||
535 | UNUSED(dev); | ||
536 | UNUSED(state); | ||
537 | |||
538 | return -ENOSYS; | ||
539 | } | ||
540 | |||
541 | static int hdmi_adev_get_mic_mute(const audio_hw_device_t *dev, bool *state) | ||
542 | { | ||
543 | TRACE(); | ||
544 | UNUSED(dev); | ||
545 | UNUSED(state); | ||
546 | |||
547 | return -ENOSYS; | ||
548 | } | ||
549 | |||
550 | static int hdmi_adev_set_parameters(audio_hw_device_t *dev, const char *kv_pairs) | ||
551 | { | ||
552 | TRACEM("dev=%p kv_pairss='%s'", dev, kv_pairs); | ||
553 | |||
554 | struct str_parms *params; | ||
555 | char *str; | ||
556 | char value[HDMI_MAX_CHANNELS]; | ||
557 | int ret, x, val, numMatch = 0; | ||
558 | hdmi_device_t *adev = (hdmi_device_t *)dev; | ||
559 | |||
560 | params = str_parms_create_str(kv_pairs); | ||
561 | //Handle maximum of 8 channels | ||
562 | ret = str_parms_get_str(params, "channel_map", value, HDMI_MAX_CHANNELS); | ||
563 | if (ret >= 0) { | ||
564 | val = strtol(value, NULL, 10); | ||
565 | for(x = 0; x < HDMI_MAX_CHANNELS; x++) { | ||
566 | adev->map[x] = (val & (0xF << x*4)) >> x*4; | ||
567 | if (adev->map[x] == cea_channel_map[x]) | ||
568 | numMatch += 1; | ||
569 | } | ||
570 | if (numMatch >= 5) | ||
571 | adev->CEAMap = true; | ||
572 | else | ||
573 | adev->CEAMap = false; | ||
574 | } | ||
575 | return 0; | ||
576 | } | ||
577 | |||
578 | static char* hdmi_adev_get_parameters(const audio_hw_device_t *dev, | ||
579 | const char *keys) | ||
580 | { | ||
581 | TRACEM("dev=%p keys='%s'", dev, keys); | ||
582 | UNUSED(dev); | ||
583 | UNUSED(keys); | ||
584 | |||
585 | return NULL; | ||
586 | } | ||
587 | |||
588 | static size_t hdmi_adev_get_input_buffer_size(const audio_hw_device_t *dev, | ||
589 | const struct audio_config *config) | ||
590 | { | ||
591 | TRACE(); | ||
592 | UNUSED(dev); | ||
593 | UNUSED(config); | ||
594 | |||
595 | return 0; | ||
596 | } | ||
597 | |||
598 | #define DUMP_FLAG(flags, flag) { \ | ||
599 | if ((flags) & (flag)) { \ | ||
600 | ALOGV("set: " #flag); \ | ||
601 | } else { \ | ||
602 | ALOGV("unset: " #flag); \ | ||
603 | } \ | ||
604 | } | ||
605 | |||
606 | static int hdmi_adev_open_output_stream(audio_hw_device_t *dev, | ||
607 | audio_io_handle_t handle, | ||
608 | audio_devices_t devices, | ||
609 | audio_output_flags_t flags, | ||
610 | struct audio_config *config, | ||
611 | struct audio_stream_out **stream_out, | ||
612 | const char *address) | ||
613 | { | ||
614 | hdmi_out_t *out = 0; | ||
615 | struct pcm_config *pcm_config = 0; | ||
616 | struct audio_config *a_config = 0; | ||
617 | |||
618 | TRACE(); | ||
619 | UNUSED(handle); | ||
620 | UNUSED(devices); | ||
621 | UNUSED(flags); | ||
622 | UNUSED(address); | ||
623 | |||
624 | out = calloc(1, sizeof(hdmi_out_t)); | ||
625 | if (!out) { | ||
626 | return -ENOMEM; | ||
627 | } | ||
628 | |||
629 | out->dev = (hdmi_device_t *)dev; | ||
630 | memcpy(&out->stream_out, &hdmi_stream_out_descriptor, | ||
631 | sizeof(audio_stream_out_t)); | ||
632 | memcpy(&out->android_config, config, sizeof(audio_config_t)); | ||
633 | |||
634 | pcm_config = &out->config; | ||
635 | a_config = &out->android_config; | ||
636 | |||
637 | #if defined(LOG_NDEBUG) && (LOG_NDEBUG == 0) | ||
638 | /* Analyze flags */ | ||
639 | if (flags) { | ||
640 | DUMP_FLAG(flags, AUDIO_OUTPUT_FLAG_DIRECT) | ||
641 | DUMP_FLAG(flags, AUDIO_OUTPUT_FLAG_PRIMARY) | ||
642 | DUMP_FLAG(flags, AUDIO_OUTPUT_FLAG_FAST) | ||
643 | DUMP_FLAG(flags, AUDIO_OUTPUT_FLAG_DEEP_BUFFER) | ||
644 | } else { | ||
645 | ALOGV("flags == AUDIO_OUTPUT_FLAG_NONE (0)"); | ||
646 | } | ||
647 | #endif /* defined(LOG_NDEBUG) && (LOG_NDEBUG == 0) */ | ||
648 | /* Initialize the PCM Configuration */ | ||
649 | pcm_config->period_size = HDMI_PERIOD_SIZE; | ||
650 | pcm_config->period_count = HDMI_PERIOD_COUNT; | ||
651 | |||
652 | if (a_config->sample_rate) { | ||
653 | pcm_config->rate = config->sample_rate; | ||
654 | } else { | ||
655 | pcm_config->rate = HDMI_SAMPLING_RATE; | ||
656 | a_config->sample_rate = HDMI_SAMPLING_RATE; | ||
657 | } | ||
658 | |||
659 | switch (a_config->format) { | ||
660 | case AUDIO_FORMAT_DEFAULT: | ||
661 | a_config->format = AUDIO_FORMAT_PCM_16_BIT; | ||
662 | /* fall through */ | ||
663 | case AUDIO_FORMAT_PCM_16_BIT: | ||
664 | pcm_config->format = PCM_FORMAT_S16_LE; | ||
665 | break; | ||
666 | default: | ||
667 | ALOGE("HDMI rejecting format %x", config->format); | ||
668 | goto fail; | ||
669 | } | ||
670 | |||
671 | a_config->channel_mask = config->channel_mask; | ||
672 | switch (config->channel_mask) { | ||
673 | case AUDIO_CHANNEL_OUT_STEREO: | ||
674 | pcm_config->channels = 2; | ||
675 | break; | ||
676 | case AUDIO_CHANNEL_OUT_QUAD: | ||
677 | pcm_config->channels = 4; | ||
678 | break; | ||
679 | case AUDIO_CHANNEL_OUT_5POINT1: | ||
680 | pcm_config->channels = 6; | ||
681 | break; | ||
682 | case AUDIO_CHANNEL_OUT_7POINT1: | ||
683 | pcm_config->channels = 8; | ||
684 | break; | ||
685 | default: | ||
686 | ALOGE("HDMI setting a default channel_mask %x -> 8", config->channel_mask); | ||
687 | config->channel_mask = AUDIO_CHANNEL_OUT_7POINT1; | ||
688 | a_config->channel_mask = AUDIO_CHANNEL_OUT_7POINT1; | ||
689 | pcm_config->channels = 8; | ||
690 | } | ||
691 | |||
692 | //Allocating buffer for at most 8 channels | ||
693 | out->buffcpy = malloc(pcm_config->period_size * sizeof(int16_t) * HDMI_MAX_CHANNELS); | ||
694 | if (!out->buffcpy){ | ||
695 | ALOGE("Could not allocate memory"); | ||
696 | goto fail; | ||
697 | } | ||
698 | |||
699 | ALOGV("stream = %p", out); | ||
700 | *stream_out = &out->stream_out; | ||
701 | |||
702 | return 0; | ||
703 | |||
704 | fail: | ||
705 | free(out); | ||
706 | return -ENOSYS; | ||
707 | } | ||
708 | |||
709 | static void hdmi_adev_close_output_stream(audio_hw_device_t *dev, | ||
710 | struct audio_stream_out* stream_out) | ||
711 | { | ||
712 | hdmi_out_t *out = (hdmi_out_t*)stream_out; | ||
713 | |||
714 | TRACEM("dev=%p stream_out=%p", dev, stream_out); | ||
715 | UNUSED(dev); | ||
716 | |||
717 | stream_out->common.standby((audio_stream_t*)stream_out); | ||
718 | free(out->buffcpy); | ||
719 | out->buffcpy = NULL; | ||
720 | free(stream_out); | ||
721 | } | ||
722 | |||
723 | static int hdmi_adev_open_input_stream(audio_hw_device_t *dev, | ||
724 | audio_io_handle_t handle, | ||
725 | audio_devices_t devices, | ||
726 | struct audio_config *config, | ||
727 | struct audio_stream_in **stream_in, | ||
728 | audio_input_flags_t flags, | ||
729 | const char *address, | ||
730 | audio_source_t source) | ||
731 | { | ||
732 | TRACE(); | ||
733 | UNUSED(dev); | ||
734 | UNUSED(handle); | ||
735 | UNUSED(devices); | ||
736 | UNUSED(config); | ||
737 | UNUSED(stream_in); | ||
738 | UNUSED(flags); | ||
739 | UNUSED(address); | ||
740 | UNUSED(source); | ||
741 | |||
742 | return -ENOSYS; | ||
743 | } | ||
744 | |||
745 | static void hdmi_adev_close_input_stream(audio_hw_device_t *dev, | ||
746 | struct audio_stream_in *stream_in) | ||
747 | { | ||
748 | TRACE(); | ||
749 | UNUSED(dev); | ||
750 | UNUSED(stream_in); | ||
751 | } | ||
752 | |||
753 | static int hdmi_adev_dump(const audio_hw_device_t *dev, int fd) | ||
754 | { | ||
755 | TRACE(); | ||
756 | UNUSED(dev); | ||
757 | UNUSED(fd); | ||
758 | |||
759 | return 0; | ||
760 | } | ||
761 | |||
762 | static hdmi_device_t hdmi_adev = { | ||
763 | .device = { | ||
764 | .common = { | ||
765 | .tag = HARDWARE_DEVICE_TAG, | ||
766 | .version = AUDIO_DEVICE_API_VERSION_2_0, | ||
767 | .module = NULL, | ||
768 | .close = hdmi_adev_close, | ||
769 | }, | ||
770 | .get_supported_devices = hdmi_adev_get_supported_devices, | ||
771 | .init_check = hdmi_adev_init_check, | ||
772 | .set_voice_volume = hdmi_adev_set_voice_volume, | ||
773 | .set_master_volume = hdmi_adev_set_master_volume, | ||
774 | .get_master_volume = hdmi_adev_get_master_volume, | ||
775 | .set_mode = hdmi_adev_set_mode, | ||
776 | .set_mic_mute = hdmi_adev_set_mic_mute, | ||
777 | .get_mic_mute = hdmi_adev_get_mic_mute, | ||
778 | .set_parameters = hdmi_adev_set_parameters, | ||
779 | .get_parameters = hdmi_adev_get_parameters, | ||
780 | .get_input_buffer_size = hdmi_adev_get_input_buffer_size, | ||
781 | .open_output_stream = hdmi_adev_open_output_stream, | ||
782 | .close_output_stream = hdmi_adev_close_output_stream, | ||
783 | .open_input_stream = hdmi_adev_open_input_stream, | ||
784 | .close_input_stream = hdmi_adev_close_input_stream, | ||
785 | .dump = hdmi_adev_dump, | ||
786 | }, | ||
787 | |||
788 | // Don't reorder channels until a valid CEA mapping has been | ||
789 | // explicitly set. IOW, assume default channel mapping is | ||
790 | // fine until some other mapping is requested. | ||
791 | .CEAMap = true, | ||
792 | .map = {0}, | ||
793 | }; | ||
794 | |||
795 | static int hdmi_adev_open(const hw_module_t* module, | ||
796 | const char* name, | ||
797 | hw_device_t** device) | ||
798 | { | ||
799 | TRACE(); | ||
800 | |||
801 | if (strcmp(name, AUDIO_HARDWARE_INTERFACE) != 0) | ||
802 | return -EINVAL; | ||
803 | |||
804 | hdmi_adev.device.common.module = (struct hw_module_t *) module; | ||
805 | *device = &hdmi_adev.device.common; | ||
806 | |||
807 | return 0; | ||
808 | } | ||
809 | |||
810 | static struct hw_module_methods_t hal_module_methods = { | ||
811 | .open = hdmi_adev_open, | ||
812 | }; | ||
813 | |||
814 | struct audio_module HAL_MODULE_INFO_SYM = { | ||
815 | .common = { | ||
816 | .tag = HARDWARE_MODULE_TAG, | ||
817 | .version_major = 1, | ||
818 | .version_minor = 0, | ||
819 | .id = AUDIO_HARDWARE_MODULE_ID, | ||
820 | .name = "OMAP HDMI audio HW HAL", | ||
821 | .author = "Texas Instruments", | ||
822 | .methods = &hal_module_methods, | ||
823 | }, | ||
824 | }; | ||
diff --git a/audio/hdmi/hdmi_audio_utils.c b/audio/hdmi/hdmi_audio_utils.c new file mode 100644 index 0000000..b29ec25 --- /dev/null +++ b/audio/hdmi/hdmi_audio_utils.c | |||
@@ -0,0 +1,414 @@ | |||
1 | /* -*- mode: C; c-file-style: "stroustrup"; indent-tabs-mode: nil; -*- */ | ||
2 | /* | ||
3 | * Copyright (C) 2012 Texas Instruments | ||
4 | * | ||
5 | * Licensed under the Apache License, Version 2.0 (the "License"); | ||
6 | * you may not use this file except in compliance with the License. | ||
7 | * You may obtain a copy of the License at | ||
8 | * | ||
9 | * http://www.apache.org/licenses/LICENSE-2.0 | ||
10 | * | ||
11 | * Unless required by applicable law or agreed to in writing, software | ||
12 | * distributed under the License is distributed on an "AS IS" BASIS, | ||
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
14 | * See the License for the specific language governing permissions and | ||
15 | * limitations under the License. | ||
16 | */ | ||
17 | |||
18 | #define LOG_TAG "hdmi_audio_caps" | ||
19 | /* #define LOG_NDEBUG 0 */ | ||
20 | |||
21 | #include <sys/types.h> | ||
22 | #include <sys/stat.h> | ||
23 | #include <fcntl.h> | ||
24 | #include <string.h> | ||
25 | #include <errno.h> | ||
26 | |||
27 | #include <xf86drm.h> | ||
28 | #include <xf86drmMode.h> | ||
29 | |||
30 | #include "hdmi_audio_hal.h" | ||
31 | |||
32 | /* | ||
33 | * This program can be compiled standalone for debugging and | ||
34 | * testing by defining HDMI_CAPS_STANDALONE | ||
35 | */ | ||
36 | |||
37 | #ifdef HDMI_CAPS_STANDALONE | ||
38 | #include <stdio.h> | ||
39 | |||
40 | #define lfmt(x) x | ||
41 | #define ALOGV(fmt, ...) printf(lfmt(fmt) "\n", ##__VA_ARGS__) | ||
42 | #define ALOGW(fmt, ...) printf(lfmt(fmt) "\n", ##__VA_ARGS__) | ||
43 | #define ALOGE(fmt, ...) printf(lfmt(fmt) "\n", ##__VA_ARGS__) | ||
44 | #define ALOGV_IF(cond, fmt, ...) { if (cond) { printf(lfmt(fmt) "\n", ##__VA_ARGS__); } } | ||
45 | #define ALOGE_IF(cond, fmt, ...) { if (cond) { printf(lfmt(fmt) "\n", ##__VA_ARGS__); } } | ||
46 | #else /* HDMI_CAPS_STANDALONE */ | ||
47 | |||
48 | #include <cutils/log.h> | ||
49 | |||
50 | #endif /* HDMI_CAPS_STANDALONE */ | ||
51 | |||
52 | /***************************************************************** | ||
53 | * HDMI AUDIO CAPABILITIES (EDID PARSING) | ||
54 | * | ||
55 | * To maintain this section, you need these references: | ||
56 | * | ||
57 | * - High-Definition Multimedia Interface Specification Version 1.4a | ||
58 | * Mar 4, 2010, Hitachi et al. (See Section 8.3 "E-EDID Data | ||
59 | * Structure") | ||
60 | * | ||
61 | * - E-EDID, VESA Enhanced Extended Display Identification Data | ||
62 | * Standard, Release A, Revision 2, September 25. 2006, | ||
63 | * Video Electronics Standards Association (VESA) | ||
64 | * | ||
65 | * - CEA-861-D, A DTV Profile for Uncompressed High Speed Digital | ||
66 | * Interfaces, July 2006 (Section 7.5) | ||
67 | * | ||
68 | * | ||
69 | * Summary: The E-EDID is composed of 128-byte blocks. The first byte | ||
70 | * identifies the block. HDMI devices are required to send a CEA | ||
71 | * Extension block (version 3), and the first one is the one we /must/ | ||
72 | * parse. (You can probably go far with just this info and the | ||
73 | * CEA-861-D spec.) | ||
74 | * | ||
75 | ***************************************************************** | ||
76 | */ | ||
77 | |||
78 | #define DISPLAY_MAX 3 | ||
79 | #define DISPLAY_NAME_MAX 20 | ||
80 | #define HDMI_DISPLAY_NAME "hdmi" | ||
81 | #define OMAP_DSS_SYSFS "/sys/devices/platform/omapdss/" | ||
82 | #define DRM_OMAP_DEVNAME "omapdrm" | ||
83 | #define DRM_EDID_PROPNAME "EDID" | ||
84 | |||
85 | /* TODO: Figure this out dynamically, but ATM this is enforced | ||
86 | * in the kernel. | ||
87 | */ | ||
88 | #define HDMI_MAX_EDID 512 | ||
89 | |||
90 | #define EDID_BLOCK_SIZE 128 | ||
91 | #define EDID_BLOCK_MAP_ID 0xF0 | ||
92 | #define EDID_BLOCK_CEA_ID 0x02 | ||
93 | #define EDID_BLOCK_CEA_REV_3 0x03 | ||
94 | |||
95 | #define CEA_TAG_MASK 0xE0 | ||
96 | #define CEA_SIZE_MASK 0x1F | ||
97 | #define CEA_TAG_AUDIO (1 << 5) | ||
98 | #define CEA_TAG_VIDEO (2 << 5) | ||
99 | #define CEA_TAG_SPKRS (4 << 5) | ||
100 | #define CEA_BIT_AUDIO (1 << 6) | ||
101 | |||
102 | static void hdmi_dump_short_audio_descriptor_block(unsigned char *mem) | ||
103 | { | ||
104 | const unsigned char FORMAT_MASK = 0x78; | ||
105 | const unsigned char MAX_CH_MASK = 0x07; | ||
106 | int n, size; | ||
107 | unsigned char *p, *end, byte, format, chs; | ||
108 | const char* formats[] = { | ||
109 | "Reserved (0)", | ||
110 | "LPCM", | ||
111 | "AC-3", | ||
112 | "MPEG1 (Layers 1 & 2)", | ||
113 | "MP3 (MPEG1 Layer 3)", | ||
114 | "MPEG2 (multichannel)", | ||
115 | "AAC", | ||
116 | "DTS", | ||
117 | "ATRAC", | ||
118 | "One Bit Audio", | ||
119 | "Dolby Digital +", | ||
120 | "DTS-HD", | ||
121 | "MAT (MLP)", | ||
122 | "DST", | ||
123 | "WMA Pro", | ||
124 | "Reserved (15)", | ||
125 | }; | ||
126 | |||
127 | size = *mem & CEA_SIZE_MASK; | ||
128 | end = mem + 1 + size; | ||
129 | |||
130 | for (p = mem + 1 ; p < end ; p += 3) { | ||
131 | byte = p[0]; | ||
132 | format = (byte & FORMAT_MASK) >> 3; | ||
133 | chs = byte & MAX_CH_MASK; | ||
134 | |||
135 | ALOGV("Parsing Short Audio Descriptor:"); | ||
136 | ALOGV(" format: %s", formats[format]); | ||
137 | ALOGV(" max channels: %d", chs + 1); | ||
138 | ALOGV(" sample rates:"); | ||
139 | |||
140 | byte = p[1]; | ||
141 | ALOGV_IF(byte & (1 << 0), " 32.0 kHz"); | ||
142 | ALOGV_IF(byte & (1 << 1), " 44.1 kHz"); | ||
143 | ALOGV_IF(byte & (1 << 2), " 48.0 kHz"); | ||
144 | ALOGV_IF(byte & (1 << 3), " 88.2 kHz"); | ||
145 | ALOGV_IF(byte & (1 << 4), " 96.0 kHz"); | ||
146 | ALOGV_IF(byte & (1 << 5), " 176.4 kHz"); | ||
147 | ALOGV_IF(byte & (1 << 6), " 192.0 kHz"); | ||
148 | |||
149 | byte = p[2]; | ||
150 | if ((format >= 2) && (format <= 8)) { | ||
151 | ALOGV(" max bit rate: %d kHz", ((int)byte) * 8); | ||
152 | } | ||
153 | } | ||
154 | } | ||
155 | |||
156 | int hdmi_get_edid_sysfs(unsigned char *edid) | ||
157 | { | ||
158 | int fd; | ||
159 | char fn[256]; | ||
160 | char disp_name[DISPLAY_NAME_MAX]; | ||
161 | int status; | ||
162 | int index, n; | ||
163 | |||
164 | ALOGI("Trying to read the HDMI EDID through sysfs"); | ||
165 | |||
166 | for (index = 0; index < DISPLAY_MAX; index++) { | ||
167 | snprintf(fn, sizeof(fn), OMAP_DSS_SYSFS "display%u", index); | ||
168 | fd = open(fn, O_RDONLY); | ||
169 | if (fd < 0) { | ||
170 | ALOGE("HDMI sysfs entry not found"); | ||
171 | return -ENODEV; | ||
172 | } | ||
173 | close(fd); | ||
174 | |||
175 | snprintf(fn, sizeof(fn), OMAP_DSS_SYSFS "display%u/name", index); | ||
176 | fd = open(fn, O_RDONLY); | ||
177 | if (fd < 0) { | ||
178 | ALOGE("Error opening display name sysfs entry"); | ||
179 | return -ENODEV; | ||
180 | } | ||
181 | |||
182 | status = read(fd, disp_name, sizeof(disp_name)); | ||
183 | close(fd); | ||
184 | if (status == -1) { | ||
185 | ALOGE("Error reading display name sysfs entry"); | ||
186 | return -errno; | ||
187 | } | ||
188 | |||
189 | if (!strncasecmp(disp_name, HDMI_DISPLAY_NAME, strlen(HDMI_DISPLAY_NAME))) { | ||
190 | ALOGV("HDMI device found at display%u", index); | ||
191 | break; | ||
192 | } | ||
193 | } | ||
194 | |||
195 | if (index == DISPLAY_MAX) { | ||
196 | ALOGE("HDMI sysfs entry not found"); | ||
197 | return -ENODEV; | ||
198 | } | ||
199 | |||
200 | snprintf(fn, sizeof(fn), OMAP_DSS_SYSFS "display%u/edid", index); | ||
201 | fd = open(fn, O_RDONLY); | ||
202 | if (fd == -1) { | ||
203 | return -errno; | ||
204 | } | ||
205 | |||
206 | memset(edid, 0, HDMI_MAX_EDID); | ||
207 | |||
208 | status = read(fd, edid, HDMI_MAX_EDID); | ||
209 | close(fd); | ||
210 | if (status == -1) { | ||
211 | ALOGV("Error reading EDID through sysfs"); | ||
212 | return -errno; | ||
213 | } else { | ||
214 | ALOGV("read %d bytes from edid sysfs file", status); | ||
215 | } | ||
216 | |||
217 | return 0; | ||
218 | } | ||
219 | |||
220 | int hdmi_get_edid_drm(unsigned char *edid) | ||
221 | { | ||
222 | drmModeRes *resources; | ||
223 | drmModeConnector *connector; | ||
224 | drmModePropertyPtr prop; | ||
225 | drmModePropertyBlobPtr blob; | ||
226 | unsigned char *blob_data; | ||
227 | int fd, i, j; | ||
228 | int found = 0; | ||
229 | |||
230 | ALOGI("Trying to read the HDMI EDID through DRM"); | ||
231 | |||
232 | fd = drmOpen(DRM_OMAP_DEVNAME, NULL); | ||
233 | if (fd < 0) { | ||
234 | ALOGE("Failed to open DRM device '%s'", DRM_OMAP_DEVNAME); | ||
235 | return -ENODEV; | ||
236 | } | ||
237 | |||
238 | resources = drmModeGetResources(fd); | ||
239 | if (!resources) { | ||
240 | ALOGE("Failed to get DRM resources"); | ||
241 | drmClose(fd); | ||
242 | return -EINVAL; | ||
243 | } | ||
244 | |||
245 | for (i = 0; (i < resources->count_connectors) && !found; i++) { | ||
246 | connector = drmModeGetConnector(fd, resources->connectors[i]); | ||
247 | if (!connector) { | ||
248 | continue; | ||
249 | } | ||
250 | |||
251 | if (!connector->count_modes) { | ||
252 | drmModeFreeConnector(connector); | ||
253 | continue; | ||
254 | } | ||
255 | |||
256 | for (j = 0; (j < connector->count_props) && !found; j++) { | ||
257 | prop = drmModeGetProperty(fd, connector->props[j]); | ||
258 | if (!prop) { | ||
259 | continue; | ||
260 | } | ||
261 | |||
262 | if (strcmp(prop->name, DRM_EDID_PROPNAME) || | ||
263 | !(prop->flags & DRM_MODE_PROP_BLOB)) { | ||
264 | drmModeFreeProperty(prop); | ||
265 | continue; | ||
266 | } | ||
267 | |||
268 | blob = drmModeGetPropertyBlob(fd, connector->prop_values[j]); | ||
269 | if (!blob) { | ||
270 | drmModeFreeProperty(prop); | ||
271 | continue; | ||
272 | } | ||
273 | |||
274 | if (blob->length > HDMI_MAX_EDID) { | ||
275 | ALOGW("EDID blob length %u is larger than expected %d, skipping", | ||
276 | blob->length, HDMI_MAX_EDID); | ||
277 | drmModeFreePropertyBlob(blob); | ||
278 | drmModeFreeProperty(prop); | ||
279 | continue; | ||
280 | } | ||
281 | |||
282 | ALOGV("EDID has been found, length is %u bytes", blob->length); | ||
283 | memset(edid, 0, HDMI_MAX_EDID); | ||
284 | memcpy(edid, blob->data, blob->length); | ||
285 | drmModeFreePropertyBlob(blob); | ||
286 | drmModeFreeProperty(prop); | ||
287 | found = 1; | ||
288 | } | ||
289 | |||
290 | drmModeFreeConnector(connector); | ||
291 | } | ||
292 | |||
293 | drmModeFreeResources(resources); | ||
294 | |||
295 | drmClose(fd); | ||
296 | |||
297 | return found ? 0 : -ENODEV; | ||
298 | } | ||
299 | |||
300 | int hdmi_query_audio_caps(hdmi_audio_caps_t *caps) | ||
301 | { | ||
302 | unsigned char edid[HDMI_MAX_EDID]; | ||
303 | int index, n; | ||
304 | int nblocks; | ||
305 | int edid_size; | ||
306 | int has_audio = 0; | ||
307 | int speaker_alloc = 0; | ||
308 | int done = 0; | ||
309 | int status; | ||
310 | |||
311 | status = hdmi_get_edid_sysfs(edid); | ||
312 | if (status) { | ||
313 | ALOGE("Failed to read EDID through sysfs"); | ||
314 | status = hdmi_get_edid_drm(edid); | ||
315 | if (status) { | ||
316 | ALOGE("Failed to read EDID through DRM"); | ||
317 | return status; | ||
318 | } | ||
319 | } | ||
320 | |||
321 | nblocks = edid[0x7E]; | ||
322 | if (edid[EDID_BLOCK_SIZE] == EDID_BLOCK_MAP_ID) { | ||
323 | /* The block map is just an index... don't need it. */ | ||
324 | ++nblocks; | ||
325 | } | ||
326 | ALOGV("EDID contians %d additional extension block(s)", nblocks); | ||
327 | edid_size = EDID_BLOCK_SIZE * (nblocks + 1); | ||
328 | if (edid_size > HDMI_MAX_EDID) { | ||
329 | edid_size = HDMI_MAX_EDID; | ||
330 | } | ||
331 | |||
332 | for (index = EDID_BLOCK_SIZE ; index < edid_size ; index += EDID_BLOCK_SIZE) { | ||
333 | if ((edid[index] == EDID_BLOCK_CEA_ID) | ||
334 | && (edid[index+1] == EDID_BLOCK_CEA_REV_3)) { | ||
335 | /* CEA BLOCK */ | ||
336 | unsigned char d; | ||
337 | unsigned char byte, tag, size; | ||
338 | |||
339 | /* Parse CEA header for size and audio presence */ | ||
340 | d = edid[index + 2]; | ||
341 | if (edid[index + 3] & CEA_BIT_AUDIO) { | ||
342 | has_audio = 1; | ||
343 | } else { | ||
344 | break; | ||
345 | } | ||
346 | |||
347 | n = 4; | ||
348 | while (n < d) { | ||
349 | byte = edid[index + n]; | ||
350 | tag = byte & CEA_TAG_MASK; | ||
351 | size = byte & CEA_SIZE_MASK; | ||
352 | ++n; | ||
353 | |||
354 | switch (tag) { | ||
355 | case CEA_TAG_AUDIO: | ||
356 | /* ignore for now, needed for bitstream. */ | ||
357 | hdmi_dump_short_audio_descriptor_block(&edid[index + n - 1]); | ||
358 | break; | ||
359 | case CEA_TAG_SPKRS: /* I think this fails... not sure why */ | ||
360 | ALOGE_IF(size != 3, "CEA Speaker Allocation Block is wrong size " | ||
361 | "(got %d, expected 3)", size); | ||
362 | byte = edid[index + n]; | ||
363 | speaker_alloc = byte; | ||
364 | break; | ||
365 | } | ||
366 | |||
367 | n += size; | ||
368 | } | ||
369 | |||
370 | /* Per spec, only need to parse the first block */ | ||
371 | break; | ||
372 | } | ||
373 | } | ||
374 | |||
375 | caps->has_audio = has_audio; | ||
376 | caps->speaker_alloc = speaker_alloc; | ||
377 | |||
378 | ALOGI_IF(caps->has_audio, "HDMI speaker allocation 0x%02x", | ||
379 | caps->speaker_alloc); | ||
380 | |||
381 | ALOGW_IF(!caps->has_audio, "HDMI device doesn't support audio"); | ||
382 | |||
383 | return 0; | ||
384 | } | ||
385 | |||
386 | |||
387 | #ifdef HDMI_CAPS_STANDALONE | ||
388 | int main(int argc, char* argv[]) | ||
389 | { | ||
390 | const char prog_name[] = "hdmi_audio_caps"; | ||
391 | hdmi_audio_caps_t caps = { | ||
392 | .has_audio = 0, | ||
393 | }; | ||
394 | |||
395 | if (argc < 1) { | ||
396 | printf("usage: %s\n", argc ? argv[0] : prog_name); | ||
397 | return 0; | ||
398 | } | ||
399 | |||
400 | if (hdmi_query_audio_caps(&caps)) { | ||
401 | fprintf(stderr, "Fatal error: could not read EDID (%s)\n", | ||
402 | strerror(errno)); | ||
403 | return 1; | ||
404 | } | ||
405 | |||
406 | printf("\n"); | ||
407 | printf("caps = {\n"); | ||
408 | printf(" .has_audio = %d\n", caps.has_audio); | ||
409 | printf(" .speaker_alloc = 0x%02x\n", caps.speaker_alloc); | ||
410 | printf("}\n"); | ||
411 | |||
412 | return 0; | ||
413 | } | ||
414 | #endif /* HDMI_CAPS_STANDALONE */ | ||
diff --git a/audio/primary/Android.mk b/audio/primary/Android.mk new file mode 100644 index 0000000..40f60da --- /dev/null +++ b/audio/primary/Android.mk | |||
@@ -0,0 +1,43 @@ | |||
1 | # Copyright (C) 2013 Texas Instruments | ||
2 | # | ||
3 | # Licensed under the Apache License, Version 2.0 (the "License"); | ||
4 | # you may not use this file except in compliance with the License. | ||
5 | # You may obtain a copy of the License at | ||
6 | # | ||
7 | # http://www.apache.org/licenses/LICENSE-2.0 | ||
8 | # | ||
9 | # Unless required by applicable law or agreed to in writing, software | ||
10 | # distributed under the License is distributed on an "AS IS" BASIS, | ||
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
12 | # See the License for the specific language governing permissions and | ||
13 | # limitations under the License. | ||
14 | |||
15 | LOCAL_PATH := $(call my-dir) | ||
16 | |||
17 | ifeq ($(findstring am57x, $(TARGET_BOARD_PLATFORM)),am57x) | ||
18 | |||
19 | include $(CLEAR_VARS) | ||
20 | |||
21 | LOCAL_MODULE := audio.primary.$(TARGET_BOARD_PLATFORM) | ||
22 | |||
23 | LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw | ||
24 | LOCAL_SRC_FILES := audio_hw.c | ||
25 | |||
26 | LOCAL_C_INCLUDES += \ | ||
27 | external/tinyalsa/include \ | ||
28 | system/media/audio_route/include \ | ||
29 | system/media/audio_utils/include \ | ||
30 | system/media/audio_effects/include | ||
31 | |||
32 | LOCAL_SHARED_LIBRARIES := \ | ||
33 | liblog \ | ||
34 | libcutils \ | ||
35 | libtinyalsa \ | ||
36 | libaudioroute \ | ||
37 | libaudioutils | ||
38 | |||
39 | LOCAL_MODULE_TAGS := optional | ||
40 | |||
41 | include $(BUILD_SHARED_LIBRARY) | ||
42 | |||
43 | endif | ||
diff --git a/audio/primary/audio_hw.c b/audio/primary/audio_hw.c new file mode 100644 index 0000000..ab87dfa --- /dev/null +++ b/audio/primary/audio_hw.c | |||
@@ -0,0 +1,1766 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2013 Texas Instruments | ||
3 | * | ||
4 | * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | * you may not use this file except in compliance with the License. | ||
6 | * You may obtain a copy of the License at | ||
7 | * | ||
8 | * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | * | ||
10 | * Unless required by applicable law or agreed to in writing, software | ||
11 | * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | * See the License for the specific language governing permissions and | ||
14 | * limitations under the License. | ||
15 | */ | ||
16 | |||
17 | #define LOG_TAG "audio_hw_primary" | ||
18 | //#define LOG_NDEBUG 0 | ||
19 | //#define VERY_VERBOSE_LOGGING | ||
20 | #ifdef VERY_VERBOSE_LOGGING | ||
21 | #define ALOGVV ALOGV | ||
22 | #else | ||
23 | #define ALOGVV(a...) do { } while(0) | ||
24 | #endif | ||
25 | |||
26 | #include <errno.h> | ||
27 | #include <stdint.h> | ||
28 | #include <stdlib.h> | ||
29 | #include <sys/time.h> | ||
30 | |||
31 | #include <cutils/log.h> | ||
32 | #include <cutils/str_parms.h> | ||
33 | #include <cutils/properties.h> | ||
34 | |||
35 | #include <audio_utils/resampler.h> | ||
36 | #include <audio_route/audio_route.h> | ||
37 | #include <system/audio.h> | ||
38 | #include <hardware/hardware.h> | ||
39 | #include <hardware/audio.h> | ||
40 | #include <hardware/audio_effect.h> | ||
41 | |||
42 | #include <tinyalsa/asoundlib.h> | ||
43 | |||
44 | #define UNUSED(x) (void)(x) | ||
45 | |||
46 | /* yet another definition of ARRAY_SIZE macro) */ | ||
47 | #define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0])) | ||
48 | |||
49 | /* | ||
50 | * additional space in resampler buffer allowing for extra samples to be returned | ||
51 | * by speex resampler when sample rates ratio is not an integer | ||
52 | */ | ||
53 | #define RESAMPLER_HEADROOM_FRAMES 10 | ||
54 | |||
55 | /* buffer_remix: functor for doing in-place buffer manipulations. | ||
56 | * | ||
57 | * NB. When remix_func is called, the memory at `buf` must be at least | ||
58 | * as large as frames * sample_size * MAX(in_chans, out_chans). | ||
59 | */ | ||
60 | struct buffer_remix { | ||
61 | void (*remix_func)(struct buffer_remix *data, void *buf, size_t frames); | ||
62 | size_t sample_size; /* size of one audio sample, in bytes */ | ||
63 | size_t in_chans; /* number of input channels */ | ||
64 | size_t out_chans; /* number of output channels */ | ||
65 | }; | ||
66 | |||
67 | struct j6_voice_stream { | ||
68 | struct j6_audio_device *dev; | ||
69 | struct pcm *pcm_in; | ||
70 | struct pcm *pcm_out; | ||
71 | struct pcm_config in_config; | ||
72 | struct pcm_config out_config; | ||
73 | struct resampler_itfe *resampler; | ||
74 | struct resampler_buffer_provider buf_provider; | ||
75 | struct buffer_remix *remix; | ||
76 | pthread_t thread; | ||
77 | int16_t *in_buffer; | ||
78 | int16_t *out_buffer; | ||
79 | size_t in_frames; | ||
80 | size_t out_frames; | ||
81 | size_t in_frame_size; | ||
82 | size_t out_frame_size; | ||
83 | char *name; | ||
84 | }; | ||
85 | |||
86 | struct j6_voice { | ||
87 | struct j6_voice_stream ul; | ||
88 | struct j6_voice_stream dl; | ||
89 | }; | ||
90 | |||
91 | struct j6_audio_device { | ||
92 | struct audio_hw_device device; | ||
93 | struct j6_stream_in *in; | ||
94 | struct j6_stream_out *out; | ||
95 | struct j6_voice voice; | ||
96 | struct audio_route *route; | ||
97 | audio_devices_t in_device; | ||
98 | audio_devices_t out_device; | ||
99 | pthread_mutex_t lock; | ||
100 | unsigned int card; | ||
101 | unsigned int in_port; | ||
102 | unsigned int out_port; | ||
103 | unsigned int bt_card; | ||
104 | unsigned int bt_port; | ||
105 | bool mic_mute; | ||
106 | bool in_call; | ||
107 | audio_mode_t mode; | ||
108 | }; | ||
109 | |||
110 | struct j6_stream_in { | ||
111 | struct audio_stream_in stream; | ||
112 | struct j6_audio_device *dev; | ||
113 | struct pcm_config config; | ||
114 | struct pcm *pcm; | ||
115 | struct buffer_remix *remix; /* adapt hw chan count to client */ | ||
116 | struct resampler_itfe *resampler; | ||
117 | struct resampler_buffer_provider buf_provider; | ||
118 | int16_t *buffer; | ||
119 | size_t frames_in; | ||
120 | size_t hw_frame_size; | ||
121 | unsigned int requested_rate; | ||
122 | unsigned int requested_channels; | ||
123 | int read_status; | ||
124 | pthread_mutex_t lock; | ||
125 | bool standby; | ||
126 | }; | ||
127 | |||
128 | struct j6_stream_out { | ||
129 | struct audio_stream_out stream; | ||
130 | struct j6_audio_device *dev; | ||
131 | struct pcm_config config; | ||
132 | struct pcm *pcm; | ||
133 | struct timespec last; | ||
134 | pthread_mutex_t lock; | ||
135 | bool standby; | ||
136 | int64_t written; /* total frames written, not cleared when entering standby */ | ||
137 | }; | ||
138 | |||
139 | |||
140 | static const char *supported_media_cards[] = { | ||
141 | "dra7evm", | ||
142 | "VayuEVM", | ||
143 | "DRA7xxEVM", | ||
144 | }; | ||
145 | |||
146 | static const char *supported_bt_cards[] = { | ||
147 | "DRA7xxWiLink", | ||
148 | }; | ||
149 | |||
150 | #define SUPPORTED_IN_DEVICES (AUDIO_DEVICE_IN_BUILTIN_MIC | \ | ||
151 | AUDIO_DEVICE_IN_WIRED_HEADSET | \ | ||
152 | AUDIO_DEVICE_IN_DEFAULT) | ||
153 | #define SUPPORTED_OUT_DEVICES (AUDIO_DEVICE_OUT_SPEAKER | \ | ||
154 | AUDIO_DEVICE_OUT_WIRED_HEADSET | \ | ||
155 | AUDIO_DEVICE_OUT_WIRED_HEADPHONE | \ | ||
156 | AUDIO_DEVICE_OUT_DEFAULT) | ||
157 | |||
158 | #define CAPTURE_SAMPLE_RATE 44100 | ||
159 | #define CAPTURE_PERIOD_SIZE 256 | ||
160 | #define CAPTURE_PERIOD_COUNT 4 | ||
161 | #define CAPTURE_BUFFER_SIZE (CAPTURE_PERIOD_SIZE * CAPTURE_PERIOD_COUNT) | ||
162 | |||
163 | #define PLAYBACK_SAMPLE_RATE 44100 | ||
164 | #define PLAYBACK_PERIOD_SIZE 256 | ||
165 | #define PLAYBACK_PERIOD_COUNT 4 | ||
166 | #define PLAYBACK_BUFFER_SIZE (PLAYBACK_PERIOD_SIZE * PLAYBACK_PERIOD_COUNT) | ||
167 | |||
168 | #define BT_SAMPLE_RATE 8000 | ||
169 | #define BT_PERIOD_SIZE 80 | ||
170 | #define BT_PERIOD_COUNT 4 | ||
171 | #define BT_BUFFER_SIZE (BT_PERIOD_SIZE * BT_PERIOD_COUNT) | ||
172 | |||
173 | struct pcm_config pcm_config_capture = { | ||
174 | .channels = 2, | ||
175 | .rate = CAPTURE_SAMPLE_RATE, | ||
176 | .format = PCM_FORMAT_S16_LE, | ||
177 | .period_size = CAPTURE_PERIOD_SIZE, | ||
178 | .period_count = CAPTURE_PERIOD_COUNT, | ||
179 | .start_threshold = 1, | ||
180 | .stop_threshold = CAPTURE_BUFFER_SIZE, | ||
181 | }; | ||
182 | |||
183 | struct pcm_config pcm_config_playback = { | ||
184 | .channels = 2, | ||
185 | .rate = PLAYBACK_SAMPLE_RATE, | ||
186 | .format = PCM_FORMAT_S16_LE, | ||
187 | .period_size = PLAYBACK_PERIOD_SIZE, | ||
188 | .period_count = PLAYBACK_PERIOD_COUNT, | ||
189 | .start_threshold = PLAYBACK_BUFFER_SIZE / 2, | ||
190 | .stop_threshold = PLAYBACK_BUFFER_SIZE, | ||
191 | .avail_min = PLAYBACK_PERIOD_SIZE, | ||
192 | }; | ||
193 | |||
194 | struct pcm_config pcm_config_bt_in = { | ||
195 | .channels = 1, | ||
196 | .rate = BT_SAMPLE_RATE, | ||
197 | .format = PCM_FORMAT_S16_LE, | ||
198 | .period_size = BT_PERIOD_SIZE, | ||
199 | .period_count = BT_PERIOD_COUNT, | ||
200 | }; | ||
201 | |||
202 | struct pcm_config pcm_config_bt_out = { | ||
203 | .channels = 1, | ||
204 | .rate = BT_SAMPLE_RATE, | ||
205 | .format = PCM_FORMAT_S16_LE, | ||
206 | .period_size = BT_PERIOD_SIZE, | ||
207 | .period_count = BT_PERIOD_COUNT, | ||
208 | }; | ||
209 | |||
210 | static void do_out_standby(struct j6_stream_out *out); | ||
211 | |||
212 | /* must be called with device lock held */ | ||
213 | static void select_input_device(struct j6_audio_device *adev) | ||
214 | { | ||
215 | if (adev->in_device & ~SUPPORTED_IN_DEVICES) | ||
216 | ALOGW("select_input_device() device not supported, will use default device"); | ||
217 | } | ||
218 | |||
219 | /* must be called with device lock held */ | ||
220 | static void select_output_device(struct j6_audio_device *adev) | ||
221 | { | ||
222 | if (adev->out_device & ~SUPPORTED_OUT_DEVICES) | ||
223 | ALOGW("select_output_device() device(s) not supported, will use default devices"); | ||
224 | } | ||
225 | |||
226 | static size_t get_input_buffer_size(uint32_t sample_rate, int format, int channel_count) | ||
227 | { | ||
228 | size_t size; | ||
229 | |||
230 | /* | ||
231 | * take resampling into account and return the closest majoring | ||
232 | * multiple of 16 frames, as audioflinger expects audio buffers to | ||
233 | * be a multiple of 16 frames | ||
234 | */ | ||
235 | size = (pcm_config_capture.period_size * sample_rate) / pcm_config_capture.rate; | ||
236 | size = ((size + 15) / 16) * 16; | ||
237 | |||
238 | return size * channel_count * audio_bytes_per_sample(format); | ||
239 | } | ||
240 | |||
241 | /* | ||
242 | * Implementation of buffer_remix::remix_func that removes | ||
243 | * channels in place without doing any other processing. The | ||
244 | * extra channels are truncated. | ||
245 | */ | ||
246 | static void remove_channels_from_buf(struct buffer_remix *data, void *buf, size_t frames) | ||
247 | { | ||
248 | size_t samp_size, in_frame, out_frame; | ||
249 | size_t N, c; | ||
250 | char *s, *d; | ||
251 | |||
252 | ALOGVV("remove_channels_from_buf() remix=%p buf=%p frames=%u", | ||
253 | data, buf, frames); | ||
254 | |||
255 | if (frames == 0) | ||
256 | return; | ||
257 | |||
258 | samp_size = data->sample_size; | ||
259 | in_frame = data->in_chans * samp_size; | ||
260 | out_frame = data->out_chans * samp_size; | ||
261 | |||
262 | if (out_frame >= in_frame) { | ||
263 | ALOGE("BUG: remove_channels_from_buf() can not add channels to a buffer.\n"); | ||
264 | return; | ||
265 | } | ||
266 | |||
267 | N = frames - 1; | ||
268 | d = (char*)buf + out_frame; | ||
269 | s = (char*)buf + in_frame; | ||
270 | |||
271 | /* take the first several channels and truncate the rest */ | ||
272 | while (N--) { | ||
273 | for (c = 0; c < out_frame; ++c) | ||
274 | d[c] = s[c]; | ||
275 | d += out_frame; | ||
276 | s += in_frame; | ||
277 | } | ||
278 | } | ||
279 | |||
280 | static int setup_stereo_to_mono_input_remix(struct j6_stream_in *in) | ||
281 | { | ||
282 | ALOGV("setup_stereo_to_mono_input_remix() stream=%p", in); | ||
283 | |||
284 | struct buffer_remix *br = (struct buffer_remix *)calloc(1, sizeof(struct buffer_remix)); | ||
285 | if (!br) | ||
286 | return -ENOMEM; | ||
287 | |||
288 | br->remix_func = remove_channels_from_buf; | ||
289 | br->sample_size = sizeof(int16_t); | ||
290 | br->in_chans = 2; | ||
291 | br->out_chans = 1; | ||
292 | in->remix = br; | ||
293 | |||
294 | return 0; | ||
295 | } | ||
296 | |||
297 | /* | ||
298 | * Implementation of buffer_remix::remix_func that duplicates the first | ||
299 | * channel into the rest of channels in the frame without doing any other | ||
300 | * processing | ||
301 | */ | ||
302 | static void duplicate_channels_from_mono(struct buffer_remix *data, void *buf, size_t frames) | ||
303 | { | ||
304 | int samp_size, in_frame, out_frame; | ||
305 | int N, c; | ||
306 | char *s, *d; | ||
307 | |||
308 | ALOGVV("duplicate_channels_from_mono() remix=%p buf=%p frames=%u", | ||
309 | data, buf, frames); | ||
310 | |||
311 | if (frames == 0) | ||
312 | return; | ||
313 | |||
314 | samp_size = data->sample_size; | ||
315 | in_frame = data->in_chans * samp_size; | ||
316 | out_frame = data->out_chans * samp_size; | ||
317 | |||
318 | if (in_frame >= out_frame) { | ||
319 | ALOGE("BUG: duplicate_channels_from_mono() can not drop channels\n"); | ||
320 | return; | ||
321 | } | ||
322 | |||
323 | N = frames - 1; | ||
324 | d = (char*)buf + N * out_frame; | ||
325 | s = (char*)buf + N * in_frame; | ||
326 | |||
327 | /* duplicate first channel into the rest of channels in the frame */ | ||
328 | while (N-- >= 0) { | ||
329 | for (c = 0; c < out_frame; ++c) | ||
330 | d[c] = s[c % in_frame]; | ||
331 | d -= out_frame; | ||
332 | s -= in_frame; | ||
333 | } | ||
334 | } | ||
335 | |||
336 | static int setup_voice_remix(struct j6_voice_stream *stream) | ||
337 | { | ||
338 | ALOGVV("setup_voice_mono_to_stereo_input_remix() %s stream", stream->name); | ||
339 | |||
340 | struct buffer_remix *br = (struct buffer_remix *)calloc(1, sizeof(struct buffer_remix)); | ||
341 | if (!br) | ||
342 | return -ENOMEM; | ||
343 | |||
344 | br->remix_func = duplicate_channels_from_mono; | ||
345 | br->sample_size = sizeof(int16_t); | ||
346 | br->in_chans = stream->in_config.channels; | ||
347 | br->out_chans = stream->out_config.channels; | ||
348 | |||
349 | if (br->in_chans == 1) | ||
350 | br->remix_func = duplicate_channels_from_mono; | ||
351 | else if (br->out_chans == 1) | ||
352 | br->remix_func = remove_channels_from_buf; | ||
353 | else | ||
354 | return -ENOTSUP; | ||
355 | |||
356 | stream->remix = br; | ||
357 | |||
358 | return 0; | ||
359 | } | ||
360 | |||
361 | static int voice_get_next_buffer(struct resampler_buffer_provider *buffer_provider, | ||
362 | struct resampler_buffer* buffer) | ||
363 | { | ||
364 | struct j6_voice_stream *stream; | ||
365 | int ret; | ||
366 | |||
367 | if (buffer_provider == NULL || buffer == NULL) { | ||
368 | ALOGE("voice_get_next_buffer() invalid buffer/provider"); | ||
369 | return -EINVAL; | ||
370 | } | ||
371 | |||
372 | stream = (struct j6_voice_stream *)((char *)buffer_provider - | ||
373 | offsetof(struct j6_voice_stream, buf_provider)); | ||
374 | |||
375 | if (stream->pcm_in == NULL) { | ||
376 | buffer->raw = NULL; | ||
377 | buffer->frame_count = 0; | ||
378 | return -ENODEV; | ||
379 | } | ||
380 | |||
381 | if (buffer->frame_count > stream->in_frames) { | ||
382 | ALOGW("voice_get_next_buffer() %s unexpected frame count %u, " | ||
383 | "buffer was allocated for %u frames", | ||
384 | stream->name, buffer->frame_count, stream->in_frames); | ||
385 | buffer->frame_count = stream->in_frames; | ||
386 | } | ||
387 | |||
388 | ret = pcm_read(stream->pcm_in, stream->in_buffer, | ||
389 | buffer->frame_count * stream->in_frame_size); | ||
390 | if (ret) { | ||
391 | ALOGE("voice_get_next_buffer() failed to read %s: %s", | ||
392 | stream->name, pcm_get_error(stream->pcm_in)); | ||
393 | buffer->raw = NULL; | ||
394 | buffer->frame_count = 0; | ||
395 | return ret; | ||
396 | } | ||
397 | |||
398 | buffer->i16 = stream->in_buffer; | ||
399 | |||
400 | return ret; | ||
401 | } | ||
402 | |||
403 | static void voice_release_buffer(struct resampler_buffer_provider *buffer_provider, | ||
404 | struct resampler_buffer* buffer) | ||
405 | { | ||
406 | UNUSED(buffer_provider); | ||
407 | UNUSED(buffer); | ||
408 | } | ||
409 | |||
410 | static void *voice_thread_func(void *arg) | ||
411 | { | ||
412 | struct j6_voice_stream *stream = (struct j6_voice_stream *)arg; | ||
413 | struct j6_audio_device *adev = stream->dev; | ||
414 | size_t frames = stream->out_frames; | ||
415 | uint32_t periods = 0; | ||
416 | uint32_t avail; | ||
417 | bool in_steady = false; | ||
418 | bool out_steady = false; | ||
419 | int ret = 0; | ||
420 | |||
421 | pcm_start(stream->pcm_in); | ||
422 | |||
423 | memset(stream->out_buffer, 0, stream->out_frames * stream->out_frame_size); | ||
424 | |||
425 | while (adev->in_call) { | ||
426 | if (out_steady) { | ||
427 | if (in_steady) { | ||
428 | stream->resampler->resample_from_provider(stream->resampler, | ||
429 | stream->out_buffer, | ||
430 | &frames); | ||
431 | } else { | ||
432 | avail = pcm_avail_update(stream->pcm_in); | ||
433 | if (avail > 0) { | ||
434 | in_steady = true; | ||
435 | continue; | ||
436 | } | ||
437 | } | ||
438 | } else if (++periods == stream->out_config.period_count) { | ||
439 | out_steady = true; | ||
440 | } | ||
441 | |||
442 | if (stream->remix) | ||
443 | stream->remix->remix_func(stream->remix, stream->out_buffer, frames); | ||
444 | |||
445 | ret = pcm_write(stream->pcm_out, stream->out_buffer, | ||
446 | frames * stream->out_frame_size); | ||
447 | if (ret) { | ||
448 | ALOGE("voice_thread_func() failed to write %s: %s", | ||
449 | stream->name, pcm_get_error(stream->pcm_out)); | ||
450 | usleep((frames * 1000000) / stream->out_config.rate); | ||
451 | } | ||
452 | } | ||
453 | |||
454 | return (void*)ret; | ||
455 | } | ||
456 | |||
457 | static void voice_stream_exit(struct j6_voice_stream *stream) | ||
458 | { | ||
459 | if (stream->resampler) { | ||
460 | release_resampler(stream->resampler); | ||
461 | stream->resampler = NULL; | ||
462 | } | ||
463 | |||
464 | if (stream->pcm_out) { | ||
465 | pcm_close(stream->pcm_out); | ||
466 | stream->pcm_out = NULL; | ||
467 | } | ||
468 | |||
469 | if (stream->pcm_in) { | ||
470 | pcm_close(stream->pcm_in); | ||
471 | stream->pcm_in = NULL; | ||
472 | } | ||
473 | |||
474 | if (stream->in_buffer) { | ||
475 | free(stream->in_buffer); | ||
476 | stream->in_buffer = NULL; | ||
477 | stream->in_frames = 0; | ||
478 | } | ||
479 | |||
480 | if (stream->out_buffer) { | ||
481 | free(stream->out_buffer); | ||
482 | stream->out_buffer = NULL; | ||
483 | stream->out_frames = 0; | ||
484 | } | ||
485 | |||
486 | if (stream->remix) { | ||
487 | free(stream->remix); | ||
488 | stream->remix = NULL; | ||
489 | } | ||
490 | |||
491 | if (stream->name) { | ||
492 | free(stream->name); | ||
493 | stream->name = NULL; | ||
494 | } | ||
495 | } | ||
496 | |||
497 | static int voice_stream_init(struct j6_voice_stream *stream, | ||
498 | unsigned int in_card, | ||
499 | unsigned int in_port, | ||
500 | unsigned int out_card, | ||
501 | unsigned int out_port) | ||
502 | { | ||
503 | struct j6_audio_device *adev = stream->dev; | ||
504 | int frames; | ||
505 | int ret; | ||
506 | |||
507 | stream->buf_provider.get_next_buffer = voice_get_next_buffer; | ||
508 | stream->buf_provider.release_buffer = voice_release_buffer; | ||
509 | ret = create_resampler(stream->in_config.rate, | ||
510 | stream->out_config.rate, | ||
511 | stream->in_config.channels, | ||
512 | RESAMPLER_QUALITY_DEFAULT, | ||
513 | &stream->buf_provider, | ||
514 | &stream->resampler); | ||
515 | if (ret) { | ||
516 | ALOGE("voice_stream_init() failed to create %s resampler %d", stream->name, ret); | ||
517 | return ret; | ||
518 | } | ||
519 | |||
520 | stream->pcm_in = pcm_open(in_card, in_port, | ||
521 | PCM_IN | PCM_MONOTONIC, | ||
522 | &stream->in_config); | ||
523 | stream->pcm_out = pcm_open(out_card, out_port, | ||
524 | PCM_OUT | PCM_MONOTONIC, | ||
525 | &stream->out_config); | ||
526 | |||
527 | if (!pcm_is_ready(stream->pcm_in) || !pcm_is_ready(stream->pcm_out)) { | ||
528 | ALOGE("voice_stream_init() failed to open pcm %s devices", stream->name); | ||
529 | voice_stream_exit(stream); | ||
530 | return -ENODEV; | ||
531 | } | ||
532 | |||
533 | /* | ||
534 | * Choose the largest frame size since mono-to-stereo and | ||
535 | * stereo-to-mono conversion happens in place | ||
536 | */ | ||
537 | stream->in_frame_size = pcm_frames_to_bytes(stream->pcm_in, 1); | ||
538 | stream->out_frame_size = pcm_frames_to_bytes(stream->pcm_out, 1); | ||
539 | frames = (stream->in_frame_size > stream->out_frame_size) ? | ||
540 | stream->in_frame_size : stream->out_frame_size; | ||
541 | |||
542 | /* out_buffer will store the resampled data */ | ||
543 | stream->out_frames = stream->out_config.period_size; | ||
544 | stream->out_buffer = malloc(stream->out_frames * frames); | ||
545 | |||
546 | /* in_buffer will store the frames recorded from the PCM device */ | ||
547 | stream->in_frames = (stream->out_frames * stream->in_config.rate) / stream->out_config.rate + | ||
548 | RESAMPLER_HEADROOM_FRAMES; | ||
549 | stream->in_buffer = malloc(stream->in_frames * frames); | ||
550 | |||
551 | if (!stream->in_buffer || !stream->out_buffer) { | ||
552 | ALOGE("voice_stream_init() failed to allocate %s buffers", stream->name); | ||
553 | voice_stream_exit(stream); | ||
554 | return -ENOMEM; | ||
555 | } | ||
556 | |||
557 | ret = setup_voice_remix(stream); | ||
558 | if (ret) { | ||
559 | ALOGE("voice_stream_init() failed to setup remix %d", ret); | ||
560 | voice_stream_exit(stream); | ||
561 | } | ||
562 | |||
563 | return ret; | ||
564 | } | ||
565 | |||
566 | static int enter_voice_call(struct j6_audio_device *adev) | ||
567 | { | ||
568 | struct j6_voice *voice = &adev->voice; | ||
569 | int ret; | ||
570 | |||
571 | ALOGI("enter_voice_call() entering bluetooth voice call"); | ||
572 | |||
573 | /* Let the primary output switch to a dummy sink */ | ||
574 | if (adev->out) | ||
575 | do_out_standby(adev->out); | ||
576 | |||
577 | /* Uplink: Mic (44.1kHz) -> BT (8kHz) */ | ||
578 | voice->ul.name = strdup("UL"); | ||
579 | voice->ul.in_config = pcm_config_capture; | ||
580 | voice->ul.out_config = pcm_config_bt_out; | ||
581 | voice->ul.dev = adev; | ||
582 | ret = voice_stream_init(&voice->ul, adev->card, adev->in_port, | ||
583 | adev->bt_card, adev->bt_port); | ||
584 | if (ret) { | ||
585 | ALOGE("enter_voice_call() failed to init uplink %d", ret); | ||
586 | return ret; | ||
587 | } | ||
588 | |||
589 | /* Downlink: BT (8kHz) -> HP/Spk (44.1kHz) */ | ||
590 | voice->dl.name = strdup("DL"); | ||
591 | voice->dl.in_config = pcm_config_bt_in; | ||
592 | voice->dl.out_config = pcm_config_playback; | ||
593 | voice->dl.dev = adev; | ||
594 | ret = voice_stream_init(&voice->dl, adev->bt_card, adev->bt_port, | ||
595 | adev->card, adev->out_port); | ||
596 | if (ret) { | ||
597 | ALOGE("enter_voice_call() failed to init downlink %d", ret); | ||
598 | goto err_dl_init; | ||
599 | } | ||
600 | |||
601 | adev->in_call = true; | ||
602 | |||
603 | /* Create uplink thread: Mic -> BT */ | ||
604 | ret = pthread_create(&voice->ul.thread, NULL, voice_thread_func, &voice->ul); | ||
605 | if (ret) { | ||
606 | ALOGE("enter_voice_call() failed to create uplink thread %d", ret); | ||
607 | adev->in_call = false; | ||
608 | goto err_ul_thread; | ||
609 | } | ||
610 | |||
611 | /* Create downlink thread: BT -> HP/Spk */ | ||
612 | ret = pthread_create(&voice->dl.thread, NULL, voice_thread_func, &voice->dl); | ||
613 | if (ret) { | ||
614 | ALOGE("enter_voice_call() failed to create downlink thread %d", ret); | ||
615 | adev->in_call = false; | ||
616 | goto err_dl_thread; | ||
617 | } | ||
618 | |||
619 | return 0; | ||
620 | |||
621 | err_dl_thread: | ||
622 | pthread_join(voice->ul.thread, NULL); | ||
623 | err_ul_thread: | ||
624 | voice_stream_exit(&voice->ul); | ||
625 | err_dl_init: | ||
626 | voice_stream_exit(&voice->dl); | ||
627 | |||
628 | return ret; | ||
629 | } | ||
630 | |||
631 | static void leave_voice_call(struct j6_audio_device *adev) | ||
632 | { | ||
633 | struct j6_voice *voice = &adev->voice; | ||
634 | struct j6_voice_stream *ul = &voice->ul; | ||
635 | struct j6_voice_stream *dl = &voice->dl; | ||
636 | void *ret; | ||
637 | |||
638 | ALOGI("leave_voice_call() leaving bluetooth voice call"); | ||
639 | |||
640 | adev->in_call = false; | ||
641 | |||
642 | if (ul->pcm_out) | ||
643 | pcm_stop(ul->pcm_out); | ||
644 | if (dl->pcm_in) | ||
645 | pcm_stop(dl->pcm_in); | ||
646 | |||
647 | pthread_join(voice->dl.thread, &ret); | ||
648 | pthread_join(voice->ul.thread, &ret); | ||
649 | |||
650 | voice_stream_exit(&voice->dl); | ||
651 | voice_stream_exit(&voice->ul); | ||
652 | |||
653 | /* Let the primary output switch back to its ALSA PCM device */ | ||
654 | if (adev->out) | ||
655 | do_out_standby(adev->out); | ||
656 | } | ||
657 | |||
658 | static uint32_t time_diff(struct timespec t1, struct timespec t0) | ||
659 | { | ||
660 | struct timespec temp; | ||
661 | |||
662 | if ((t1.tv_nsec - t0.tv_nsec) < 0) { | ||
663 | temp.tv_sec = t1.tv_sec - t0.tv_sec-1; | ||
664 | temp.tv_nsec = 1000000000UL + t1.tv_nsec - t0.tv_nsec; | ||
665 | } else { | ||
666 | temp.tv_sec = t1.tv_sec - t0.tv_sec; | ||
667 | temp.tv_nsec = t1.tv_nsec - t0.tv_nsec; | ||
668 | } | ||
669 | |||
670 | return (temp.tv_sec * 1000000UL + temp.tv_nsec / 1000); | ||
671 | } | ||
672 | |||
673 | /* audio HAL functions */ | ||
674 | |||
675 | static uint32_t out_get_sample_rate(const struct audio_stream *stream) | ||
676 | { | ||
677 | uint32_t rate = PLAYBACK_SAMPLE_RATE; | ||
678 | |||
679 | UNUSED(stream); | ||
680 | ALOGVV("out_get_sample_rate() stream=%p rate=%u", stream, rate); | ||
681 | |||
682 | return rate; | ||
683 | } | ||
684 | |||
685 | static int out_set_sample_rate(struct audio_stream *stream, uint32_t rate) | ||
686 | { | ||
687 | UNUSED(stream); | ||
688 | UNUSED(rate); | ||
689 | ALOGVV("out_set_sample_rate() stream=%p rate=%u", stream, rate); | ||
690 | |||
691 | return 0; | ||
692 | } | ||
693 | |||
694 | static size_t out_get_buffer_size(const struct audio_stream *stream) | ||
695 | { | ||
696 | const struct audio_stream_out *s = (const struct audio_stream_out *)stream; | ||
697 | uint32_t frames = ((PLAYBACK_PERIOD_SIZE + 15) / 16) * 16; | ||
698 | size_t bytes = frames * audio_stream_out_frame_size(s); | ||
699 | |||
700 | ALOGVV("out_get_buffer_size() stream=%p frames=%u bytes=%u", stream, frames, bytes); | ||
701 | |||
702 | return bytes; | ||
703 | } | ||
704 | |||
705 | static audio_channel_mask_t out_get_channels(const struct audio_stream *stream) | ||
706 | { | ||
707 | audio_channel_mask_t channels = AUDIO_CHANNEL_OUT_STEREO; | ||
708 | |||
709 | UNUSED(stream); | ||
710 | ALOGVV("out_get_channels() stream=%p channels=%u", stream, popcount(channels)); | ||
711 | |||
712 | return channels; | ||
713 | } | ||
714 | |||
715 | static audio_format_t out_get_format(const struct audio_stream *stream) | ||
716 | { | ||
717 | audio_format_t format = AUDIO_FORMAT_PCM_16_BIT; | ||
718 | |||
719 | UNUSED(stream); | ||
720 | ALOGVV("out_set_format() stream=%p format=0x%08x (%u bits/sample)", | ||
721 | stream, format, audio_bytes_per_sample(format) << 3); | ||
722 | |||
723 | return format; | ||
724 | } | ||
725 | |||
726 | static int out_set_format(struct audio_stream *stream, audio_format_t format) | ||
727 | { | ||
728 | UNUSED(stream); | ||
729 | ALOGVV("out_set_format() stream=%p format=0x%08x (%u bits/sample)", | ||
730 | stream, format, audio_bytes_per_sample(format) << 3); | ||
731 | |||
732 | if (format != AUDIO_FORMAT_PCM_16_BIT) { | ||
733 | return -ENOSYS; | ||
734 | } else { | ||
735 | return 0; | ||
736 | } | ||
737 | } | ||
738 | |||
739 | /* must be called with locks held */ | ||
740 | static void do_out_standby(struct j6_stream_out *out) | ||
741 | { | ||
742 | struct j6_audio_device *adev = out->dev; | ||
743 | |||
744 | if (!out->standby) { | ||
745 | if (adev->mode != AUDIO_MODE_IN_CALL) { | ||
746 | ALOGI("do_out_standby() close card %u port %u", adev->card, adev->out_port); | ||
747 | pcm_close(out->pcm); | ||
748 | out->pcm = NULL; | ||
749 | } else { | ||
750 | ALOGI("do_out_standby() close dummy card"); | ||
751 | } | ||
752 | out->standby = true; | ||
753 | } | ||
754 | } | ||
755 | |||
756 | static int out_standby(struct audio_stream *stream) | ||
757 | { | ||
758 | struct j6_stream_out *out = (struct j6_stream_out *)(stream); | ||
759 | struct j6_audio_device *adev = out->dev; | ||
760 | |||
761 | ALOGV("out_standby() stream=%p", out); | ||
762 | pthread_mutex_lock(&adev->lock); | ||
763 | pthread_mutex_lock(&out->lock); | ||
764 | do_out_standby(out); | ||
765 | pthread_mutex_unlock(&out->lock); | ||
766 | pthread_mutex_unlock(&adev->lock); | ||
767 | |||
768 | return 0; | ||
769 | } | ||
770 | |||
771 | static int out_dump(const struct audio_stream *stream, int fd) | ||
772 | { | ||
773 | UNUSED(stream); | ||
774 | UNUSED(fd); | ||
775 | |||
776 | return 0; | ||
777 | } | ||
778 | |||
779 | static int out_set_parameters(struct audio_stream *stream, const char *kvpairs) | ||
780 | { | ||
781 | struct j6_stream_out *out = (struct j6_stream_out *)(stream); | ||
782 | struct j6_audio_device *adev = out->dev; | ||
783 | struct str_parms *parms; | ||
784 | char value[32]; | ||
785 | int ret; | ||
786 | uint32_t val = 0; | ||
787 | |||
788 | ALOGV("out_set_parameters() stream=%p parameter='%s'", out, kvpairs); | ||
789 | |||
790 | parms = str_parms_create_str(kvpairs); | ||
791 | |||
792 | ret = str_parms_get_str(parms, AUDIO_PARAMETER_STREAM_ROUTING, value, sizeof(value)); | ||
793 | if (ret >= 0) { | ||
794 | val = atoi(value); | ||
795 | pthread_mutex_lock(&adev->lock); | ||
796 | pthread_mutex_lock(&out->lock); | ||
797 | if (val != 0) { | ||
798 | if ((adev->out_device & AUDIO_DEVICE_OUT_ALL) != val) | ||
799 | do_out_standby(out); | ||
800 | |||
801 | /* set the active output device */ | ||
802 | adev->out_device = val; | ||
803 | select_output_device(adev); | ||
804 | } | ||
805 | pthread_mutex_unlock(&out->lock); | ||
806 | pthread_mutex_unlock(&adev->lock); | ||
807 | } | ||
808 | |||
809 | return 0; | ||
810 | } | ||
811 | |||
812 | static char* out_get_parameters(const struct audio_stream *stream, const char *keys) | ||
813 | { | ||
814 | UNUSED(stream); | ||
815 | UNUSED(keys); | ||
816 | |||
817 | return strdup(""); | ||
818 | } | ||
819 | |||
820 | static uint32_t out_get_latency(const struct audio_stream_out *stream) | ||
821 | { | ||
822 | const struct j6_stream_out *out = (const struct j6_stream_out *)(stream); | ||
823 | uint32_t frames = PLAYBACK_BUFFER_SIZE; | ||
824 | uint32_t latency = (frames * 1000) / PLAYBACK_SAMPLE_RATE; | ||
825 | |||
826 | ALOGVV("out_get_latency() stream=%p latency=%u msecs", out, latency); | ||
827 | |||
828 | return latency; | ||
829 | } | ||
830 | |||
831 | static int out_set_volume(struct audio_stream_out *stream, float left, | ||
832 | float right) | ||
833 | { | ||
834 | UNUSED(stream); | ||
835 | UNUSED(left); | ||
836 | UNUSED(right); | ||
837 | |||
838 | return -ENOSYS; | ||
839 | } | ||
840 | |||
841 | static ssize_t out_write(struct audio_stream_out *stream, const void* buffer, | ||
842 | size_t bytes) | ||
843 | { | ||
844 | const struct audio_stream_out *s = (const struct audio_stream_out *)stream; | ||
845 | struct j6_stream_out *out = (struct j6_stream_out *)(stream); | ||
846 | struct j6_audio_device *adev = out->dev; | ||
847 | struct timespec now; | ||
848 | const size_t frame_size = audio_stream_out_frame_size(s); | ||
849 | const size_t frames = bytes / frame_size; | ||
850 | uint32_t rate = out->config.rate; | ||
851 | uint32_t write_usecs = frames * 1000000 / rate; | ||
852 | uint32_t diff_usecs; | ||
853 | int ret = 0; | ||
854 | |||
855 | ALOGVV("out_write() stream=%p buffer=%p size=%u/%u time=%u usecs", | ||
856 | out, buffer, frames, rate, write_usecs); | ||
857 | |||
858 | pthread_mutex_lock(&adev->lock); | ||
859 | pthread_mutex_lock(&out->lock); | ||
860 | |||
861 | if (out->standby) { | ||
862 | if (!adev->in_call) { | ||
863 | select_output_device(adev); | ||
864 | |||
865 | ALOGI("out_write() open card %u port %u", adev->card, adev->out_port); | ||
866 | out->pcm = pcm_open(adev->card, adev->out_port, | ||
867 | PCM_OUT | PCM_MONOTONIC, | ||
868 | &out->config); | ||
869 | if (!pcm_is_ready(out->pcm)) { | ||
870 | ALOGE("out_write() failed to open pcm out: %s", pcm_get_error(out->pcm)); | ||
871 | pcm_close(out->pcm); | ||
872 | out->pcm = NULL; | ||
873 | ret = -ENODEV; | ||
874 | } | ||
875 | } else { | ||
876 | ALOGI("out_write() open dummy port"); | ||
877 | clock_gettime(CLOCK_REALTIME, &out->last); | ||
878 | } | ||
879 | |||
880 | if (ret) { | ||
881 | usleep(write_usecs); /* limits the rate of error messages */ | ||
882 | pthread_mutex_unlock(&out->lock); | ||
883 | pthread_mutex_unlock(&adev->lock); | ||
884 | return ret; | ||
885 | } | ||
886 | |||
887 | out->standby = false; | ||
888 | } | ||
889 | |||
890 | pthread_mutex_unlock(&adev->lock); | ||
891 | |||
892 | if (!adev->in_call) { | ||
893 | ret = pcm_write(out->pcm, buffer, bytes); | ||
894 | if (ret) { | ||
895 | ALOGE("out_write() failed to write audio data %d", ret); | ||
896 | usleep(write_usecs); /* limits the rate of error messages */ | ||
897 | } | ||
898 | } else { | ||
899 | clock_gettime(CLOCK_REALTIME, &now); | ||
900 | diff_usecs = time_diff(now, out->last); | ||
901 | if (write_usecs > diff_usecs) | ||
902 | usleep(write_usecs - diff_usecs); | ||
903 | |||
904 | clock_gettime(CLOCK_REALTIME, &out->last); | ||
905 | } | ||
906 | |||
907 | out->written += frames; | ||
908 | |||
909 | pthread_mutex_unlock(&out->lock); | ||
910 | |||
911 | return bytes; | ||
912 | } | ||
913 | |||
914 | static int out_get_render_position(const struct audio_stream_out *stream, | ||
915 | uint32_t *dsp_frames) | ||
916 | { | ||
917 | UNUSED(stream); | ||
918 | UNUSED(dsp_frames); | ||
919 | |||
920 | return -EINVAL; | ||
921 | } | ||
922 | |||
923 | static int out_add_audio_effect(const struct audio_stream *stream, effect_handle_t effect) | ||
924 | { | ||
925 | UNUSED(stream); | ||
926 | UNUSED(effect); | ||
927 | |||
928 | return 0; | ||
929 | } | ||
930 | |||
931 | static int out_remove_audio_effect(const struct audio_stream *stream, effect_handle_t effect) | ||
932 | { | ||
933 | UNUSED(stream); | ||
934 | UNUSED(effect); | ||
935 | |||
936 | return 0; | ||
937 | } | ||
938 | |||
939 | static int out_get_next_write_timestamp(const struct audio_stream_out *stream, | ||
940 | int64_t *timestamp) | ||
941 | { | ||
942 | UNUSED(stream); | ||
943 | UNUSED(timestamp); | ||
944 | |||
945 | return -EINVAL; | ||
946 | } | ||
947 | |||
948 | static int out_get_presentation_position(const struct audio_stream_out *stream, | ||
949 | uint64_t *frames, struct timespec *timestamp) | ||
950 | { | ||
951 | struct j6_stream_out *out = (struct j6_stream_out *)(stream); | ||
952 | struct j6_audio_device *adev = out->dev; | ||
953 | int64_t signed_frames = -1; | ||
954 | size_t avail; | ||
955 | int ret = -1; | ||
956 | |||
957 | pthread_mutex_lock(&out->lock); | ||
958 | |||
959 | if (!adev->in_call) { | ||
960 | if (pcm_get_htimestamp(out->pcm, &avail, timestamp) == 0) { | ||
961 | signed_frames = out->written - pcm_get_buffer_size(out->pcm) + avail; | ||
962 | } | ||
963 | } else { | ||
964 | clock_gettime(CLOCK_MONOTONIC, timestamp); | ||
965 | signed_frames = out->written + | ||
966 | (time_diff(*timestamp, out->last) * out->config.rate) / 1000000; | ||
967 | } | ||
968 | |||
969 | /* It would be unusual for this value to be negative, but check just in case ... */ | ||
970 | if (signed_frames >= 0) { | ||
971 | *frames = signed_frames; | ||
972 | ret = 0; | ||
973 | } | ||
974 | |||
975 | pthread_mutex_unlock(&out->lock); | ||
976 | |||
977 | return ret; | ||
978 | } | ||
979 | |||
980 | /** audio_stream_in implementation **/ | ||
981 | static uint32_t in_get_sample_rate(const struct audio_stream *stream) | ||
982 | { | ||
983 | const struct j6_stream_in *in = (const struct j6_stream_in *)(stream); | ||
984 | |||
985 | ALOGVV("in_get_sample_rate() stream=%p rate=%u", stream, in->requested_rate); | ||
986 | |||
987 | return in->requested_rate; | ||
988 | } | ||
989 | |||
990 | static int in_set_sample_rate(struct audio_stream *stream, uint32_t rate) | ||
991 | { | ||
992 | ALOGV("in_set_sample_rate() stream=%p rate=%u", stream, rate); | ||
993 | |||
994 | return 0; | ||
995 | } | ||
996 | |||
997 | static size_t in_get_buffer_size(const struct audio_stream *stream) | ||
998 | { | ||
999 | const struct j6_stream_in *in = (const struct j6_stream_in *)(stream); | ||
1000 | |||
1001 | size_t bytes = get_input_buffer_size(in->requested_rate, | ||
1002 | AUDIO_FORMAT_PCM_16_BIT, | ||
1003 | in->requested_channels); | ||
1004 | |||
1005 | ALOGVV("in_get_buffer_size() stream=%p bytes=%u", in, bytes); | ||
1006 | |||
1007 | return bytes; | ||
1008 | } | ||
1009 | |||
1010 | static audio_channel_mask_t in_get_channels(const struct audio_stream *stream) | ||
1011 | { | ||
1012 | const struct j6_stream_in *in = (const struct j6_stream_in *)(stream); | ||
1013 | audio_channel_mask_t channels = audio_channel_in_mask_from_count(in->requested_channels); | ||
1014 | |||
1015 | ALOGVV("in_get_channels() stream=%p channels=%u", in, in->requested_channels); | ||
1016 | |||
1017 | return channels; | ||
1018 | } | ||
1019 | |||
1020 | static audio_format_t in_get_format(const struct audio_stream *stream) | ||
1021 | { | ||
1022 | audio_format_t format = AUDIO_FORMAT_PCM_16_BIT; | ||
1023 | |||
1024 | UNUSED(stream); | ||
1025 | ALOGVV("in_set_format() stream=%p format=0x%08x (%u bits/sample)", | ||
1026 | stream, format, audio_bytes_per_sample(format) << 3); | ||
1027 | |||
1028 | return format; | ||
1029 | } | ||
1030 | |||
1031 | static int in_set_format(struct audio_stream *stream, audio_format_t format) | ||
1032 | { | ||
1033 | UNUSED(stream); | ||
1034 | ALOGV("in_set_format() stream=%p format=0x%08x (%u bits/sample)", | ||
1035 | stream, format, audio_bytes_per_sample(format) << 3); | ||
1036 | |||
1037 | if (format != AUDIO_FORMAT_PCM_16_BIT) { | ||
1038 | return -ENOSYS; | ||
1039 | } else { | ||
1040 | return 0; | ||
1041 | } | ||
1042 | } | ||
1043 | |||
1044 | /* must be called with locks held */ | ||
1045 | static void do_in_standby(struct j6_stream_in *in) | ||
1046 | { | ||
1047 | struct j6_audio_device *adev = in->dev; | ||
1048 | |||
1049 | if (!in->standby) { | ||
1050 | ALOGI("do_in_standby() close card %u port %u", adev->card, adev->out_port); | ||
1051 | pcm_close(in->pcm); | ||
1052 | in->pcm = NULL; | ||
1053 | in->standby = true; | ||
1054 | } | ||
1055 | } | ||
1056 | |||
1057 | static int in_standby(struct audio_stream *stream) | ||
1058 | { | ||
1059 | struct j6_stream_in *in = (struct j6_stream_in *)(stream); | ||
1060 | struct j6_audio_device *adev = in->dev; | ||
1061 | |||
1062 | ALOGV("in_standby() stream=%p", in); | ||
1063 | pthread_mutex_lock(&adev->lock); | ||
1064 | pthread_mutex_lock(&in->lock); | ||
1065 | do_in_standby(in); | ||
1066 | pthread_mutex_unlock(&in->lock); | ||
1067 | pthread_mutex_unlock(&adev->lock); | ||
1068 | |||
1069 | return 0; | ||
1070 | } | ||
1071 | |||
1072 | static int in_dump(const struct audio_stream *stream, int fd) | ||
1073 | { | ||
1074 | UNUSED(stream); | ||
1075 | UNUSED(fd); | ||
1076 | |||
1077 | return 0; | ||
1078 | } | ||
1079 | |||
1080 | static int in_set_parameters(struct audio_stream *stream, const char *kvpairs) | ||
1081 | { | ||
1082 | struct j6_stream_in *in = (struct j6_stream_in *)(stream); | ||
1083 | struct j6_audio_device *adev = in->dev; | ||
1084 | struct str_parms *parms; | ||
1085 | char value[32]; | ||
1086 | int ret; | ||
1087 | uint32_t val = 0; | ||
1088 | |||
1089 | ALOGV("in_set_parameters() stream=%p parameter='%s'", stream, kvpairs); | ||
1090 | |||
1091 | parms = str_parms_create_str(kvpairs); | ||
1092 | |||
1093 | /* Nothing to do for AUDIO_PARAMETER_STREAM_INPUT_SOURCE, so it's ignored */ | ||
1094 | |||
1095 | ret = str_parms_get_str(parms, AUDIO_PARAMETER_STREAM_ROUTING, value, sizeof(value)); | ||
1096 | if (ret >= 0) { | ||
1097 | val = atoi(value); | ||
1098 | pthread_mutex_lock(&adev->lock); | ||
1099 | pthread_mutex_lock(&in->lock); | ||
1100 | if (val != 0) { | ||
1101 | if ((adev->in_device & AUDIO_DEVICE_IN_ALL) != val) | ||
1102 | do_in_standby(in); | ||
1103 | |||
1104 | /* set the active input device */ | ||
1105 | adev->in_device = val; | ||
1106 | select_input_device(adev); | ||
1107 | } | ||
1108 | pthread_mutex_unlock(&in->lock); | ||
1109 | pthread_mutex_unlock(&adev->lock); | ||
1110 | } | ||
1111 | |||
1112 | return 0; | ||
1113 | } | ||
1114 | |||
1115 | static char * in_get_parameters(const struct audio_stream *stream, | ||
1116 | const char *keys) | ||
1117 | { | ||
1118 | UNUSED(stream); | ||
1119 | UNUSED(keys); | ||
1120 | |||
1121 | return strdup(""); | ||
1122 | } | ||
1123 | |||
1124 | static int in_set_gain(struct audio_stream_in *stream, float gain) | ||
1125 | { | ||
1126 | UNUSED(stream); | ||
1127 | UNUSED(gain); | ||
1128 | |||
1129 | return 0; | ||
1130 | } | ||
1131 | |||
1132 | static int get_next_buffer(struct resampler_buffer_provider *buffer_provider, | ||
1133 | struct resampler_buffer* buffer) | ||
1134 | { | ||
1135 | struct j6_stream_in *in; | ||
1136 | struct buffer_remix *remix; | ||
1137 | |||
1138 | if (buffer_provider == NULL || buffer == NULL) | ||
1139 | return -EINVAL; | ||
1140 | |||
1141 | in = (struct j6_stream_in *)((char *)buffer_provider - | ||
1142 | offsetof(struct j6_stream_in, buf_provider)); | ||
1143 | |||
1144 | if (in->pcm == NULL) { | ||
1145 | buffer->raw = NULL; | ||
1146 | buffer->frame_count = 0; | ||
1147 | in->read_status = -ENODEV; | ||
1148 | return -ENODEV; | ||
1149 | } | ||
1150 | |||
1151 | if (in->frames_in == 0) { | ||
1152 | in->read_status = pcm_read(in->pcm, | ||
1153 | (void*)in->buffer, | ||
1154 | buffer->frame_count * in->hw_frame_size); | ||
1155 | if (in->read_status != 0) { | ||
1156 | ALOGE("get_next_buffer() pcm_read error %d", in->read_status); | ||
1157 | buffer->raw = NULL; | ||
1158 | buffer->frame_count = 0; | ||
1159 | return in->read_status; | ||
1160 | } | ||
1161 | in->frames_in = buffer->frame_count; | ||
1162 | |||
1163 | remix = in->remix; | ||
1164 | if (remix) | ||
1165 | remix->remix_func(remix, in->buffer, in->frames_in); | ||
1166 | } | ||
1167 | |||
1168 | buffer->frame_count = (buffer->frame_count > in->frames_in) ? | ||
1169 | in->frames_in : buffer->frame_count; | ||
1170 | buffer->i16 = in->buffer; | ||
1171 | |||
1172 | return in->read_status; | ||
1173 | } | ||
1174 | |||
1175 | static void release_buffer(struct resampler_buffer_provider *buffer_provider, | ||
1176 | struct resampler_buffer* buffer) | ||
1177 | { | ||
1178 | struct j6_stream_in *in; | ||
1179 | |||
1180 | if (buffer_provider == NULL || buffer == NULL) | ||
1181 | return; | ||
1182 | |||
1183 | in = (struct j6_stream_in *)((char *)buffer_provider - | ||
1184 | offsetof(struct j6_stream_in, buf_provider)); | ||
1185 | |||
1186 | in->frames_in -= buffer->frame_count; | ||
1187 | } | ||
1188 | |||
1189 | /* | ||
1190 | * read_frames() reads frames from kernel driver, down samples to capture rate | ||
1191 | * if necessary and output the number of frames requested to the buffer specified | ||
1192 | */ | ||
1193 | static ssize_t read_frames(struct j6_stream_in *in, void *buffer, ssize_t frames) | ||
1194 | { | ||
1195 | const struct audio_stream_in *s = (const struct audio_stream_in *)in; | ||
1196 | ssize_t frames_wr = 0; | ||
1197 | size_t frame_size; | ||
1198 | |||
1199 | ALOGVV("read_frames() stream=%p frames=%u", in, frames); | ||
1200 | |||
1201 | if (in->remix) | ||
1202 | frame_size = audio_stream_in_frame_size(s); | ||
1203 | else | ||
1204 | frame_size = in->hw_frame_size; | ||
1205 | |||
1206 | while (frames_wr < frames) { | ||
1207 | size_t frames_rd = frames - frames_wr; | ||
1208 | |||
1209 | if (in->resampler) { | ||
1210 | in->resampler->resample_from_provider(in->resampler, | ||
1211 | (int16_t *)((char *)buffer + frames_wr * frame_size), | ||
1212 | &frames_rd); | ||
1213 | } else { | ||
1214 | struct resampler_buffer buf = { | ||
1215 | { raw : NULL, }, | ||
1216 | frame_count : frames_rd, | ||
1217 | }; | ||
1218 | get_next_buffer(&in->buf_provider, &buf); | ||
1219 | if (buf.raw) { | ||
1220 | memcpy((char *)buffer + frames_wr * frame_size, | ||
1221 | buf.raw, | ||
1222 | buf.frame_count * frame_size); | ||
1223 | frames_rd = buf.frame_count; | ||
1224 | } | ||
1225 | release_buffer(&in->buf_provider, &buf); | ||
1226 | } | ||
1227 | |||
1228 | /* in->read_status is updated by getNextBuffer() also called by | ||
1229 | * in->resampler->resample_from_provider() */ | ||
1230 | if (in->read_status != 0) | ||
1231 | return in->read_status; | ||
1232 | |||
1233 | frames_wr += frames_rd; | ||
1234 | } | ||
1235 | |||
1236 | return frames_wr; | ||
1237 | } | ||
1238 | |||
1239 | static ssize_t in_read(struct audio_stream_in *stream, void* buffer, | ||
1240 | size_t bytes) | ||
1241 | { | ||
1242 | const struct audio_stream_in *s = (const struct audio_stream_in *)stream; | ||
1243 | struct j6_stream_in *in = (struct j6_stream_in *)(stream); | ||
1244 | struct j6_audio_device *adev = in->dev; | ||
1245 | const size_t frame_size = audio_stream_in_frame_size(stream); | ||
1246 | const size_t frames = bytes / frame_size; | ||
1247 | uint32_t rate = in_get_sample_rate(&stream->common); | ||
1248 | uint32_t read_usecs = frames * 1000000 / rate; | ||
1249 | int ret; | ||
1250 | |||
1251 | ALOGVV("in_read() stream=%p buffer=%p size=%u/%u time=%u usecs", | ||
1252 | stream, buffer, frames, rate, read_usecs); | ||
1253 | |||
1254 | pthread_mutex_lock(&adev->lock); | ||
1255 | pthread_mutex_lock(&in->lock); | ||
1256 | |||
1257 | if (in->standby) { | ||
1258 | select_input_device(adev); | ||
1259 | |||
1260 | ALOGI("in_read() open card %u port %u", adev->card, adev->in_port); | ||
1261 | in->pcm = pcm_open(adev->card, adev->in_port, | ||
1262 | PCM_IN | PCM_MONOTONIC, | ||
1263 | &in->config); | ||
1264 | if (!pcm_is_ready(in->pcm)) { | ||
1265 | ALOGE("in_read() failed to open pcm in: %s", pcm_get_error(in->pcm)); | ||
1266 | pcm_close(in->pcm); | ||
1267 | in->pcm = NULL; | ||
1268 | usleep(read_usecs); /* limits the rate of error messages */ | ||
1269 | pthread_mutex_unlock(&in->lock); | ||
1270 | pthread_mutex_unlock(&adev->lock); | ||
1271 | return -ENODEV; | ||
1272 | } | ||
1273 | |||
1274 | /* if no supported sample rate is available, use the resampler */ | ||
1275 | if (in->resampler) { | ||
1276 | in->resampler->reset(in->resampler); | ||
1277 | in->frames_in = 0; | ||
1278 | } | ||
1279 | |||
1280 | in->standby = false; | ||
1281 | } | ||
1282 | |||
1283 | pthread_mutex_unlock(&adev->lock); | ||
1284 | |||
1285 | if (in->resampler || in->remix) | ||
1286 | ret = read_frames(in, buffer, frames); | ||
1287 | else | ||
1288 | ret = pcm_read(in->pcm, buffer, bytes); | ||
1289 | |||
1290 | if (ret < 0) { | ||
1291 | ALOGE("in_read() failed to read audio data %d", ret); | ||
1292 | usleep(read_usecs); /* limits the rate of error messages */ | ||
1293 | memset(buffer, 0, bytes); | ||
1294 | } else if (adev->mic_mute) { | ||
1295 | memset(buffer, 0, bytes); | ||
1296 | } | ||
1297 | |||
1298 | pthread_mutex_unlock(&in->lock); | ||
1299 | |||
1300 | return bytes; | ||
1301 | } | ||
1302 | |||
1303 | static uint32_t in_get_input_frames_lost(struct audio_stream_in *stream) | ||
1304 | { | ||
1305 | UNUSED(stream); | ||
1306 | ALOGVV("in_get_input_frames_lost() stream=%p frames=%u", stream, 0); | ||
1307 | |||
1308 | return 0; | ||
1309 | } | ||
1310 | |||
1311 | static int in_add_audio_effect(const struct audio_stream *stream, effect_handle_t effect) | ||
1312 | { | ||
1313 | UNUSED(stream); | ||
1314 | UNUSED(effect); | ||
1315 | |||
1316 | return 0; | ||
1317 | } | ||
1318 | |||
1319 | static int in_remove_audio_effect(const struct audio_stream *stream, effect_handle_t effect) | ||
1320 | { | ||
1321 | UNUSED(stream); | ||
1322 | UNUSED(effect); | ||
1323 | |||
1324 | return 0; | ||
1325 | } | ||
1326 | |||
1327 | static int adev_open_output_stream(struct audio_hw_device *dev, | ||
1328 | audio_io_handle_t handle, | ||
1329 | audio_devices_t devices, | ||
1330 | audio_output_flags_t flags, | ||
1331 | struct audio_config *config, | ||
1332 | struct audio_stream_out **stream_out, | ||
1333 | const char *address) | ||
1334 | { | ||
1335 | struct j6_audio_device *adev = (struct j6_audio_device *)dev; | ||
1336 | struct j6_stream_out *out; | ||
1337 | |||
1338 | UNUSED(handle); | ||
1339 | UNUSED(devices); | ||
1340 | UNUSED(address); | ||
1341 | |||
1342 | out = (struct j6_stream_out *)calloc(1, sizeof(struct j6_stream_out)); | ||
1343 | if (!out) | ||
1344 | return -ENOMEM; | ||
1345 | |||
1346 | ALOGV("adev_open_output_stream() stream=%p rate=%u channels=%u " | ||
1347 | "format=0x%08x flags=0x%08x", | ||
1348 | out, config->sample_rate, popcount(config->channel_mask), | ||
1349 | config->format, flags); | ||
1350 | |||
1351 | pthread_mutex_init(&out->lock, NULL); | ||
1352 | |||
1353 | out->stream.common.get_sample_rate = out_get_sample_rate; | ||
1354 | out->stream.common.set_sample_rate = out_set_sample_rate; | ||
1355 | out->stream.common.get_buffer_size = out_get_buffer_size; | ||
1356 | out->stream.common.get_channels = out_get_channels; | ||
1357 | out->stream.common.get_format = out_get_format; | ||
1358 | out->stream.common.set_format = out_set_format; | ||
1359 | out->stream.common.standby = out_standby; | ||
1360 | out->stream.common.dump = out_dump; | ||
1361 | out->stream.common.set_parameters = out_set_parameters; | ||
1362 | out->stream.common.get_parameters = out_get_parameters; | ||
1363 | out->stream.common.add_audio_effect = out_add_audio_effect; | ||
1364 | out->stream.common.remove_audio_effect = out_remove_audio_effect; | ||
1365 | out->stream.get_latency = out_get_latency; | ||
1366 | out->stream.set_volume = out_set_volume; | ||
1367 | out->stream.write = out_write; | ||
1368 | out->stream.get_render_position = out_get_render_position; | ||
1369 | out->stream.get_next_write_timestamp = out_get_next_write_timestamp; | ||
1370 | out->stream.get_presentation_position = out_get_presentation_position; | ||
1371 | |||
1372 | out->dev = adev; | ||
1373 | out->standby = true; | ||
1374 | out->config = pcm_config_playback; | ||
1375 | out->written = 0; | ||
1376 | adev->out = out; | ||
1377 | |||
1378 | config->format = out_get_format(&out->stream.common); | ||
1379 | config->channel_mask = out_get_channels(&out->stream.common); | ||
1380 | config->sample_rate = out_get_sample_rate(&out->stream.common); | ||
1381 | |||
1382 | *stream_out = &out->stream; | ||
1383 | |||
1384 | return 0; | ||
1385 | } | ||
1386 | |||
1387 | static void adev_close_output_stream(struct audio_hw_device *dev, | ||
1388 | struct audio_stream_out *stream) | ||
1389 | { | ||
1390 | struct j6_audio_device *adev = (struct j6_audio_device *)dev; | ||
1391 | struct j6_stream_out *out = (struct j6_stream_out *)(stream); | ||
1392 | |||
1393 | ALOGV("adev_close_output_stream() stream=%p", out); | ||
1394 | |||
1395 | out_standby(&stream->common); | ||
1396 | out->dev = NULL; | ||
1397 | adev->out = NULL; | ||
1398 | |||
1399 | free(stream); | ||
1400 | } | ||
1401 | |||
1402 | static int adev_set_parameters(struct audio_hw_device *dev, const char *kvpairs) | ||
1403 | { | ||
1404 | UNUSED(dev); | ||
1405 | UNUSED(kvpairs); | ||
1406 | |||
1407 | return -ENOSYS; | ||
1408 | } | ||
1409 | |||
1410 | static char * adev_get_parameters(const struct audio_hw_device *dev, | ||
1411 | const char *keys) | ||
1412 | { | ||
1413 | UNUSED(dev); | ||
1414 | UNUSED(keys); | ||
1415 | |||
1416 | return strdup("");; | ||
1417 | } | ||
1418 | |||
1419 | static int adev_init_check(const struct audio_hw_device *dev) | ||
1420 | { | ||
1421 | UNUSED(dev); | ||
1422 | |||
1423 | return 0; | ||
1424 | } | ||
1425 | |||
1426 | static int adev_set_voice_volume(struct audio_hw_device *dev, float volume) | ||
1427 | { | ||
1428 | UNUSED(dev); | ||
1429 | UNUSED(volume); | ||
1430 | |||
1431 | return -ENOSYS; | ||
1432 | } | ||
1433 | |||
1434 | static int adev_set_master_volume(struct audio_hw_device *dev, float volume) | ||
1435 | { | ||
1436 | UNUSED(dev); | ||
1437 | UNUSED(volume); | ||
1438 | |||
1439 | return -ENOSYS; | ||
1440 | } | ||
1441 | |||
1442 | static int adev_get_master_volume(struct audio_hw_device *dev, float *volume) | ||
1443 | { | ||
1444 | UNUSED(dev); | ||
1445 | UNUSED(volume); | ||
1446 | |||
1447 | return -ENOSYS; | ||
1448 | } | ||
1449 | |||
1450 | static int adev_set_master_mute(struct audio_hw_device *dev, bool muted) | ||
1451 | { | ||
1452 | UNUSED(dev); | ||
1453 | UNUSED(muted); | ||
1454 | |||
1455 | return -ENOSYS; | ||
1456 | } | ||
1457 | |||
1458 | static int adev_get_master_mute(struct audio_hw_device *dev, bool *muted) | ||
1459 | { | ||
1460 | UNUSED(dev); | ||
1461 | UNUSED(muted); | ||
1462 | |||
1463 | return -ENOSYS; | ||
1464 | } | ||
1465 | |||
1466 | static int adev_set_mode(struct audio_hw_device *dev, audio_mode_t mode) | ||
1467 | { | ||
1468 | struct j6_audio_device *adev = (struct j6_audio_device *)dev; | ||
1469 | struct j6_stream_out *out = adev->out; | ||
1470 | int ret = 0; | ||
1471 | |||
1472 | ALOGV("adev_set_mode() mode=0x%08x", mode); | ||
1473 | |||
1474 | pthread_mutex_lock(&adev->lock); | ||
1475 | pthread_mutex_lock(&out->lock); | ||
1476 | |||
1477 | if (adev->mode == mode) { | ||
1478 | ALOGV("adev_set_mode() already in mode=0x%08x", mode); | ||
1479 | goto out; | ||
1480 | } | ||
1481 | |||
1482 | if (mode == AUDIO_MODE_IN_CALL) { | ||
1483 | ret = enter_voice_call(adev); | ||
1484 | if (ret) { | ||
1485 | ALOGE("adev_set_mode() failed to initialize voice call %d", ret); | ||
1486 | goto out; | ||
1487 | } | ||
1488 | } else if (adev->mode == AUDIO_MODE_IN_CALL) { | ||
1489 | leave_voice_call(adev); | ||
1490 | } | ||
1491 | |||
1492 | adev->mode = mode; | ||
1493 | |||
1494 | out: | ||
1495 | pthread_mutex_unlock(&out->lock); | ||
1496 | pthread_mutex_unlock(&adev->lock); | ||
1497 | |||
1498 | return ret; | ||
1499 | } | ||
1500 | |||
1501 | static int adev_set_mic_mute(struct audio_hw_device *dev, bool state) | ||
1502 | { | ||
1503 | struct j6_audio_device *adev = (struct j6_audio_device *)dev; | ||
1504 | |||
1505 | ALOGV("adev_set_mic_mute() state=%s", state ? "mute" : "unmute"); | ||
1506 | adev->mic_mute = state; | ||
1507 | |||
1508 | return 0; | ||
1509 | } | ||
1510 | |||
1511 | static int adev_get_mic_mute(const struct audio_hw_device *dev, bool *state) | ||
1512 | { | ||
1513 | const struct j6_audio_device *adev = (const struct j6_audio_device *)dev; | ||
1514 | |||
1515 | *state = adev->mic_mute; | ||
1516 | ALOGV("adev_get_mic_mute() state=%s", *state ? "mute" : "unmute"); | ||
1517 | |||
1518 | return 0; | ||
1519 | } | ||
1520 | |||
1521 | static size_t adev_get_input_buffer_size(const struct audio_hw_device *dev, | ||
1522 | const struct audio_config *config) | ||
1523 | { | ||
1524 | UNUSED(dev); | ||
1525 | |||
1526 | size_t bytes = get_input_buffer_size(config->sample_rate, | ||
1527 | config->format, | ||
1528 | popcount(config->channel_mask)); | ||
1529 | |||
1530 | ALOGVV("adev_in_get_buffer_size() bytes=%u", bytes); | ||
1531 | |||
1532 | return bytes; | ||
1533 | } | ||
1534 | |||
1535 | static int adev_open_input_stream(struct audio_hw_device *dev, | ||
1536 | audio_io_handle_t handle, | ||
1537 | audio_devices_t devices, | ||
1538 | struct audio_config *config, | ||
1539 | struct audio_stream_in **stream_in, | ||
1540 | audio_input_flags_t flags, | ||
1541 | const char *address, | ||
1542 | audio_source_t source) | ||
1543 | { | ||
1544 | struct j6_audio_device *adev = (struct j6_audio_device *)dev; | ||
1545 | struct j6_stream_in *in; | ||
1546 | int ret; | ||
1547 | |||
1548 | UNUSED(handle); | ||
1549 | UNUSED(devices); | ||
1550 | UNUSED(flags); | ||
1551 | UNUSED(address); | ||
1552 | UNUSED(source); | ||
1553 | |||
1554 | in = (struct j6_stream_in *)calloc(1, sizeof(struct j6_stream_in)); | ||
1555 | if (!in) | ||
1556 | return -ENOMEM; | ||
1557 | |||
1558 | ALOGV("adev_open_input_stream() stream=%p rate=%u channels=%u format=0x%08x", | ||
1559 | in, config->sample_rate, popcount(config->channel_mask), config->format); | ||
1560 | |||
1561 | pthread_mutex_init(&in->lock, NULL); | ||
1562 | |||
1563 | in->stream.common.get_sample_rate = in_get_sample_rate; | ||
1564 | in->stream.common.set_sample_rate = in_set_sample_rate; | ||
1565 | in->stream.common.get_buffer_size = in_get_buffer_size; | ||
1566 | in->stream.common.get_channels = in_get_channels; | ||
1567 | in->stream.common.get_format = in_get_format; | ||
1568 | in->stream.common.set_format = in_set_format; | ||
1569 | in->stream.common.standby = in_standby; | ||
1570 | in->stream.common.dump = in_dump; | ||
1571 | in->stream.common.set_parameters = in_set_parameters; | ||
1572 | in->stream.common.get_parameters = in_get_parameters; | ||
1573 | in->stream.common.add_audio_effect = in_add_audio_effect; | ||
1574 | in->stream.common.remove_audio_effect = in_remove_audio_effect; | ||
1575 | in->stream.set_gain = in_set_gain; | ||
1576 | in->stream.read = in_read; | ||
1577 | in->stream.get_input_frames_lost = in_get_input_frames_lost; | ||
1578 | |||
1579 | in->dev = adev; | ||
1580 | in->standby = true; | ||
1581 | in->config = pcm_config_capture; | ||
1582 | in->requested_rate = config->sample_rate; | ||
1583 | in->requested_channels = popcount(config->channel_mask); | ||
1584 | in->hw_frame_size = in->config.channels * sizeof(int16_t); | ||
1585 | in->remix = NULL; | ||
1586 | in->resampler = NULL; | ||
1587 | in->buffer = NULL; | ||
1588 | adev->in = in; | ||
1589 | |||
1590 | /* in-place stereo-to-mono remix since capture stream is stereo */ | ||
1591 | if (in->requested_channels == 1) { | ||
1592 | ALOGV("adev_open_input_stream() stereo-to-mono remix needed"); | ||
1593 | |||
1594 | /* | ||
1595 | * buffer size is already enough to allow stereo-to-mono remix | ||
1596 | * and resample if needed | ||
1597 | */ | ||
1598 | in->buffer = malloc(2 * in->config.period_size * in->hw_frame_size); | ||
1599 | if (!in->buffer) { | ||
1600 | ret = -ENOMEM; | ||
1601 | goto err1; | ||
1602 | } | ||
1603 | |||
1604 | ret = setup_stereo_to_mono_input_remix(in); | ||
1605 | if (ret) { | ||
1606 | ALOGE("adev_open_input_stream() failed to setup remix %d", ret); | ||
1607 | goto err2; | ||
1608 | } | ||
1609 | } | ||
1610 | |||
1611 | if (in->requested_rate != in->config.rate) { | ||
1612 | ALOGV("adev_open_input_stream() resample needed, req=%uHz got=%uHz", | ||
1613 | in->requested_rate, in->config.rate); | ||
1614 | |||
1615 | in->buf_provider.get_next_buffer = get_next_buffer; | ||
1616 | in->buf_provider.release_buffer = release_buffer; | ||
1617 | ret = create_resampler(in->config.rate, | ||
1618 | in->requested_rate, | ||
1619 | in->requested_channels, | ||
1620 | RESAMPLER_QUALITY_DEFAULT, | ||
1621 | &in->buf_provider, | ||
1622 | &in->resampler); | ||
1623 | if (ret) { | ||
1624 | ALOGE("adev_open_input_stream() failed to create resampler %d", ret); | ||
1625 | goto err3; | ||
1626 | } | ||
1627 | } | ||
1628 | |||
1629 | *stream_in = &in->stream; | ||
1630 | |||
1631 | return 0; | ||
1632 | |||
1633 | err3: | ||
1634 | free(in->remix); | ||
1635 | err2: | ||
1636 | free(in->buffer); | ||
1637 | err1: | ||
1638 | free(in); | ||
1639 | return ret; | ||
1640 | } | ||
1641 | |||
1642 | static void adev_close_input_stream(struct audio_hw_device *dev, | ||
1643 | struct audio_stream_in *stream) | ||
1644 | { | ||
1645 | struct j6_audio_device *adev = (struct j6_audio_device *)dev; | ||
1646 | struct j6_stream_in *in = (struct j6_stream_in *)(stream); | ||
1647 | |||
1648 | ALOGV("adev_close_input_stream() stream=%p", stream); | ||
1649 | |||
1650 | in_standby(&stream->common); | ||
1651 | |||
1652 | if (in->resampler) | ||
1653 | release_resampler(in->resampler); | ||
1654 | in->resampler = NULL; | ||
1655 | |||
1656 | if (in->remix) | ||
1657 | free(in->remix); | ||
1658 | in->remix = NULL; | ||
1659 | |||
1660 | in->dev = NULL; | ||
1661 | adev->in = NULL; | ||
1662 | |||
1663 | free(in->buffer); | ||
1664 | free(in); | ||
1665 | } | ||
1666 | |||
1667 | static int adev_dump(const audio_hw_device_t *device, int fd) | ||
1668 | { | ||
1669 | UNUSED(device); | ||
1670 | UNUSED(fd); | ||
1671 | |||
1672 | return 0; | ||
1673 | } | ||
1674 | |||
1675 | static int adev_close(hw_device_t *device) | ||
1676 | { | ||
1677 | struct j6_audio_device *adev = (struct j6_audio_device *)device; | ||
1678 | |||
1679 | ALOGI("adev_close()"); | ||
1680 | |||
1681 | audio_route_free(adev->route); | ||
1682 | free(device); | ||
1683 | |||
1684 | return 0; | ||
1685 | } | ||
1686 | |||
1687 | static int adev_open(const hw_module_t* module, const char* name, | ||
1688 | hw_device_t** device) | ||
1689 | { | ||
1690 | struct j6_audio_device *adev; | ||
1691 | |||
1692 | ALOGI("adev_open() %s", name); | ||
1693 | |||
1694 | if (strcmp(name, AUDIO_HARDWARE_INTERFACE) != 0) | ||
1695 | return -EINVAL; | ||
1696 | |||
1697 | adev = (struct j6_audio_device*)calloc(1, sizeof(struct j6_audio_device)); | ||
1698 | if (!adev) | ||
1699 | return -ENOMEM; | ||
1700 | |||
1701 | pthread_mutex_init(&adev->lock, NULL); | ||
1702 | |||
1703 | adev->device.common.tag = HARDWARE_DEVICE_TAG; | ||
1704 | adev->device.common.version = AUDIO_DEVICE_API_VERSION_2_0; | ||
1705 | adev->device.common.module = (struct hw_module_t *) module; | ||
1706 | adev->device.common.close = adev_close; | ||
1707 | |||
1708 | adev->device.init_check = adev_init_check; | ||
1709 | adev->device.set_voice_volume = adev_set_voice_volume; | ||
1710 | adev->device.set_master_volume = adev_set_master_volume; | ||
1711 | adev->device.get_master_volume = adev_get_master_volume; | ||
1712 | adev->device.set_master_mute = adev_set_master_mute; | ||
1713 | adev->device.get_master_mute = adev_get_master_mute; | ||
1714 | adev->device.set_mode = adev_set_mode; | ||
1715 | adev->device.set_mic_mute = adev_set_mic_mute; | ||
1716 | adev->device.get_mic_mute = adev_get_mic_mute; | ||
1717 | adev->device.set_parameters = adev_set_parameters; | ||
1718 | adev->device.get_parameters = adev_get_parameters; | ||
1719 | adev->device.get_input_buffer_size = adev_get_input_buffer_size; | ||
1720 | adev->device.open_output_stream = adev_open_output_stream; | ||
1721 | adev->device.close_output_stream = adev_close_output_stream; | ||
1722 | adev->device.open_input_stream = adev_open_input_stream; | ||
1723 | adev->device.close_input_stream = adev_close_input_stream; | ||
1724 | adev->device.dump = adev_dump; | ||
1725 | |||
1726 | adev->in_device = AUDIO_DEVICE_IN_BUILTIN_MIC; | ||
1727 | adev->out_device = AUDIO_DEVICE_OUT_SPEAKER; | ||
1728 | adev->card = 0; | ||
1729 | adev->in_port = 0; | ||
1730 | adev->out_port = 0; | ||
1731 | ALOGI("Media card is hw:%d\n", adev->card); | ||
1732 | adev->bt_card=2; | ||
1733 | adev->bt_port = 0; | ||
1734 | ALOGI("Bluetooth SCO card is hw:%d\n", adev->bt_card); | ||
1735 | |||
1736 | adev->mic_mute = false; | ||
1737 | adev->in_call = false; | ||
1738 | adev->mode = AUDIO_MODE_NORMAL; | ||
1739 | |||
1740 | adev->route = audio_route_init(adev->card, NULL); | ||
1741 | if (!adev->route) { | ||
1742 | ALOGE("Unable to initialize audio routes"); | ||
1743 | free(adev); | ||
1744 | return -EINVAL; | ||
1745 | } | ||
1746 | |||
1747 | *device = &adev->device.common; | ||
1748 | |||
1749 | return 0; | ||
1750 | } | ||
1751 | |||
1752 | static struct hw_module_methods_t hal_module_methods = { | ||
1753 | .open = adev_open, | ||
1754 | }; | ||
1755 | |||
1756 | struct audio_module HAL_MODULE_INFO_SYM = { | ||
1757 | .common = { | ||
1758 | .tag = HARDWARE_MODULE_TAG, | ||
1759 | .module_api_version = AUDIO_MODULE_API_VERSION_0_1, | ||
1760 | .hal_api_version = HARDWARE_HAL_API_VERSION, | ||
1761 | .id = AUDIO_HARDWARE_MODULE_ID, | ||
1762 | .name = "Jacinto6 Audio HAL", | ||
1763 | .author = "Texas Instruments Inc.", | ||
1764 | .methods = &hal_module_methods, | ||
1765 | }, | ||
1766 | }; | ||
diff --git a/audio/primary/mixer_paths.xml b/audio/primary/mixer_paths.xml new file mode 100644 index 0000000..b872afa --- /dev/null +++ b/audio/primary/mixer_paths.xml | |||
@@ -0,0 +1,31 @@ | |||
1 | <mixer> | ||
2 | |||
3 | <!-- Card default routes --> | ||
4 | |||
5 | <!-- Capture: Mic --> | ||
6 | <ctl name="Left PGA Mixer Mic3L Switch" value="1" /> | ||
7 | <ctl name="Right PGA Mixer Mic3R Switch" value="1" /> | ||
8 | <ctl name="PGA Capture Switch" value="1" /> | ||
9 | <ctl name="PGA Capture Volume" value="24" /> | ||
10 | <ctl name="Left Line1L Mux" value="single-ended" /> | ||
11 | <ctl name="Right Line1L Mux" value="single-ended" /> | ||
12 | <ctl name="Left Line1R Mux" value="single-ended" /> | ||
13 | <ctl name="Right Line1R Mux" value="single-ended" /> | ||
14 | |||
15 | <!-- Playback: Line-Out and Headphone --> | ||
16 | <ctl name="Left DAC Mux" value="DAC_L1" /> | ||
17 | <ctl name="Right DAC Mux" value="DAC_R1" /> | ||
18 | <ctl name="PCM Playback Volume" value="127" /> | ||
19 | |||
20 | <ctl name="Left HP Mixer DACL1 Switch" value="1" /> | ||
21 | <ctl name="Right HP Mixer DACR1 Switch" value="1" /> | ||
22 | <ctl name="HP Playback Switch" value="1" /> | ||
23 | <ctl name="HP DAC Playback Volume" value="118" /> | ||
24 | <ctl name="Output Driver Power-On time" value="200ms" /> | ||
25 | |||
26 | <ctl name="Left Line Mixer DACL1 Switch" value="1" /> | ||
27 | <ctl name="Right Line Mixer DACR1 Switch" value="1" /> | ||
28 | <ctl name="Line Playback Switch" value="1" /> | ||
29 | <ctl name="Line DAC Playback Volume" value="118" /> | ||
30 | |||
31 | </mixer> | ||