blob: bb34bf0978fee1b302651add5a2c320f30596bc5 [file] [log] [blame]
Subhash Chandra Bose Naripeddy19dc03b2014-03-10 14:43:05 -07001/*
2 * Copyright (c) 2014, The Linux Foundation. All rights reserved.
3 * Not a Contribution.
4 *
5 * Copyright (C) 2014 The Android Open Source Project
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 */
19
20#define LOG_TAG "audio_hw_utils"
21/* #define LOG_NDEBUG 0 */
22
23#include <errno.h>
24#include <cutils/properties.h>
25#include <cutils/config_utils.h>
26#include <stdlib.h>
27#include <dlfcn.h>
28#include <cutils/str_parms.h>
29#include <cutils/log.h>
30#include <cutils/misc.h>
31
32#include "audio_hw.h"
33#include "platform.h"
34#include "platform_api.h"
35#include "audio_extn.h"
36
37#define AUDIO_OUTPUT_POLICY_VENDOR_CONFIG_FILE "/vendor/etc/audio_output_policy.conf"
38
39#define OUTPUTS_TAG "outputs"
40
41#define DYNAMIC_VALUE_TAG "dynamic"
42#define FLAGS_TAG "flags"
43#define FORMATS_TAG "formats"
44#define SAMPLING_RATES_TAG "sampling_rates"
45#define BIT_WIDTH_TAG "bit_width"
46#define APP_TYPE_TAG "app_type"
47
48#define STRING_TO_ENUM(string) { #string, string }
49#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
50
51struct string_to_enum {
52 const char *name;
53 uint32_t value;
54};
55
56const struct string_to_enum s_flag_name_to_enum_table[] = {
57 STRING_TO_ENUM(AUDIO_OUTPUT_FLAG_DIRECT),
58 STRING_TO_ENUM(AUDIO_OUTPUT_FLAG_PRIMARY),
59 STRING_TO_ENUM(AUDIO_OUTPUT_FLAG_FAST),
60 STRING_TO_ENUM(AUDIO_OUTPUT_FLAG_DEEP_BUFFER),
61 STRING_TO_ENUM(AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD),
62 STRING_TO_ENUM(AUDIO_OUTPUT_FLAG_NON_BLOCKING),
63#ifdef INCALL_MUSIC_ENABLED
64 STRING_TO_ENUM(AUDIO_OUTPUT_FLAG_INCALL_MUSIC),
65#endif
66#ifdef COMPRESS_VOIP_ENABLED
67 STRING_TO_ENUM(AUDIO_OUTPUT_FLAG_VOIP_RX),
68#endif
69};
70
71const struct string_to_enum s_format_name_to_enum_table[] = {
72 STRING_TO_ENUM(AUDIO_FORMAT_PCM_16_BIT),
73 STRING_TO_ENUM(AUDIO_FORMAT_PCM_8_BIT),
74 STRING_TO_ENUM(AUDIO_FORMAT_MP3),
75 STRING_TO_ENUM(AUDIO_FORMAT_AAC),
76 STRING_TO_ENUM(AUDIO_FORMAT_VORBIS),
77#ifdef FORMATS_ENABLED
78 STRING_TO_ENUM(AUDIO_FORMAT_AC3),
79 STRING_TO_ENUM(AUDIO_FORMAT_EAC3),
80 STRING_TO_ENUM(AUDIO_FORMAT_DTS),
81 STRING_TO_ENUM(AUDIO_FORMAT_DTS_LBR),
82 STRING_TO_ENUM(AUDIO_FORMAT_WMA),
83 STRING_TO_ENUM(AUDIO_FORMAT_WMA_PRO),
84 STRING_TO_ENUM(AUDIO_FORMAT_AAC_ADIF),
85 STRING_TO_ENUM(AUDIO_FORMAT_AMR_NB),
86 STRING_TO_ENUM(AUDIO_FORMAT_AMR_WB),
87 STRING_TO_ENUM(AUDIO_FORMAT_AMR_WB_PLUS),
88 STRING_TO_ENUM(AUDIO_FORMAT_EVRC),
89 STRING_TO_ENUM(AUDIO_FORMAT_EVRCB),
90 STRING_TO_ENUM(AUDIO_FORMAT_EVRCWB),
91 STRING_TO_ENUM(AUDIO_FORMAT_QCELP),
92 STRING_TO_ENUM(AUDIO_FORMAT_MP2),
93 STRING_TO_ENUM(AUDIO_FORMAT_EVRCNW),
94 STRING_TO_ENUM(AUDIO_FORMAT_PCM_16_BIT_OFFLOAD),
95 STRING_TO_ENUM(AUDIO_FORMAT_PCM_24_BIT_OFFLOAD),
96#endif
97};
98
99static uint32_t string_to_enum(const struct string_to_enum *table, size_t size,
100 const char *name)
101{
102 size_t i;
103 for (i = 0; i < size; i++) {
104 if (strcmp(table[i].name, name) == 0) {
105 ALOGV("%s found %s", __func__, table[i].name);
106 return table[i].value;
107 }
108 }
109 return 0;
110}
111
112static audio_output_flags_t parse_flag_names(char *name)
113{
114 uint32_t flag = 0;
115 char *flag_name = strtok(name, "|");
116 while (flag_name != NULL) {
117 if (strlen(flag_name) != 0) {
118 flag |= string_to_enum(s_flag_name_to_enum_table,
119 ARRAY_SIZE(s_flag_name_to_enum_table),
120 flag_name);
121 }
122 flag_name = strtok(NULL, "|");
123 }
124
125 ALOGV("parse_flag_names: flag - %d", flag);
126 return (audio_output_flags_t)flag;
127}
128
129static void parse_format_names(char *name, struct streams_output_cfg *so_info)
130{
131 struct stream_format *sf_info = NULL;
132 char *str = strtok(name, "|");
133
134 if (str != NULL && strcmp(str, DYNAMIC_VALUE_TAG) == 0)
135 return;
136
137 list_init(&so_info->format_list);
138 while (str != NULL) {
139 audio_format_t format = (audio_format_t)string_to_enum(s_format_name_to_enum_table,
140 ARRAY_SIZE(s_format_name_to_enum_table), str);
141 ALOGV("%s: format - %d", __func__, format);
142 if (format != 0) {
143 sf_info = (struct stream_format *)calloc(1, sizeof(struct stream_format));
144 sf_info->format = format;
145 list_add_tail(&so_info->format_list, &sf_info->list);
146 }
147 str = strtok(NULL, "|");
148 }
149}
150
151static int parse_sample_rate_names(char *name)
152{
153 int sample_rate = 48000;
154 char *str = strtok(name, "|");
155
156 if (str != NULL && strcmp(str, DYNAMIC_VALUE_TAG))
157 sample_rate = (int)strtol(str, (char **)NULL, 10);
158
159 ALOGV("%s: sample_rate - %d", __func__, sample_rate);
160 return sample_rate;
161}
162
163static int parse_bit_width_names(char *name)
164{
165 int bit_width = 16;
166 char *str = strtok(name, "|");
167
168 if (str != NULL && strcmp(str, DYNAMIC_VALUE_TAG))
169 bit_width = (int)strtol(str, (char **)NULL, 10);
170
171 ALOGV("%s: bit_width - %d", __func__, bit_width);
172 return bit_width;
173}
174
175static int parse_app_type_names(void *platform, char *name)
176{
177 int app_type = 0; /* TODO: default app type from acdb when exposed using "platform" */
178 char *str = strtok(name, "|");
179
180 if (str != NULL && strcmp(str, DYNAMIC_VALUE_TAG))
181 app_type = (int)strtol(str, (char **)NULL, 10);
182
183 ALOGV("%s: app_type - %d", __func__, app_type);
184 return app_type;
185}
186
187static void update_streams_output_cfg_list(cnode *root, void *platform,
188 struct listnode *streams_output_cfg_list)
189{
190 cnode *node = root->first_child;
191 struct streams_output_cfg *so_info;
192
193 ALOGV("%s", __func__);
194 so_info = (struct streams_output_cfg *)calloc(1, sizeof(struct streams_output_cfg));
195 while (node) {
196 if (strcmp(node->name, FLAGS_TAG) == 0) {
197 so_info->flags = parse_flag_names((char *)node->value);
198 } else if (strcmp(node->name, FORMATS_TAG) == 0) {
199 parse_format_names((char *)node->value, so_info);
200 } else if (strcmp(node->name, SAMPLING_RATES_TAG) == 0) {
201 so_info->app_type_cfg.sample_rate = parse_sample_rate_names((char *)node->value);
202 } else if (strcmp(node->name, BIT_WIDTH_TAG) == 0) {
203 so_info->app_type_cfg.bit_width = parse_bit_width_names((char *)node->value);
204 } else if (strcmp(node->name, APP_TYPE_TAG) == 0) {
205 so_info->app_type_cfg.app_type = parse_app_type_names(platform, (char *)node->value);
206 }
207 node = node->next;
208 }
209 list_add_tail(streams_output_cfg_list, &so_info->list);
210}
211
212static void load_output(cnode *root, void *platform,
213 struct listnode *streams_output_cfg_list)
214{
215 cnode *node = config_find(root, OUTPUTS_TAG);
216 if (node == NULL) {
217 ALOGE("%s: could not load output, node is NULL", __func__);
218 return;
219 }
220
221 node = node->first_child;
222 while (node) {
223 ALOGV("%s: loading output %s", __func__, node->name);
224 update_streams_output_cfg_list(node, platform, streams_output_cfg_list);
225 node = node->next;
226 }
227}
228
229static void send_app_type_cfg(void *platform, struct mixer *mixer,
230 struct listnode *streams_output_cfg_list)
231{
232 int app_type_cfg[MAX_LENGTH_MIXER_CONTROL_IN_INT] = {-1};
233 int length = 0, i, num_app_types = 0;
234 struct listnode *node;
235 bool update;
236 struct mixer_ctl *ctl = NULL;
237 const char *mixer_ctl_name = "App Type Config";
238 struct streams_output_cfg *so_info;
239
240 if (!mixer) {
241 ALOGE("%s: mixer is null",__func__);
242 return;
243 }
244 ctl = mixer_get_ctl_by_name(mixer, mixer_ctl_name);
245 if (!ctl) {
246 ALOGE("%s: Could not get ctl for mixer cmd - %s",__func__, mixer_ctl_name);
247 return;
248 }
249 if (streams_output_cfg_list == NULL) {
250 app_type_cfg[length++] = 1;
251 app_type_cfg[length++] = 0; /* TODO: default app type from acdb when exposed from "platform" */
252 app_type_cfg[length++] = 48000;
253 app_type_cfg[length++] = 16;
254 mixer_ctl_set_array(ctl, app_type_cfg, length);
255 return;
256 }
257
258 app_type_cfg[length++] = num_app_types;
259 list_for_each(node, streams_output_cfg_list) {
260 so_info = node_to_item(node, struct streams_output_cfg, list);
261 update = true;
262 for (i=0; i<length; i=i+3) {
263 if (app_type_cfg[i+1] == -1)
264 break;
265 else if (app_type_cfg[i+1] == so_info->app_type_cfg.app_type) {
266 update = false;
267 break;
268 }
269 }
270 if (update && ((length + 3) <= MAX_LENGTH_MIXER_CONTROL_IN_INT)) {
271 num_app_types += 1 ;
272 app_type_cfg[length++] = so_info->app_type_cfg.app_type;
273 app_type_cfg[length++] = so_info->app_type_cfg.sample_rate;
274 app_type_cfg[length++] = so_info->app_type_cfg.bit_width;
275 }
276 }
277 ALOGV("%s: num_app_types: %d", __func__, num_app_types);
278 if (num_app_types) {
279 app_type_cfg[0] = num_app_types;
280 mixer_ctl_set_array(ctl, app_type_cfg, length);
281 }
282}
283
284void audio_extn_utils_update_streams_output_cfg_list(void *platform,
285 struct mixer *mixer,
286 struct listnode *streams_output_cfg_list)
287{
288 cnode *root;
289 char *data;
290
291 ALOGV("%s", __func__);
292 list_init(streams_output_cfg_list);
293 data = (char *)load_file(AUDIO_OUTPUT_POLICY_VENDOR_CONFIG_FILE, NULL);
294 if (data == NULL) {
295 send_app_type_cfg(platform, mixer, NULL);
296 ALOGE("%s: could not load output policy config file", __func__);
297 return;
298 }
299
300 root = config_node("", "");
301 config_load(root, data);
302 load_output(root, platform, streams_output_cfg_list);
303
304 send_app_type_cfg(platform, mixer, streams_output_cfg_list);
305}
306
307void audio_extn_utils_dump_streams_output_cfg_list(
308 struct listnode *streams_output_cfg_list)
309{
310 int i=0;
311 struct listnode *node_i, *node_j;
312 struct streams_output_cfg *so_info;
313 struct stream_format *sf_info;
314 ALOGV("%s", __func__);
315 list_for_each(node_i, streams_output_cfg_list) {
316 so_info = node_to_item(node_i, struct streams_output_cfg, list);
317 ALOGV("%d: flags-%d, output_sample_rate-%d, output_bit_width-%d, app_type-%d",
318 i++, so_info->flags, so_info->app_type_cfg.sample_rate,
319 so_info->app_type_cfg.bit_width, so_info->app_type_cfg.app_type);
320 list_for_each(node_j, &so_info->format_list) {
321 sf_info = node_to_item(node_j, struct stream_format, list);
322 ALOGV("format-%x", sf_info->format);
323 }
324 }
325}
326
327void audio_extn_utils_release_streams_output_cfg_list(
328 struct listnode *streams_output_cfg_list)
329{
330 struct listnode *node_i, *node_j;
331 struct streams_output_cfg *so_info;
332 struct stream_format *sf_info;
333
334 ALOGV("%s", __func__);
335 while (!list_empty(streams_output_cfg_list)) {
336 node_i = list_head(streams_output_cfg_list);
337 so_info = node_to_item(node_i, struct streams_output_cfg, list);
338 while (!list_empty(&so_info->format_list)) {
339 node_j = list_head(&so_info->format_list);
340 list_remove(node_j);
341 free(node_to_item(node_j, struct stream_format, list));
342 }
343 list_remove(node_i);
344 free(node_to_item(node_i, struct streams_output_cfg, list));
345 }
346}
347
348void audio_extn_utils_update_stream_app_type_cfg(void *platform,
349 struct listnode *streams_output_cfg_list,
350 audio_output_flags_t flags,
351 audio_format_t format,
352 struct stream_app_type_cfg *app_type_cfg)
353{
354 struct listnode *node_i, *node_j;
355 struct streams_output_cfg *so_info;
356 struct stream_format *sf_info;
357
358 ALOGV("%s: flags: %x, format: %x", __func__, flags, format);
359 list_for_each(node_i, streams_output_cfg_list) {
360 so_info = node_to_item(node_i, struct streams_output_cfg, list);
361 if (so_info->flags == flags) {
362 list_for_each(node_j, &so_info->format_list) {
363 sf_info = node_to_item(node_j, struct stream_format, list);
364 if (sf_info->format == format) {
365 ALOGV("App type: %d", so_info->app_type_cfg.app_type);
366 app_type_cfg->app_type = so_info->app_type_cfg.app_type;
367 app_type_cfg->sample_rate = so_info->app_type_cfg.sample_rate;
368 app_type_cfg->bit_width = so_info->app_type_cfg.bit_width;
369 return;
370 }
371 }
372 }
373 }
374 list_for_each(node_i, streams_output_cfg_list) {
375 so_info = node_to_item(node_i, struct streams_output_cfg, list);
376 if (so_info->flags == AUDIO_OUTPUT_FLAG_PRIMARY) {
377 ALOGV("Compatible output profile not found.");
378 ALOGV("App type default to primary output: %d", so_info->app_type_cfg.app_type);
379 app_type_cfg->app_type = so_info->app_type_cfg.app_type;
380 app_type_cfg->sample_rate = so_info->app_type_cfg.sample_rate;
381 app_type_cfg->bit_width = so_info->app_type_cfg.bit_width;
382 return;
383 }
384 }
385 ALOGW("%s: App type could not be selected. Falling back to default", __func__);
386 app_type_cfg->app_type = 0; /* TODO: default app type from acdb when exposed from "platform" */
387 app_type_cfg->sample_rate = 48000;
388 app_type_cfg->bit_width = 16;
389}
390
391int audio_extn_utils_send_app_type_cfg(struct audio_usecase *usecase)
392{
393 char mixer_ctl_name[MAX_LENGTH_MIXER_CONTROL_IN_INT];
394 int app_type_cfg[MAX_LENGTH_MIXER_CONTROL_IN_INT], len = 0, rc;
395 struct stream_out *out = usecase->stream.out;
396 struct audio_device *adev = out->dev;
397 struct mixer_ctl *ctl;
398 int pcm_device_id, acdb_dev_id, snd_device = usecase->out_snd_device;
399
400 ALOGV("%s", __func__);
401
402 if (usecase->type != PCM_PLAYBACK) {
403 ALOGV("%s: not a playback path, no need to cfg app type", __func__);
404 rc = 0;
405 goto exit_send_app_type_cfg;
406 }
407 if ((usecase->id != USECASE_AUDIO_PLAYBACK_DEEP_BUFFER) &&
408 (usecase->id != USECASE_AUDIO_PLAYBACK_LOW_LATENCY) &&
409 (usecase->id != USECASE_AUDIO_PLAYBACK_MULTI_CH) &&
410 (usecase->id != USECASE_AUDIO_PLAYBACK_OFFLOAD)) {
411 ALOGV("%s: a playback path where app type cfg is not required", __func__);
412 rc = 0;
413 goto exit_send_app_type_cfg;
414 }
415
416 snd_device = usecase->out_snd_device;
417
418 pcm_device_id = platform_get_pcm_device_id(out->usecase, PCM_PLAYBACK);
419
420 snprintf(mixer_ctl_name, sizeof(mixer_ctl_name),
421 "Audio Stream %d App Type Cfg", pcm_device_id);
422
423 ctl = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name);
424 if (!ctl) {
425 ALOGE("%s: Could not get ctl for mixer cmd - %s", __func__,
426 mixer_ctl_name);
427 rc = -EINVAL;
428 goto exit_send_app_type_cfg;
429 }
430 snd_device = (snd_device == SND_DEVICE_OUT_SPEAKER) ?
431 audio_extn_get_spkr_prot_snd_device(snd_device) : snd_device;
432 acdb_dev_id = platform_get_snd_device_acdb_id(snd_device);
433 if (acdb_dev_id < 0) {
434 ALOGE("%s: Couldn't get the acdb dev id", __func__);
435 rc = -EINVAL;
436 goto exit_send_app_type_cfg;
437 }
438 app_type_cfg[len++] = out->app_type_cfg.app_type;
439 app_type_cfg[len++] = acdb_dev_id;
440
441 mixer_ctl_set_array(ctl, app_type_cfg, len);
442
443 rc = 0;
444exit_send_app_type_cfg:
445 return rc;
446}